Skip to content

Commit

Permalink
Updates to What's New (#4672)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajcvickers committed Mar 9, 2024
1 parent 74574db commit 7d09410
Show file tree
Hide file tree
Showing 11 changed files with 586 additions and 114 deletions.
2 changes: 2 additions & 0 deletions entity-framework/core/what-is-new/ef-core-8.0/whatsnew.md
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,8 @@ WHERE "Id" = @p1
RETURNING 1;
```
<a name="hierarchyid"></a>
## HierarchyId in .NET and EF Core
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:
Expand Down
266 changes: 244 additions & 22 deletions entity-framework/core/what-is-new/ef-core-9.0/whatsnew.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion samples/core/Miscellaneous/NewInEFCore9/BlogsContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> (UseSqlite
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
: optionsBuilder.UseSqlServer(
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}",
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseNetTopologySuite()))
.EnableSensitiveDataLogging()
.LogTo(
Expand Down
120 changes: 120 additions & 0 deletions samples/core/Miscellaneous/NewInEFCore9/CustomConventionsSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;

namespace NewInEfCore9;

public static class CustomConventionsSample
{
public static async Task Conventions_enhancements_in_EF9()
{
PrintSampleName();

await using var context = new TestContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
await context.Seed();

context.LoggingEnabled = true;
context.ChangeTracker.Clear();

Console.WriteLine(context.Model.ToDebugString());
}

private static void PrintSampleName([CallerMemberName] string? methodName = null)
{
Console.WriteLine($">>>> Sample: {methodName}");
Console.WriteLine();
}

public class TestContext : DbContext
{
public bool LoggingEnabled { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0")
.EnableSensitiveDataLogging()
.LogTo(
s =>
{
if (LoggingEnabled)
{
Console.WriteLine(s);
}
}, LogLevel.Information);

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}

public async Task Seed()
{
await SaveChangesAsync();
}
}

#region AttributeBasedPropertyDiscoveryConvention
public class AttributeBasedPropertyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependencies)
: PropertyDiscoveryConvention(dependencies)
{
protected override bool IsCandidatePrimitiveProperty(
MemberInfo memberInfo, IConventionTypeBase structuralType, out CoreTypeMapping? mapping)
{
if (base.IsCandidatePrimitiveProperty(memberInfo, structuralType, out mapping))
{
if (Attribute.IsDefined(memberInfo, typeof(PersistAttribute), inherit: true))
{
return true;
}

structuralType.Builder.Ignore(memberInfo.Name);
}

mapping = null;
return false;
}
}
#endregion

#region Country
public class Country
{
[Persist]
public int Code { get; set; }

[Persist]
public required string Name { get; set; }

public bool IsDirty { get; set; } // Will not be mapped by default.

private class FooConfiguration : IEntityTypeConfiguration<Country>
{
private FooConfiguration()
{
}

public void Configure(EntityTypeBuilder<Country> builder)
{
builder.HasKey(e => e.Code);
}
}
}
#endregion
}

#region PersistAttribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class PersistAttribute : Attribute
{
}
#endregion
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> (UseSqlite
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
: optionsBuilder.UseSqlServer(
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}",
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseNetTopologySuite()))
.EnableSensitiveDataLogging()
.LogTo(Console.WriteLine, LogLevel.Information);
Expand Down
121 changes: 121 additions & 0 deletions samples/core/Miscellaneous/NewInEFCore9/HierarchyIdSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
public static class HierarchyIdSample
{
public static async Task SQL_Server_HierarchyId()
{
PrintSampleName();

await using var context = new FamilyTreeContext();
await context.Database.EnsureDeletedAsync();

context.LoggingEnabled = true;

await context.Database.EnsureCreatedAsync();
await context.Seed();

context.ChangeTracker.Clear();

#region HierarchyIdQuery
var daisy = await context.Halflings.SingleAsync(e => e.Name == "Daisy");
#endregion

#region HierarchyIdParse1
var child1 = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 1), "Toast");
var child2 = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 2), "Wills");
#endregion

#region HierarchyIdParse2
var child1b = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 1, 5), "Toast");
#endregion

context.AddRange(child1, child2, child1b);

await context.SaveChangesAsync();
}

private static void PrintSampleName([CallerMemberName] string? methodName = null)
{
Console.WriteLine($">>>> Sample: {methodName}");
Console.WriteLine();
}

#region Halfling
public class Halfling
{
public Halfling(HierarchyId pathFromPatriarch, string name, int? yearOfBirth = null)
{
PathFromPatriarch = pathFromPatriarch;
Name = name;
YearOfBirth = yearOfBirth;
}

public int Id { get; private set; }
public HierarchyId PathFromPatriarch { get; set; }
public string Name { get; set; }
public int? YearOfBirth { get; set; }
}
#endregion

public class FamilyTreeContext : DbContext
{
public bool LoggingEnabled { get; set; }

public DbSet<Halfling> Halflings => Set<Halfling>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseHierarchyId())
.EnableSensitiveDataLogging()
.LogTo(
s =>
{
if (LoggingEnabled)
{
Console.WriteLine(s);
}
}, LogLevel.Information);

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}

public async Task Seed()
{
#region AddRangeAsync
await AddRangeAsync(
new Halfling(HierarchyId.Parse("/"), "Balbo", 1167),
new Halfling(HierarchyId.Parse("/1/"), "Mungo", 1207),
new Halfling(HierarchyId.Parse("/2/"), "Pansy", 1212),
new Halfling(HierarchyId.Parse("/3/"), "Ponto", 1216),
new Halfling(HierarchyId.Parse("/4/"), "Largo", 1220),
new Halfling(HierarchyId.Parse("/5/"), "Lily", 1222),
new Halfling(HierarchyId.Parse("/1/1/"), "Bungo", 1246),
new Halfling(HierarchyId.Parse("/1/2/"), "Belba", 1256),
new Halfling(HierarchyId.Parse("/1/3/"), "Longo", 1260),
new Halfling(HierarchyId.Parse("/1/4/"), "Linda", 1262),
new Halfling(HierarchyId.Parse("/1/5/"), "Bingo", 1264),
new Halfling(HierarchyId.Parse("/3/1/"), "Rosa", 1256),
new Halfling(HierarchyId.Parse("/3/2/"), "Polo"),
new Halfling(HierarchyId.Parse("/4/1/"), "Fosco", 1264),
new Halfling(HierarchyId.Parse("/1/1/1/"), "Bilbo", 1290),
new Halfling(HierarchyId.Parse("/1/3/1/"), "Otho", 1310),
new Halfling(HierarchyId.Parse("/1/5/1/"), "Falco", 1303),
new Halfling(HierarchyId.Parse("/3/2/1/"), "Posco", 1302),
new Halfling(HierarchyId.Parse("/3/2/2/"), "Prisca", 1306),
new Halfling(HierarchyId.Parse("/4/1/1/"), "Dora", 1302),
new Halfling(HierarchyId.Parse("/4/1/2/"), "Drogo", 1308),
new Halfling(HierarchyId.Parse("/4/1/3/"), "Dudo", 1311),
new Halfling(HierarchyId.Parse("/1/3/1/1/"), "Lotho", 1310),
new Halfling(HierarchyId.Parse("/1/5/1/1/"), "Poppy", 1344),
new Halfling(HierarchyId.Parse("/3/2/1/1/"), "Ponto", 1346),
new Halfling(HierarchyId.Parse("/3/2/1/2/"), "Porto", 1348),
new Halfling(HierarchyId.Parse("/3/2/1/3/"), "Peony", 1350),
new Halfling(HierarchyId.Parse("/4/1/2/1/"), "Frodo", 1368),
new Halfling(HierarchyId.Parse("/4/1/3/1/"), "Daisy", 1350),
new Halfling(HierarchyId.Parse("/3/2/1/1/1/"), "Angelica", 1381));

await SaveChangesAsync();
#endregion
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> (UseSqlite
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
// Note that SQL Server 2022 is required.
: optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name}"))
: optionsBuilder.UseSqlServer(@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0"))
.EnableSensitiveDataLogging()
.LogTo(
s =>
Expand Down
Loading

0 comments on commit 7d09410

Please sign in to comment.