Skip to content

Commit 4790afe

Browse files
committed
Add more solution code to Ch10
1 parent ed4a951 commit 4790afe

19 files changed

+9268
-1
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.ComponentModel.DataAnnotations.Schema; // [Column]
2+
3+
namespace Packt.Shared;
4+
5+
public class Category
6+
{
7+
// these properties map to columns in the database
8+
public int CategoryId { get; set; }
9+
10+
public string? CategoryName { get; set; }
11+
12+
[Column(TypeName = "ntext")]
13+
public string? Description { get; set; }
14+
15+
// defines a navigation property for related rows
16+
public virtual ICollection<Product> Products { get; set; }
17+
18+
public Category()
19+
{
20+
// to enable developers to add products to a Category we must
21+
// initialize the navigation property to an empty collection
22+
Products = new HashSet<Product>();
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<Using Include="System.Console" Static="true" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0-*" />
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<None Update="Northwind.db">
20+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
21+
</None>
22+
</ItemGroup>
23+
24+
</Project>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using Microsoft.EntityFrameworkCore; // DbContext, DbContextOptionsBuilder
2+
using Microsoft.EntityFrameworkCore.Diagnostics;
3+
4+
namespace Packt.Shared;
5+
6+
// this manages the connection to the database
7+
public class Northwind : DbContext
8+
{
9+
// these properties map to tables in the database
10+
public DbSet<Category>? Categories { get; set; }
11+
public DbSet<Product>? Products { get; set; }
12+
13+
protected override void OnConfiguring(
14+
DbContextOptionsBuilder optionsBuilder)
15+
{
16+
string path = Path.Combine(
17+
Environment.CurrentDirectory, "Northwind.db");
18+
19+
string connection = $"Filename={path}";
20+
21+
ConsoleColor previousColor = ForegroundColor;
22+
ForegroundColor = ConsoleColor.DarkYellow;
23+
WriteLine($"Connection: {connection}");
24+
ForegroundColor = previousColor;
25+
26+
optionsBuilder.UseSqlite(connection);
27+
}
28+
29+
protected override void OnModelCreating(
30+
ModelBuilder modelBuilder)
31+
{
32+
// example of using Fluent API instead of attributes
33+
// to limit the length of a category name to 15
34+
modelBuilder.Entity<Category>()
35+
.Property(category => category.CategoryName)
36+
.IsRequired() // NOT NULL
37+
.HasMaxLength(15);
38+
39+
if (Database.ProviderName?.Contains("Sqlite") ?? false)
40+
{
41+
// added to "fix" the lack of decimal support in SQLite
42+
modelBuilder.Entity<Product>()
43+
.Property(product => product.Cost)
44+
.HasConversion<double>();
45+
}
46+
}
47+
}
Binary file not shown.

code/Chapter10/Ch10Ex02DataSerialization/Northwind4SQLite.sql

Lines changed: 8722 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.ComponentModel.DataAnnotations; // [Required], [StringLength]
2+
using System.ComponentModel.DataAnnotations.Schema; // [Column]
3+
4+
namespace Packt.Shared;
5+
6+
public class Product
7+
{
8+
public int ProductId { get; set; } // primary key
9+
10+
[Required]
11+
[StringLength(40)]
12+
public string ProductName { get; set; } = null!;
13+
14+
[Column("UnitPrice", TypeName = "money")]
15+
public decimal? Cost { get; set; } // property name != column name
16+
17+
[Column("UnitsInStock")]
18+
public short? Stock { get; set; }
19+
20+
public bool Discontinued { get; set; }
21+
22+
// these two define the foreign key relationship
23+
// to the Categories table
24+
public int CategoryId { get; set; }
25+
public virtual Category Category { get; set; } = null!;
26+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System.Xml; // XmlWriter
2+
using System.Text.Json; // Utf8JsonWriter, JsonWriterOptions
3+
using Packt.Shared; // Category, Product
4+
5+
using static System.IO.Path;
6+
using static System.Environment;
7+
8+
// we want to easily show the difference between outputting
9+
// XML using elements or attributes so we will define a
10+
// delegate to reference the two different methods.
11+
12+
delegate void WriteDataDelegate(string name, string? value);
13+
14+
partial class Program
15+
{
16+
static void GenerateXmlFile(
17+
IQueryable<Category> categories, bool useAttributes = true)
18+
{
19+
string which = useAttributes ? "attibutes" : "elements";
20+
21+
string xmlFile = $"categories-and-products-using-{which}.xml";
22+
23+
using (FileStream xmlStream = File.Create(
24+
Combine(CurrentDirectory, xmlFile)))
25+
{
26+
using (XmlWriter xml = XmlWriter.Create(xmlStream,
27+
new XmlWriterSettings { Indent = true }))
28+
{
29+
30+
WriteDataDelegate writeMethod;
31+
32+
if (useAttributes)
33+
{
34+
writeMethod = xml.WriteAttributeString;
35+
}
36+
else // use elements
37+
{
38+
writeMethod = xml.WriteElementString;
39+
}
40+
41+
xml.WriteStartDocument();
42+
xml.WriteStartElement("categories");
43+
44+
foreach (Category c in categories)
45+
{
46+
xml.WriteStartElement("category");
47+
writeMethod("id", c.CategoryId.ToString());
48+
writeMethod("name", c.CategoryName);
49+
writeMethod("desc", c.Description);
50+
writeMethod("product_count", c.Products.Count.ToString());
51+
xml.WriteStartElement("products");
52+
53+
foreach (Product p in c.Products)
54+
{
55+
xml.WriteStartElement("product");
56+
57+
writeMethod("id", p.ProductId.ToString());
58+
writeMethod("name", p.ProductName);
59+
writeMethod("cost", p.Cost is null ? "0" : p.Cost.Value.ToString());
60+
writeMethod("stock", p.Stock.ToString());
61+
writeMethod("discontinued", p.Discontinued.ToString());
62+
63+
xml.WriteEndElement(); // </product>
64+
}
65+
xml.WriteEndElement(); // </products>
66+
xml.WriteEndElement(); // </category>
67+
}
68+
xml.WriteEndElement(); // </categories>
69+
}
70+
}
71+
72+
WriteLine("{0} contains {1:N0} bytes.",
73+
arg0: xmlFile,
74+
arg1: new FileInfo(xmlFile).Length);
75+
}
76+
77+
static void GenerateCsvFile(IQueryable<Category> categories)
78+
{
79+
string csvFile = "categories-and-products.csv";
80+
81+
using (FileStream csvStream = File.Create(Combine(CurrentDirectory, csvFile)))
82+
{
83+
using (StreamWriter csv = new(csvStream))
84+
{
85+
86+
csv.WriteLine("CategoryId,CategoryName,Description,ProductId,ProductName,Cost,Stock,Discontinued");
87+
88+
foreach (Category c in categories)
89+
{
90+
foreach (Product p in c.Products)
91+
{
92+
csv.Write("{0},\"{1}\",\"{2}\",",
93+
arg0: c.CategoryId,
94+
arg1: c.CategoryName,
95+
arg2: c.Description);
96+
97+
csv.Write("{0},\"{1}\",{2},",
98+
arg0: p.ProductId,
99+
arg1: p.ProductName,
100+
arg2: p.Cost is null ? 0 : p.Cost.Value);
101+
102+
csv.WriteLine("{0},{1}",
103+
arg0: p.Stock,
104+
arg1: p.Discontinued);
105+
}
106+
}
107+
}
108+
}
109+
110+
WriteLine("{0} contains {1:N0} bytes.",
111+
arg0: csvFile,
112+
arg1: new FileInfo(csvFile).Length);
113+
}
114+
115+
static void GenerateJsonFile(IQueryable<Category> categories)
116+
{
117+
string jsonFile = "categories-and-products.json";
118+
119+
using (FileStream jsonStream = File.Create(Combine(CurrentDirectory, jsonFile)))
120+
{
121+
using (Utf8JsonWriter json = new(jsonStream,
122+
new JsonWriterOptions { Indented = true }))
123+
{
124+
json.WriteStartObject();
125+
json.WriteStartArray("categories");
126+
127+
foreach (Category c in categories)
128+
{
129+
json.WriteStartObject();
130+
131+
json.WriteNumber("id", c.CategoryId);
132+
json.WriteString("name", c.CategoryName);
133+
json.WriteString("desc", c.Description);
134+
json.WriteNumber("product_count", c.Products.Count);
135+
136+
json.WriteStartArray("products");
137+
138+
foreach (Product p in c.Products)
139+
{
140+
json.WriteStartObject();
141+
142+
json.WriteNumber("id", p.ProductId);
143+
json.WriteString("name", p.ProductName);
144+
json.WriteNumber("cost", p.Cost is null ? 0 : p.Cost.Value);
145+
json.WriteNumber("stock", p.Stock is null ? 0 : p.Stock.Value);
146+
json.WriteBoolean("discontinued", p.Discontinued);
147+
148+
json.WriteEndObject(); // product
149+
}
150+
json.WriteEndArray(); // products
151+
json.WriteEndObject(); // category
152+
}
153+
json.WriteEndArray(); // categories
154+
json.WriteEndObject();
155+
}
156+
}
157+
158+
WriteLine("{0} contains {1:N0} bytes.",
159+
arg0: jsonFile,
160+
arg1: new FileInfo(jsonFile).Length);
161+
}
162+
}
163+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.EntityFrameworkCore; // Include extension method
2+
using Packt.Shared; // Northwind, Category, Product
3+
4+
WriteLine("Creating four files containing serialized categories and products.");
5+
6+
using (Northwind db = new())
7+
{
8+
// a query to get all categories and their related products
9+
IQueryable<Category>? categories = db.Categories?.Include(c => c.Products);
10+
11+
if (categories is null)
12+
{
13+
WriteLine("No categories found.");
14+
return;
15+
}
16+
17+
GenerateXmlFile(categories);
18+
GenerateXmlFile(categories, useAttributes: false);
19+
GenerateCsvFile(categories);
20+
GenerateJsonFile(categories);
21+
22+
WriteLine($"Current directory: {Environment.CurrentDirectory}");
23+
}

code/Chapter10/Chapter10.sln

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.6.33829.357
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithEFCore", "WorkingWithEFCore\WorkingWithEFCore.csproj", "{F370D9A0-3198-4D1B-AD9B-7F2C379EB169}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkingWithEFCore", "WorkingWithEFCore\WorkingWithEFCore.csproj", "{F370D9A0-3198-4D1B-AD9B-7F2C379EB169}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Northwind.EntityModels", "Northwind.EntityModels\Northwind.EntityModels.csproj", "{C82EA526-086F-4E32-96F8-72ACD61D46D3}"
9+
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ch10Ex02DataSerialization", "Ch10Ex02DataSerialization\Ch10Ex02DataSerialization.csproj", "{F5BA4B70-5138-4592-9AE6-AE1CAF59BF85}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoursesAndStudents", "CoursesAndStudents\CoursesAndStudents.csproj", "{6C953C97-C11D-4C8C-9E3F-E96150AB9795}"
713
EndProject
814
Global
915
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +21,18 @@ Global
1521
{F370D9A0-3198-4D1B-AD9B-7F2C379EB169}.Debug|Any CPU.Build.0 = Debug|Any CPU
1622
{F370D9A0-3198-4D1B-AD9B-7F2C379EB169}.Release|Any CPU.ActiveCfg = Release|Any CPU
1723
{F370D9A0-3198-4D1B-AD9B-7F2C379EB169}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{C82EA526-086F-4E32-96F8-72ACD61D46D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{C82EA526-086F-4E32-96F8-72ACD61D46D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{C82EA526-086F-4E32-96F8-72ACD61D46D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{C82EA526-086F-4E32-96F8-72ACD61D46D3}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{F5BA4B70-5138-4592-9AE6-AE1CAF59BF85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{F5BA4B70-5138-4592-9AE6-AE1CAF59BF85}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{F5BA4B70-5138-4592-9AE6-AE1CAF59BF85}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{F5BA4B70-5138-4592-9AE6-AE1CAF59BF85}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{6C953C97-C11D-4C8C-9E3F-E96150AB9795}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{6C953C97-C11D-4C8C-9E3F-E96150AB9795}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{6C953C97-C11D-4C8C-9E3F-E96150AB9795}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{6C953C97-C11D-4C8C-9E3F-E96150AB9795}.Release|Any CPU.Build.0 = Release|Any CPU
1836
EndGlobalSection
1937
GlobalSection(SolutionProperties) = preSolution
2038
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)