Initial commit

This commit is contained in:
Mark J Price 2022-03-06 10:37:59 +00:00
parent dd097904c2
commit 2b1f5c1254
48 changed files with 36395 additions and 0 deletions

View file

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-*" />
</ItemGroup>
<ItemGroup>
<None Update="Northwind.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
namespace Packt.Shared;
public class Customer
{
[StringLength(5)]
public string CustomerId { get; set; } = null!;
[Required]
[StringLength(40)]
public string CompanyName { get; set; } = null!;
[StringLength(30)]
public string? ContactName { get; set; }
[StringLength(30)]
public string? ContactTitle { get; set; }
[StringLength(60)]
public string? Address { get; set; }
[StringLength(15)]
public string? City { get; set; }
[StringLength(15)]
public string? Region { get; set; }
[StringLength(10)]
public string? PostalCode { get; set; }
[StringLength(15)]
public string? Country { get; set; }
[StringLength(24)]
public string? Phone { get; set; }
[StringLength(24)]
public string? Fax { get; set; }
}

View file

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore; // DbContext, DbSet<T>
namespace Packt.Shared;
public class Northwind : DbContext
{
public DbSet<Customer> Customers { get; set; } = null!;
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
string path = Path.Combine(Environment.CurrentDirectory, "Northwind.db");
string connection = $"Filename={path}";
optionsBuilder.UseSqlite(connection);
}
}

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
using Packt.Shared; // Northwind, Customer
using (Northwind db = new())
{
IQueryable<string?> distinctCities =
db.Customers.Select(c => c.City).Distinct();
WriteLine("A list of cities that at least one customer resides in:");
WriteLine($"{string.Join(", ", distinctCities)}");
WriteLine();
Write("Enter the name of a city: ");
string city = ReadLine()!;
IQueryable<Customer> customersInCity = db.Customers.Where(c => c.City == city);
if (customersInCity is null)
{
WriteLine($"No customers found in {city}.");
return;
}
WriteLine($"There are {customersInCity.Count()} customers in {city}:");
foreach (Customer c in customersInCity)
{
WriteLine($" {c.CompanyName}");
}
}

View file

@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32210.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqWithObjects", "LinqWithObjects\LinqWithObjects.csproj", "{0712B93E-C502-4E33-8B92-698596682A76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqWithEFCore", "LinqWithEFCore\LinqWithEFCore.csproj", "{44EB1A3F-C3E2-4072-B336-61D61FE5BC59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinqInParallel", "LinqInParallel\LinqInParallel.csproj", "{5962169C-AC14-4B9D-A079-0D8D293CD65C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch11Ex02LinqQueries", "Ch11Ex02LinqQueries\Ch11Ex02LinqQueries.csproj", "{F972829E-E7F1-4A1B-99EB-E582C5329BBC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0712B93E-C502-4E33-8B92-698596682A76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0712B93E-C502-4E33-8B92-698596682A76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0712B93E-C502-4E33-8B92-698596682A76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0712B93E-C502-4E33-8B92-698596682A76}.Release|Any CPU.Build.0 = Release|Any CPU
{44EB1A3F-C3E2-4072-B336-61D61FE5BC59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44EB1A3F-C3E2-4072-B336-61D61FE5BC59}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44EB1A3F-C3E2-4072-B336-61D61FE5BC59}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44EB1A3F-C3E2-4072-B336-61D61FE5BC59}.Release|Any CPU.Build.0 = Release|Any CPU
{5962169C-AC14-4B9D-A079-0D8D293CD65C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5962169C-AC14-4B9D-A079-0D8D293CD65C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5962169C-AC14-4B9D-A079-0D8D293CD65C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5962169C-AC14-4B9D-A079-0D8D293CD65C}.Release|Any CPU.Build.0 = Release|Any CPU
{F972829E-E7F1-4A1B-99EB-E582C5329BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F972829E-E7F1-4A1B-99EB-E582C5329BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F972829E-E7F1-4A1B-99EB-E582C5329BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F972829E-E7F1-4A1B-99EB-E582C5329BBC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6FFEB3ED-7D40-4114-AB17-601B64A8FCE5}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,36 @@
using System.Diagnostics; // Stopwatch
Write("Press ENTER to start. "); ReadLine();
Stopwatch watch = Stopwatch.StartNew();
int max = 45;
IEnumerable<int> numbers = Enumerable.Range(start: 1, count: max);
WriteLine($"Calculating Fibonacci sequence up to term {max}. Please wait...");
// int[] fibonacciNumbers = numbers
// .Select(number => Fibonacci(number))
// .ToArray();
int[] fibonacciNumbers = numbers.AsParallel()
.Select(number => Fibonacci(number))
.OrderBy(number => number)
.ToArray();
watch.Stop();
WriteLine("{0:#,##0} elapsed milliseconds.",
arg0: watch.ElapsedMilliseconds);
Write("Results:");
foreach (int number in fibonacciNumbers)
{
Write($" {number:N0}");
}
static int Fibonacci(int term) =>
term switch
{
1 => 0,
2 => 1,
_ => Fibonacci(term - 1) + Fibonacci(term - 2)
};

View file

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Packt.Shared;
public class Category
{
public int CategoryId { get; set; }
[Required]
[StringLength(15)]
public string CategoryName { get; set; } = null!;
public string? Description { get; set; }
}

View file

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-*" />
</ItemGroup>
<ItemGroup>
<None Update="Northwind.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="settings.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,78 @@
namespace System.Linq; // extend Microsoft's namespace
public static class MyLinqExtensions
{
// this is a chainable LINQ extension method
public static IEnumerable<T> ProcessSequence<T>(
this IEnumerable<T> sequence)
{
// you could do some processing here
return sequence;
}
public static IQueryable<T> ProcessSequence<T>(
this IQueryable<T> sequence)
{
// you could do some processing here
return sequence;
}
// these are scalar LINQ extension methods
public static int? Median(
this IEnumerable<int?> sequence)
{
var ordered = sequence.OrderBy(item => item);
int middlePosition = ordered.Count() / 2;
return ordered.ElementAt(middlePosition);
}
public static int? Median<T>(
this IEnumerable<T> sequence, Func<T, int?> selector)
{
return sequence.Select(selector).Median();
}
public static decimal? Median(
this IEnumerable<decimal?> sequence)
{
var ordered = sequence.OrderBy(item => item);
int middlePosition = ordered.Count() / 2;
return ordered.ElementAt(middlePosition);
}
public static decimal? Median<T>(
this IEnumerable<T> sequence, Func<T, decimal?> selector)
{
return sequence.Select(selector).Median();
}
public static int? Mode(
this IEnumerable<int?> sequence)
{
var grouped = sequence.GroupBy(item => item);
var orderedGroups = grouped.OrderByDescending(
group => group.Count());
return orderedGroups.FirstOrDefault()?.Key;
}
public static int? Mode<T>(
this IEnumerable<T> sequence, Func<T, int?> selector)
{
return sequence.Select(selector)?.Mode();
}
public static decimal? Mode(
this IEnumerable<decimal?> sequence)
{
var grouped = sequence.GroupBy(item => item);
var orderedGroups = grouped.OrderByDescending(
group => group.Count());
return orderedGroups.FirstOrDefault()?.Key;
}
public static decimal? Mode<T>(
this IEnumerable<T> sequence, Func<T, decimal?> selector)
{
return sequence.Select(selector).Mode();
}
}

View file

@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore; // DbContext, DbSet<T>
namespace Packt.Shared;
public class Northwind : DbContext
{
public DbSet<Category> Categories { get; set; } = null!;
public DbSet<Product> Products { get; set; } = null!;
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
string path = Path.Combine(
Environment.CurrentDirectory, "Northwind.db");
optionsBuilder.UseSqlite($"Filename={path}");
/*
string connection = "Data Source=.;" +
"Initial Catalog=Northwind;" +
"Integrated Security=true;" +
"MultipleActiveResultSets=true;";
optionsBuilder.UseSqlServer(connection);
*/
}
protected override void OnModelCreating(
ModelBuilder modelBuilder)
{
if (Database.ProviderName.Contains("Sqlite"))
{
modelBuilder.Entity<Product>()
.Property(product => product.UnitPrice)
.HasConversion<double>();
}
}
}

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Packt.Shared;
public class Product
{
public int ProductId { get; set; }
[Required]
[StringLength(40)]
public string ProductName { get; set; } = null!;
public int? SupplierId { get; set; }
public int? CategoryId { get; set; }
[StringLength(20)]
public string? QuantityPerUnit { get; set; }
[Column(TypeName = "money")] // required for SQL Server provider
public decimal? UnitPrice { get; set; }
public short? UnitsInStock { get; set; }
public short? UnitsOnOrder { get; set; }
public short? ReorderLevel { get; set; }
public bool Discontinued { get; set; }
}

View file

@ -0,0 +1,205 @@
using Packt.Shared; // Northwind, Category, Product
using Microsoft.EntityFrameworkCore; // DbSet<T>
using System.Xml.Linq; // XElement, XAttribute
partial class Program
{
static void FilterAndSort()
{
SectionTitle("Filter and sort");
using (Northwind db = new())
{
DbSet<Product> allProducts = db.Products;
IQueryable<Product> processedProducts = allProducts.ProcessSequence();
IQueryable<Product> filteredProducts =
processedProducts.Where(product => product.UnitPrice < 10M);
IOrderedQueryable<Product> sortedAndFilteredProducts =
filteredProducts.OrderByDescending(product => product.UnitPrice);
var projectedProducts = sortedAndFilteredProducts
.Select(product => new // anonymous type
{
product.ProductId,
product.ProductName,
product.UnitPrice
});
WriteLine(projectedProducts.ToQueryString());
WriteLine("Products that cost less than $10:");
foreach (var p in projectedProducts)
{
WriteLine("{0}: {1} costs {2:$#,##0.00}",
p.ProductId, p.ProductName, p.UnitPrice);
}
WriteLine();
}
}
static void JoinCategoriesAndProducts()
{
SectionTitle("Join categories and products");
using (Northwind db = new())
{
// join every product to its category to return 77 matches
var queryJoin = db.Categories.Join(
inner: db.Products,
outerKeySelector: category => category.CategoryId,
innerKeySelector: product => product.CategoryId,
resultSelector: (c, p) =>
new { c.CategoryName, p.ProductName, p.ProductId })
.OrderBy(cp => cp.CategoryName);
foreach (var item in queryJoin)
{
WriteLine("{0}: {1} is in {2}.",
arg0: item.ProductId,
arg1: item.ProductName,
arg2: item.CategoryName);
}
}
}
static void GroupJoinCategoriesAndProducts()
{
SectionTitle("Group join categories and products");
using (Northwind db = new())
{
// group all products by their category to return 8 matches
var queryGroup = db.Categories.AsEnumerable().GroupJoin(
inner: db.Products,
outerKeySelector: category => category.CategoryId,
innerKeySelector: product => product.CategoryId,
resultSelector: (c, matchingProducts) => new
{
c.CategoryName,
Products = matchingProducts.OrderBy(p => p.ProductName)
});
foreach (var category in queryGroup)
{
WriteLine("{0} has {1} products.",
arg0: category.CategoryName,
arg1: category.Products.Count());
foreach (var product in category.Products)
{
WriteLine($" {product.ProductName}");
}
}
}
}
static void AggregateProducts()
{
SectionTitle("Aggregate products");
using (Northwind db = new())
{
WriteLine("{0,-25} {1,10}",
arg0: "Product count:",
arg1: db.Products.Count());
WriteLine("{0,-25} {1,10:$#,##0.00}",
arg0: "Highest product price:",
arg1: db.Products.Max(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:N0}",
arg0: "Sum of units in stock:",
arg1: db.Products.Sum(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:N0}",
arg0: "Sum of units on order:",
arg1: db.Products.Sum(p => p.UnitsOnOrder));
WriteLine("{0,-25} {1,10:$#,##0.00}",
arg0: "Average unit price:",
arg1: db.Products.Average(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:$#,##0.00}",
arg0: "Value of units in stock:",
arg1: db.Products
.Sum(p => p.UnitPrice * p.UnitsInStock));
}
}
static void CustomExtensionMethods()
{
SectionTitle("Custom aggregate extension methods");
using (Northwind db = new())
{
WriteLine("{0,-25} {1,10:N0}",
"Mean units in stock:",
db.Products.Average(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:$#,##0.00}",
"Mean unit price:",
db.Products.Average(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:N0}",
"Median units in stock:",
db.Products.Median(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:$#,##0.00}",
"Median unit price:",
db.Products.Median(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:N0}",
"Mode units in stock:",
db.Products.Mode(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:$#,##0.00}",
"Mode unit price:",
db.Products.Mode(p => p.UnitPrice));
}
}
static void OutputProductsAsXml()
{
SectionTitle("Output products as XML");
using (Northwind db = new())
{
Product[] productsArray = db.Products.ToArray();
XElement xml = new("products",
from p in productsArray
select new XElement("product",
new XAttribute("id", p.ProductId),
new XAttribute("price", p.UnitPrice),
new XElement("name", p.ProductName)));
WriteLine(xml.ToString());
}
}
static void ProcessSettings()
{
string path = Path.Combine(
Environment.CurrentDirectory, "settings.xml");
WriteLine($"Settings file path: {path}");
XDocument doc = XDocument.Load(path);
var appSettings = doc.Descendants("appSettings")
.Descendants("add")
.Select(node => new
{
Key = node.Attribute("key")?.Value,
Value = node.Attribute("value")?.Value
}).ToArray();
foreach (var item in appSettings)
{
WriteLine($"{item.Key}: {item.Value}");
}
}
}

View file

@ -0,0 +1,12 @@
partial class Program
{
static void SectionTitle(string title)
{
ConsoleColor previousColor = ForegroundColor;
ForegroundColor = ConsoleColor.DarkYellow;
WriteLine("*");
WriteLine($"* {title}");
WriteLine("*");
ForegroundColor = previousColor;
}
}

View file

@ -0,0 +1,13 @@
// FilterAndSort();
// JoinCategoriesAndProducts();
// GroupJoinCategoriesAndProducts();
// AggregateProducts();
// CustomExtensionMethods();
// OutputProductsAsXml();
ProcessSettings();

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<add key="color" value="red" />
<add key="size" value="large" />
<add key="price" value="23.99" />
</appSettings>

View file

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
<!--<Using Remove="System.Linq" />-->
</ItemGroup>
</Project>

View file

@ -0,0 +1,9 @@
partial class Program
{
static bool NameLongerThanFour(string name)
{
return name.Length > 4;
}
}

View file

@ -0,0 +1,23 @@
partial class Program
{
static void SectionTitle(string title)
{
ConsoleColor previousColor = ForegroundColor;
ForegroundColor = ConsoleColor.DarkYellow;
WriteLine("*");
WriteLine($"* {title}");
WriteLine("*");
ForegroundColor = previousColor;
}
static void Output(IEnumerable<string> cohort, string description = "")
{
if (!string.IsNullOrEmpty(description))
{
WriteLine(description);
}
Write(" ");
WriteLine(string.Join(", ", cohort.ToArray()));
WriteLine();
}
}

View file

@ -0,0 +1,97 @@
// a string array is a sequence that implements IEnumerable<string>
string[] names = new[] { "Michael", "Pam", "Jim", "Dwight",
"Angela", "Kevin", "Toby", "Creed" };
SectionTitle("Deferred execution");
// Question: Which names end with an M?
// (written using a LINQ extension method)
var query1 = names.Where(name => name.EndsWith("m"));
// Question: Which names end with an M?
// (written using LINQ query comprehension syntax)
var query2 = from name in names where name.EndsWith("m") select name;
// Answer returned as an array of strings containing Pam and Jim
string[] result1 = query1.ToArray();
// Answer returned as a list of strings containing Pam and Jim
List<string> result2 = query2.ToList();
// Answer returned as we enumerate over the results
foreach (string name in query1)
{
WriteLine(name); // outputs Pam
names[2] = "Jimmy"; // change Jim to Jimmy
// on the second iteration Jimmy does not end with an M
}
SectionTitle("Writing queries");
// Where requires a Func<T, T> delegate instance...
// var query = names.Where(
// new Func<string, bool>(NameLongerThanFour));
// ...but the compiler can create it for us...
// var query = names.Where(NameLongerThanFour);
// ...or we can use a lambda expression.
IOrderedEnumerable<string> query = names
.Where(name => name.Length > 4)
.OrderBy(name => name.Length)
.ThenBy(name => name);
foreach (string item in query)
{
WriteLine(item);
}
SectionTitle("Filtering by type");
List<Exception> exceptions = new()
{
new ArgumentException(),
new SystemException(),
new IndexOutOfRangeException(),
new InvalidOperationException(),
new NullReferenceException(),
new InvalidCastException(),
new OverflowException(),
new DivideByZeroException(),
new ApplicationException()
};
IEnumerable<ArithmeticException> arithmeticExceptionsQuery =
exceptions.OfType<ArithmeticException>();
foreach (ArithmeticException exception in arithmeticExceptionsQuery)
{
WriteLine(exception);
}
string[] cohort1 = new[]
{ "Rachel", "Gareth", "Jonathan", "George" };
string[] cohort2 = new[]
{ "Jack", "Stephen", "Daniel", "Jack", "Jared" };
string[] cohort3 = new[]
{ "Declan", "Jack", "Jack", "Jasmine", "Conor" };
SectionTitle("The cohorts");
Output(cohort1, "Cohort 1");
Output(cohort2, "Cohort 2");
Output(cohort3, "Cohort 3");
SectionTitle("Set operations");
Output(cohort2.Distinct(), "cohort2.Distinct()");
Output(cohort2.DistinctBy(name => name.Substring(0, 2)),
"cohort2.DistinctBy(name => name.Substring(0, 2)):");
Output(cohort2.Union(cohort3), "cohort2.Union(cohort3)");
Output(cohort2.Concat(cohort3), "cohort2.Concat(cohort3)");
Output(cohort2.Intersect(cohort3), "cohort2.Intersect(cohort3)");
Output(cohort2.Except(cohort3), "cohort2.Except(cohort3)");
Output(cohort1.Zip(cohort2, (c1, c2) => $"{c1} matched with {c2}"),
"cohort1.Zip(cohort2)");

View file

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-*" />
</ItemGroup>
<ItemGroup>
<None Update="Northwind.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
namespace Packt.Shared;
public class Customer
{
[StringLength(5)]
public string CustomerId { get; set; } = null!;
[Required]
[StringLength(40)]
public string CompanyName { get; set; } = null!;
[StringLength(30)]
public string? ContactName { get; set; }
[StringLength(30)]
public string? ContactTitle { get; set; }
[StringLength(60)]
public string? Address { get; set; }
[StringLength(15)]
public string? City { get; set; }
[StringLength(15)]
public string? Region { get; set; }
[StringLength(10)]
public string? PostalCode { get; set; }
[StringLength(15)]
public string? Country { get; set; }
[StringLength(24)]
public string? Phone { get; set; }
[StringLength(24)]
public string? Fax { get; set; }
}

View file

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore; // DbContext, DbSet<T>
namespace Packt.Shared;
public class Northwind : DbContext
{
public DbSet<Customer> Customers { get; set; } = null!;
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
string path = Path.Combine(Environment.CurrentDirectory, "Northwind.db");
string connection = $"Filename={path}";
optionsBuilder.UseSqlite(connection);
}
}

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
using Packt.Shared; // Northwind, Customer
using (Northwind db = new())
{
IQueryable<string?> distinctCities =
db.Customers.Select(c => c.City).Distinct();
WriteLine("A list of cities that at least one customer resides in:");
WriteLine($"{string.Join(", ", distinctCities)}");
WriteLine();
Write("Enter the name of a city: ");
string city = ReadLine()!;
IQueryable<Customer> customersInCity = db.Customers.Where(c => c.City == city);
if (customersInCity is null)
{
WriteLine($"No customers found in {city}.");
return;
}
WriteLine($"There are {customersInCity.Count()} customers in {city}:");
foreach (Customer c in customersInCity)
{
WriteLine($" {c.CompanyName}");
}
}

View file

@ -0,0 +1,16 @@
{
"folders": [
{
"path": "LinqWithObjects"
},
{
"path": "LinqWithEFCore"
},
{
"path": "LinqInParallel"
},
{
"path": "Ch11Ex02LinqQueries"
}
]
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,36 @@
using System.Diagnostics; // Stopwatch
Write("Press ENTER to start. "); ReadLine();
Stopwatch watch = Stopwatch.StartNew();
int max = 45;
IEnumerable<int> numbers = Enumerable.Range(start: 1, count: max);
WriteLine($"Calculating Fibonacci sequence up to term {max}. Please wait...");
// int[] fibonacciNumbers = numbers
// .Select(number => Fibonacci(number))
// .ToArray();
int[] fibonacciNumbers = numbers.AsParallel()
.Select(number => Fibonacci(number))
.OrderBy(number => number)
.ToArray();
watch.Stop();
WriteLine("{0:#,##0} elapsed milliseconds.",
arg0: watch.ElapsedMilliseconds);
Write("Results:");
foreach (int number in fibonacciNumbers)
{
Write($" {number:N0}");
}
static int Fibonacci(int term) =>
term switch
{
1 => 0,
2 => 1,
_ => Fibonacci(term - 1) + Fibonacci(term - 2)
};

View file

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Packt.Shared;
public class Category
{
public int CategoryId { get; set; }
[Required]
[StringLength(15)]
public string CategoryName { get; set; } = null!;
public string? Description { get; set; }
}

View file

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-*" />
</ItemGroup>
<ItemGroup>
<None Update="Northwind.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="settings.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,78 @@
namespace System.Linq; // extend Microsoft's namespace
public static class MyLinqExtensions
{
// this is a chainable LINQ extension method
public static IEnumerable<T> ProcessSequence<T>(
this IEnumerable<T> sequence)
{
// you could do some processing here
return sequence;
}
public static IQueryable<T> ProcessSequence<T>(
this IQueryable<T> sequence)
{
// you could do some processing here
return sequence;
}
// these are scalar LINQ extension methods
public static int? Median(
this IEnumerable<int?> sequence)
{
var ordered = sequence.OrderBy(item => item);
int middlePosition = ordered.Count() / 2;
return ordered.ElementAt(middlePosition);
}
public static int? Median<T>(
this IEnumerable<T> sequence, Func<T, int?> selector)
{
return sequence.Select(selector).Median();
}
public static decimal? Median(
this IEnumerable<decimal?> sequence)
{
var ordered = sequence.OrderBy(item => item);
int middlePosition = ordered.Count() / 2;
return ordered.ElementAt(middlePosition);
}
public static decimal? Median<T>(
this IEnumerable<T> sequence, Func<T, decimal?> selector)
{
return sequence.Select(selector).Median();
}
public static int? Mode(
this IEnumerable<int?> sequence)
{
var grouped = sequence.GroupBy(item => item);
var orderedGroups = grouped.OrderByDescending(
group => group.Count());
return orderedGroups.FirstOrDefault()?.Key;
}
public static int? Mode<T>(
this IEnumerable<T> sequence, Func<T, int?> selector)
{
return sequence.Select(selector)?.Mode();
}
public static decimal? Mode(
this IEnumerable<decimal?> sequence)
{
var grouped = sequence.GroupBy(item => item);
var orderedGroups = grouped.OrderByDescending(
group => group.Count());
return orderedGroups.FirstOrDefault()?.Key;
}
public static decimal? Mode<T>(
this IEnumerable<T> sequence, Func<T, decimal?> selector)
{
return sequence.Select(selector).Mode();
}
}

View file

@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore; // DbContext, DbSet<T>
namespace Packt.Shared;
public class Northwind : DbContext
{
public DbSet<Category> Categories { get; set; } = null!;
public DbSet<Product> Products { get; set; } = null!;
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
string path = Path.Combine(
Environment.CurrentDirectory, "Northwind.db");
optionsBuilder.UseSqlite($"Filename={path}");
/*
string connection = "Data Source=.;" +
"Initial Catalog=Northwind;" +
"Integrated Security=true;" +
"MultipleActiveResultSets=true;";
optionsBuilder.UseSqlServer(connection);
*/
}
protected override void OnModelCreating(
ModelBuilder modelBuilder)
{
if (Database.ProviderName.Contains("Sqlite"))
{
modelBuilder.Entity<Product>()
.Property(product => product.UnitPrice)
.HasConversion<double>();
}
}
}

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Packt.Shared;
public class Product
{
public int ProductId { get; set; }
[Required]
[StringLength(40)]
public string ProductName { get; set; } = null!;
public int? SupplierId { get; set; }
public int? CategoryId { get; set; }
[StringLength(20)]
public string? QuantityPerUnit { get; set; }
[Column(TypeName = "money")] // required for SQL Server provider
public decimal? UnitPrice { get; set; }
public short? UnitsInStock { get; set; }
public short? UnitsOnOrder { get; set; }
public short? ReorderLevel { get; set; }
public bool Discontinued { get; set; }
}

View file

@ -0,0 +1,205 @@
using Packt.Shared; // Northwind, Category, Product
using Microsoft.EntityFrameworkCore; // DbSet<T>
using System.Xml.Linq; // XElement, XAttribute
partial class Program
{
static void FilterAndSort()
{
SectionTitle("Filter and sort");
using (Northwind db = new())
{
DbSet<Product> allProducts = db.Products;
IQueryable<Product> processedProducts = allProducts.ProcessSequence();
IQueryable<Product> filteredProducts =
processedProducts.Where(product => product.UnitPrice < 10M);
IOrderedQueryable<Product> sortedAndFilteredProducts =
filteredProducts.OrderByDescending(product => product.UnitPrice);
var projectedProducts = sortedAndFilteredProducts
.Select(product => new // anonymous type
{
product.ProductId,
product.ProductName,
product.UnitPrice
});
WriteLine(projectedProducts.ToQueryString());
WriteLine("Products that cost less than $10:");
foreach (var p in projectedProducts)
{
WriteLine("{0}: {1} costs {2:$#,##0.00}",
p.ProductId, p.ProductName, p.UnitPrice);
}
WriteLine();
}
}
static void JoinCategoriesAndProducts()
{
SectionTitle("Join categories and products");
using (Northwind db = new())
{
// join every product to its category to return 77 matches
var queryJoin = db.Categories.Join(
inner: db.Products,
outerKeySelector: category => category.CategoryId,
innerKeySelector: product => product.CategoryId,
resultSelector: (c, p) =>
new { c.CategoryName, p.ProductName, p.ProductId })
.OrderBy(cp => cp.CategoryName);
foreach (var item in queryJoin)
{
WriteLine("{0}: {1} is in {2}.",
arg0: item.ProductId,
arg1: item.ProductName,
arg2: item.CategoryName);
}
}
}
static void GroupJoinCategoriesAndProducts()
{
SectionTitle("Group join categories and products");
using (Northwind db = new())
{
// group all products by their category to return 8 matches
var queryGroup = db.Categories.AsEnumerable().GroupJoin(
inner: db.Products,
outerKeySelector: category => category.CategoryId,
innerKeySelector: product => product.CategoryId,
resultSelector: (c, matchingProducts) => new
{
c.CategoryName,
Products = matchingProducts.OrderBy(p => p.ProductName)
});
foreach (var category in queryGroup)
{
WriteLine("{0} has {1} products.",
arg0: category.CategoryName,
arg1: category.Products.Count());
foreach (var product in category.Products)
{
WriteLine($" {product.ProductName}");
}
}
}
}
static void AggregateProducts()
{
SectionTitle("Aggregate products");
using (Northwind db = new())
{
WriteLine("{0,-25} {1,10}",
arg0: "Product count:",
arg1: db.Products.Count());
WriteLine("{0,-25} {1,10:$#,##0.00}",
arg0: "Highest product price:",
arg1: db.Products.Max(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:N0}",
arg0: "Sum of units in stock:",
arg1: db.Products.Sum(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:N0}",
arg0: "Sum of units on order:",
arg1: db.Products.Sum(p => p.UnitsOnOrder));
WriteLine("{0,-25} {1,10:$#,##0.00}",
arg0: "Average unit price:",
arg1: db.Products.Average(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:$#,##0.00}",
arg0: "Value of units in stock:",
arg1: db.Products
.Sum(p => p.UnitPrice * p.UnitsInStock));
}
}
static void CustomExtensionMethods()
{
SectionTitle("Custom aggregate extension methods");
using (Northwind db = new())
{
WriteLine("{0,-25} {1,10:N0}",
"Mean units in stock:",
db.Products.Average(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:$#,##0.00}",
"Mean unit price:",
db.Products.Average(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:N0}",
"Median units in stock:",
db.Products.Median(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:$#,##0.00}",
"Median unit price:",
db.Products.Median(p => p.UnitPrice));
WriteLine("{0,-25} {1,10:N0}",
"Mode units in stock:",
db.Products.Mode(p => p.UnitsInStock));
WriteLine("{0,-25} {1,10:$#,##0.00}",
"Mode unit price:",
db.Products.Mode(p => p.UnitPrice));
}
}
static void OutputProductsAsXml()
{
SectionTitle("Output products as XML");
using (Northwind db = new())
{
Product[] productsArray = db.Products.ToArray();
XElement xml = new("products",
from p in productsArray
select new XElement("product",
new XAttribute("id", p.ProductId),
new XAttribute("price", p.UnitPrice),
new XElement("name", p.ProductName)));
WriteLine(xml.ToString());
}
}
static void ProcessSettings()
{
string path = Path.Combine(
Environment.CurrentDirectory, "settings.xml");
WriteLine($"Settings file path: {path}");
XDocument doc = XDocument.Load(path);
var appSettings = doc.Descendants("appSettings")
.Descendants("add")
.Select(node => new
{
Key = node.Attribute("key")?.Value,
Value = node.Attribute("value")?.Value
}).ToArray();
foreach (var item in appSettings)
{
WriteLine($"{item.Key}: {item.Value}");
}
}
}

View file

@ -0,0 +1,12 @@
partial class Program
{
static void SectionTitle(string title)
{
ConsoleColor previousColor = ForegroundColor;
ForegroundColor = ConsoleColor.DarkYellow;
WriteLine("*");
WriteLine($"* {title}");
WriteLine("*");
ForegroundColor = previousColor;
}
}

View file

@ -0,0 +1,13 @@
// FilterAndSort();
// JoinCategoriesAndProducts();
// GroupJoinCategoriesAndProducts();
// AggregateProducts();
// CustomExtensionMethods();
// OutputProductsAsXml();
ProcessSettings();

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<add key="color" value="red" />
<add key="size" value="large" />
<add key="price" value="23.99" />
</appSettings>

View file

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Console" Static="true" />
<!--<Using Remove="System.Linq" />-->
</ItemGroup>
</Project>

View file

@ -0,0 +1,9 @@
partial class Program
{
static bool NameLongerThanFour(string name)
{
return name.Length > 4;
}
}

View file

@ -0,0 +1,23 @@
partial class Program
{
static void SectionTitle(string title)
{
ConsoleColor previousColor = ForegroundColor;
ForegroundColor = ConsoleColor.DarkYellow;
WriteLine("*");
WriteLine($"* {title}");
WriteLine("*");
ForegroundColor = previousColor;
}
static void Output(IEnumerable<string> cohort, string description = "")
{
if (!string.IsNullOrEmpty(description))
{
WriteLine(description);
}
Write(" ");
WriteLine(string.Join(", ", cohort.ToArray()));
WriteLine();
}
}

View file

@ -0,0 +1,97 @@
// a string array is a sequence that implements IEnumerable<string>
string[] names = new[] { "Michael", "Pam", "Jim", "Dwight",
"Angela", "Kevin", "Toby", "Creed" };
SectionTitle("Deferred execution");
// Question: Which names end with an M?
// (written using a LINQ extension method)
var query1 = names.Where(name => name.EndsWith("m"));
// Question: Which names end with an M?
// (written using LINQ query comprehension syntax)
var query2 = from name in names where name.EndsWith("m") select name;
// Answer returned as an array of strings containing Pam and Jim
string[] result1 = query1.ToArray();
// Answer returned as a list of strings containing Pam and Jim
List<string> result2 = query2.ToList();
// Answer returned as we enumerate over the results
foreach (string name in query1)
{
WriteLine(name); // outputs Pam
names[2] = "Jimmy"; // change Jim to Jimmy
// on the second iteration Jimmy does not end with an M
}
SectionTitle("Writing queries");
// Where requires a Func<T, T> delegate instance...
// var query = names.Where(
// new Func<string, bool>(NameLongerThanFour));
// ...but the compiler can create it for us...
// var query = names.Where(NameLongerThanFour);
// ...or we can use a lambda expression.
IOrderedEnumerable<string> query = names
.Where(name => name.Length > 4)
.OrderBy(name => name.Length)
.ThenBy(name => name);
foreach (string item in query)
{
WriteLine(item);
}
SectionTitle("Filtering by type");
List<Exception> exceptions = new()
{
new ArgumentException(),
new SystemException(),
new IndexOutOfRangeException(),
new InvalidOperationException(),
new NullReferenceException(),
new InvalidCastException(),
new OverflowException(),
new DivideByZeroException(),
new ApplicationException()
};
IEnumerable<ArithmeticException> arithmeticExceptionsQuery =
exceptions.OfType<ArithmeticException>();
foreach (ArithmeticException exception in arithmeticExceptionsQuery)
{
WriteLine(exception);
}
string[] cohort1 = new[]
{ "Rachel", "Gareth", "Jonathan", "George" };
string[] cohort2 = new[]
{ "Jack", "Stephen", "Daniel", "Jack", "Jared" };
string[] cohort3 = new[]
{ "Declan", "Jack", "Jack", "Jasmine", "Conor" };
SectionTitle("The cohorts");
Output(cohort1, "Cohort 1");
Output(cohort2, "Cohort 2");
Output(cohort3, "Cohort 3");
SectionTitle("Set operations");
Output(cohort2.Distinct(), "cohort2.Distinct()");
Output(cohort2.DistinctBy(name => name.Substring(0, 2)),
"cohort2.DistinctBy(name => name.Substring(0, 2)):");
Output(cohort2.Union(cohort3), "cohort2.Union(cohort3)");
Output(cohort2.Concat(cohort3), "cohort2.Concat(cohort3)");
Output(cohort2.Intersect(cohort3), "cohort2.Intersect(cohort3)");
Output(cohort2.Except(cohort3), "cohort2.Except(cohort3)");
Output(cohort1.Zip(cohort2, (c1, c2) => $"{c1} matched with {c2}"),
"cohort1.Zip(cohort2)");