Skip to content

Commit 97da967

Browse files
committed
Updates to What's New
1 parent 74574db commit 97da967

File tree

11 files changed

+586
-114
lines changed

11 files changed

+586
-114
lines changed

entity-framework/core/what-is-new/ef-core-8.0/whatsnew.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,8 @@ WHERE "Id" = @p1
12901290
RETURNING 1;
12911291
```
12921292
1293+
<a name="hierarchyid"></a>
1294+
12931295
## HierarchyId in .NET and EF Core
12941296
12951297
Azure SQL and SQL Server have a special data type called [`hierarchyid`](/sql/t-sql/data-types/hierarchyid-data-type-method-reference) that is used to store [hierarchical data](/sql/relational-databases/hierarchical-data-sql-server). In this case, "hierarchical data" essentially means data that forms a tree structure, where each item can have a parent and/or children. Examples of such data are:

entity-framework/core/what-is-new/ef-core-9.0/whatsnew.md

Lines changed: 244 additions & 22 deletions
Large diffs are not rendered by default.

samples/core/Miscellaneous/NewInEFCore9/BlogsContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
210210
=> (UseSqlite
211211
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
212212
: optionsBuilder.UseSqlServer(
213-
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}",
213+
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
214214
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseNetTopologySuite()))
215215
.EnableSensitiveDataLogging()
216216
.LogTo(
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using Microsoft.EntityFrameworkCore.Metadata;
2+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
3+
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
4+
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
5+
using Microsoft.EntityFrameworkCore.Storage;
6+
7+
namespace NewInEfCore9;
8+
9+
public static class CustomConventionsSample
10+
{
11+
public static async Task Conventions_enhancements_in_EF9()
12+
{
13+
PrintSampleName();
14+
15+
await using var context = new TestContext();
16+
await context.Database.EnsureDeletedAsync();
17+
await context.Database.EnsureCreatedAsync();
18+
await context.Seed();
19+
20+
context.LoggingEnabled = true;
21+
context.ChangeTracker.Clear();
22+
23+
Console.WriteLine(context.Model.ToDebugString());
24+
}
25+
26+
private static void PrintSampleName([CallerMemberName] string? methodName = null)
27+
{
28+
Console.WriteLine($">>>> Sample: {methodName}");
29+
Console.WriteLine();
30+
}
31+
32+
public class TestContext : DbContext
33+
{
34+
public bool LoggingEnabled { get; set; }
35+
36+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
37+
=> optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0")
38+
.EnableSensitiveDataLogging()
39+
.LogTo(
40+
s =>
41+
{
42+
if (LoggingEnabled)
43+
{
44+
Console.WriteLine(s);
45+
}
46+
}, LogLevel.Information);
47+
48+
protected override void OnModelCreating(ModelBuilder modelBuilder)
49+
{
50+
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
51+
}
52+
53+
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
54+
{
55+
configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
56+
serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
57+
serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
58+
}
59+
60+
public async Task Seed()
61+
{
62+
await SaveChangesAsync();
63+
}
64+
}
65+
66+
#region AttributeBasedPropertyDiscoveryConvention
67+
public class AttributeBasedPropertyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependencies)
68+
: PropertyDiscoveryConvention(dependencies)
69+
{
70+
protected override bool IsCandidatePrimitiveProperty(
71+
MemberInfo memberInfo, IConventionTypeBase structuralType, out CoreTypeMapping? mapping)
72+
{
73+
if (base.IsCandidatePrimitiveProperty(memberInfo, structuralType, out mapping))
74+
{
75+
if (Attribute.IsDefined(memberInfo, typeof(PersistAttribute), inherit: true))
76+
{
77+
return true;
78+
}
79+
80+
structuralType.Builder.Ignore(memberInfo.Name);
81+
}
82+
83+
mapping = null;
84+
return false;
85+
}
86+
}
87+
#endregion
88+
89+
#region Country
90+
public class Country
91+
{
92+
[Persist]
93+
public int Code { get; set; }
94+
95+
[Persist]
96+
public required string Name { get; set; }
97+
98+
public bool IsDirty { get; set; } // Will not be mapped by default.
99+
100+
private class FooConfiguration : IEntityTypeConfiguration<Country>
101+
{
102+
private FooConfiguration()
103+
{
104+
}
105+
106+
public void Configure(EntityTypeBuilder<Country> builder)
107+
{
108+
builder.HasKey(e => e.Code);
109+
}
110+
}
111+
}
112+
#endregion
113+
}
114+
115+
#region PersistAttribute
116+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
117+
public sealed class PersistAttribute : Attribute
118+
{
119+
}
120+
#endregion

samples/core/Miscellaneous/NewInEFCore9/ExecuteUpdateSample.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
216216
=> (UseSqlite
217217
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
218218
: optionsBuilder.UseSqlServer(
219-
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}",
219+
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
220220
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseNetTopologySuite()))
221221
.EnableSensitiveDataLogging()
222222
.LogTo(Console.WriteLine, LogLevel.Information);
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
public static class HierarchyIdSample
2+
{
3+
public static async Task SQL_Server_HierarchyId()
4+
{
5+
PrintSampleName();
6+
7+
await using var context = new FamilyTreeContext();
8+
await context.Database.EnsureDeletedAsync();
9+
10+
context.LoggingEnabled = true;
11+
12+
await context.Database.EnsureCreatedAsync();
13+
await context.Seed();
14+
15+
context.ChangeTracker.Clear();
16+
17+
#region HierarchyIdQuery
18+
var daisy = await context.Halflings.SingleAsync(e => e.Name == "Daisy");
19+
#endregion
20+
21+
#region HierarchyIdParse1
22+
var child1 = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 1), "Toast");
23+
var child2 = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 2), "Wills");
24+
#endregion
25+
26+
#region HierarchyIdParse2
27+
var child1b = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 1, 5), "Toast");
28+
#endregion
29+
30+
context.AddRange(child1, child2, child1b);
31+
32+
await context.SaveChangesAsync();
33+
}
34+
35+
private static void PrintSampleName([CallerMemberName] string? methodName = null)
36+
{
37+
Console.WriteLine($">>>> Sample: {methodName}");
38+
Console.WriteLine();
39+
}
40+
41+
#region Halfling
42+
public class Halfling
43+
{
44+
public Halfling(HierarchyId pathFromPatriarch, string name, int? yearOfBirth = null)
45+
{
46+
PathFromPatriarch = pathFromPatriarch;
47+
Name = name;
48+
YearOfBirth = yearOfBirth;
49+
}
50+
51+
public int Id { get; private set; }
52+
public HierarchyId PathFromPatriarch { get; set; }
53+
public string Name { get; set; }
54+
public int? YearOfBirth { get; set; }
55+
}
56+
#endregion
57+
58+
public class FamilyTreeContext : DbContext
59+
{
60+
public bool LoggingEnabled { get; set; }
61+
62+
public DbSet<Halfling> Halflings => Set<Halfling>();
63+
64+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
65+
=> optionsBuilder.UseSqlServer(
66+
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
67+
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseHierarchyId())
68+
.EnableSensitiveDataLogging()
69+
.LogTo(
70+
s =>
71+
{
72+
if (LoggingEnabled)
73+
{
74+
Console.WriteLine(s);
75+
}
76+
}, LogLevel.Information);
77+
78+
protected override void OnModelCreating(ModelBuilder modelBuilder)
79+
{
80+
}
81+
82+
public async Task Seed()
83+
{
84+
#region AddRangeAsync
85+
await AddRangeAsync(
86+
new Halfling(HierarchyId.Parse("/"), "Balbo", 1167),
87+
new Halfling(HierarchyId.Parse("/1/"), "Mungo", 1207),
88+
new Halfling(HierarchyId.Parse("/2/"), "Pansy", 1212),
89+
new Halfling(HierarchyId.Parse("/3/"), "Ponto", 1216),
90+
new Halfling(HierarchyId.Parse("/4/"), "Largo", 1220),
91+
new Halfling(HierarchyId.Parse("/5/"), "Lily", 1222),
92+
new Halfling(HierarchyId.Parse("/1/1/"), "Bungo", 1246),
93+
new Halfling(HierarchyId.Parse("/1/2/"), "Belba", 1256),
94+
new Halfling(HierarchyId.Parse("/1/3/"), "Longo", 1260),
95+
new Halfling(HierarchyId.Parse("/1/4/"), "Linda", 1262),
96+
new Halfling(HierarchyId.Parse("/1/5/"), "Bingo", 1264),
97+
new Halfling(HierarchyId.Parse("/3/1/"), "Rosa", 1256),
98+
new Halfling(HierarchyId.Parse("/3/2/"), "Polo"),
99+
new Halfling(HierarchyId.Parse("/4/1/"), "Fosco", 1264),
100+
new Halfling(HierarchyId.Parse("/1/1/1/"), "Bilbo", 1290),
101+
new Halfling(HierarchyId.Parse("/1/3/1/"), "Otho", 1310),
102+
new Halfling(HierarchyId.Parse("/1/5/1/"), "Falco", 1303),
103+
new Halfling(HierarchyId.Parse("/3/2/1/"), "Posco", 1302),
104+
new Halfling(HierarchyId.Parse("/3/2/2/"), "Prisca", 1306),
105+
new Halfling(HierarchyId.Parse("/4/1/1/"), "Dora", 1302),
106+
new Halfling(HierarchyId.Parse("/4/1/2/"), "Drogo", 1308),
107+
new Halfling(HierarchyId.Parse("/4/1/3/"), "Dudo", 1311),
108+
new Halfling(HierarchyId.Parse("/1/3/1/1/"), "Lotho", 1310),
109+
new Halfling(HierarchyId.Parse("/1/5/1/1/"), "Poppy", 1344),
110+
new Halfling(HierarchyId.Parse("/3/2/1/1/"), "Ponto", 1346),
111+
new Halfling(HierarchyId.Parse("/3/2/1/2/"), "Porto", 1348),
112+
new Halfling(HierarchyId.Parse("/3/2/1/3/"), "Peony", 1350),
113+
new Halfling(HierarchyId.Parse("/4/1/2/1/"), "Frodo", 1368),
114+
new Halfling(HierarchyId.Parse("/4/1/3/1/"), "Daisy", 1350),
115+
new Halfling(HierarchyId.Parse("/3/2/1/1/1/"), "Angelica", 1381));
116+
117+
await SaveChangesAsync();
118+
#endregion
119+
}
120+
}
121+
}

samples/core/Miscellaneous/NewInEFCore9/LeastGreatestSample.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
207207
=> (UseSqlite
208208
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
209209
// Note that SQL Server 2022 is required.
210-
: optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}"))
210+
: optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0"))
211211
.EnableSensitiveDataLogging()
212212
.LogTo(
213213
s =>

0 commit comments

Comments
 (0)