Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's new: model building conventions for applications #4000

Merged
merged 2 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
641 changes: 559 additions & 82 deletions entity-framework/core/what-is-new/ef-core-7.0/whatsnew.md

Large diffs are not rendered by default.

253 changes: 33 additions & 220 deletions samples/core/Miscellaneous/NewInEFCore7/BlogsContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ public FeaturedPost(string title, string content, DateTime publishedOn, string p

public class Tag
{
public Tag(string text)
public Tag(string id, string text)
{
Id = id;
Text = text;
}

public int Id { get; private set; }
public string Id { get; private set; }
public string Text { get; set; }
public List<Post> Posts { get; } = new();
}
Expand Down Expand Up @@ -167,14 +168,14 @@ public Commit(DateTime committedOn, string comment)

public abstract class BlogsContext : DbContext
{
protected BlogsContext(bool useSqlite)
protected BlogsContext(bool useSqlite = false)
{
UseSqlite = useSqlite;
}

public bool UseSqlite { get; }
public bool LoggingEnabled { get; set; }
public abstract MappingStrategy MappingStrategy { get; }
public virtual MappingStrategy MappingStrategy => MappingStrategy.Tph;

public DbSet<Blog> Blogs => Set<Blog>();
public DbSet<Tag> Tags => Set<Tag>();
Expand All @@ -201,21 +202,36 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<FeaturedPost>();
}

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<List<string>>().HaveConversion<StringListConverter>();

base.ConfigureConventions(configurationBuilder);
}

private class StringListConverter : ValueConverter<List<string>, string>
{
public StringListConverter()
: base(v => string.Join(", ", v!), v => v.Split(',', StringSplitOptions.TrimEntries).ToList())
{
}
}

public async Task Seed()
{
var tagEntityFramework = new Tag("Entity Framework");
var tagDotNet = new Tag(".NET");
var tagDotNetMaui = new Tag(".NET MAUI");
var tagAspDotNet = new Tag("ASP.NET");
var tagAspDotNetCore = new Tag("ASP.NET Core");
var tagDotNetCore = new Tag(".NET Core");
var tagHacking = new Tag("Hacking");
var tagLinux = new Tag("Linux");
var tagSqlite = new Tag("SQLite");
var tagVisualStudio = new Tag("Visual Studio");
var tagGraphQl = new Tag("GraphQL");
var tagCosmosDb = new Tag("CosmosDB");
var tagBlazor = new Tag("Blazor");
var tagEntityFramework = new Tag("TagEF", "Entity Framework");
var tagDotNet = new Tag("TagNet", ".NET");
var tagDotNetMaui = new Tag("TagMaui", ".NET MAUI");
var tagAspDotNet = new Tag("TagAsp", "ASP.NET");
var tagAspDotNetCore = new Tag("TagAspC", "ASP.NET Core");
var tagDotNetCore = new Tag("TagC", ".NET Core");
var tagHacking = new Tag("TagHx", "Hacking");
var tagLinux = new Tag("TagLin", "Linux");
var tagSqlite = new Tag("TagLite", "SQLite");
var tagVisualStudio = new Tag("TagVS", "Visual Studio");
var tagGraphQl = new Tag("TagQL", "GraphQL");
var tagCosmosDb = new Tag("TagCos", "CosmosDB");
var tagBlazor = new Tag("TagBl", "Blazor");

var maddy = new Author("Maddy Montaquila")
{
Expand Down Expand Up @@ -410,206 +426,3 @@ public enum MappingStrategy
Tpt,
Tpc,
}

public class TphBlogsContext : BlogsContext
{
public TphBlogsContext()
: base(useSqlite: false)
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}

public override MappingStrategy MappingStrategy => MappingStrategy.Tph;
}

public class TphSqliteBlogsContext : BlogsContext
{
public TphSqliteBlogsContext()
: base(useSqlite: true)
{
}

public override MappingStrategy MappingStrategy => MappingStrategy.Tph;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}

public class TptBlogsContext : BlogsContext
{
public TptBlogsContext()
: base(useSqlite: false)
{
}

public override MappingStrategy MappingStrategy => MappingStrategy.Tpt;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FeaturedPost>().ToTable("FeaturedPosts");
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}

public class TpcBlogsContext : BlogsContext
{
public TpcBlogsContext()
: base(useSqlite: false)
{
}

public override MappingStrategy MappingStrategy => MappingStrategy.Tpc;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().UseTpcMappingStrategy();
modelBuilder.Entity<FeaturedPost>().ToTable("FeaturedPosts");
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}

public abstract class JsonBlogsContextBase : BlogsContext
{
protected JsonBlogsContextBase(bool useSqlite)
: base(useSqlite)
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToJson();
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
});

#region PostMetadataConfig
modelBuilder.Entity<Post>().OwnsOne(
post => post.Metadata, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToJson();
ownedNavigationBuilder.OwnsMany(metadata => metadata.TopSearches);
ownedNavigationBuilder.OwnsMany(metadata => metadata.TopGeographies);
ownedNavigationBuilder.OwnsMany(
metadata => metadata.Updates,
ownedOwnedNavigationBuilder => ownedOwnedNavigationBuilder.OwnsMany(update => update.Commits));
});
#endregion

base.OnModelCreating(modelBuilder);
}

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Issue https://github.com/dotnet/efcore/issues/28688 (Json: add support for collection of primitive types)
configurationBuilder.Properties<List<string>>().HaveConversion<StringListConverter>();

base.ConfigureConventions(configurationBuilder);
}

private class StringListConverter : ValueConverter<List<string>, string>
{
public StringListConverter()
: base(v => string.Join(", ", v!), v => v.Split(',', StringSplitOptions.TrimEntries).ToList())
{
}
}

public override MappingStrategy MappingStrategy => MappingStrategy.Tph;
}

public class JsonBlogsContext : JsonBlogsContextBase
{
public JsonBlogsContext()
: base(useSqlite: false)
{
}
}

public class JsonBlogsContextSqlite : JsonBlogsContextBase
{
public JsonBlogsContextSqlite()
: base(useSqlite: true)
{
}
}

// Used only for code snippets:

public abstract class TableSharingAggregateContext : TphBlogsContext
{
#region TableSharingAggregate
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
});
}
#endregion
}

public abstract class TableMappedAggregateContext : TphBlogsContext
{
#region TableMappedAggregate
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToTable("Contacts");
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address, ownedOwnedNavigationBuilder =>
{
ownedOwnedNavigationBuilder.ToTable("Addresses");
});
});
}
#endregion
}

public abstract class JsonColumnAggregateContext : TphBlogsContext
{
#region JsonColumnAggregate
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToJson();
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
});
}
#endregion
}
68 changes: 68 additions & 0 deletions samples/core/Miscellaneous/NewInEFCore7/ExecuteDeleteSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,71 @@ await context.Posts.Where(p => p.Author!.Posts.Count <= 1)
Console.WriteLine();
}
}

public class TphBlogsContext : BlogsContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}

public class TphSqliteBlogsContext : BlogsContext
{
public TphSqliteBlogsContext()
: base(useSqlite: true)
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}

public class TptBlogsContext : BlogsContext
{
public override MappingStrategy MappingStrategy => MappingStrategy.Tpt;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FeaturedPost>().ToTable("FeaturedPosts");
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}

public class TpcBlogsContext : BlogsContext
{
public override MappingStrategy MappingStrategy => MappingStrategy.Tpc;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().UseTpcMappingStrategy();
modelBuilder.Entity<FeaturedPost>().ToTable("FeaturedPosts");
modelBuilder.Entity<Post>().Ignore(e => e.Metadata);

// https://github.com/dotnet/efcore/issues/28671
// modelBuilder.Entity<Author>().OwnsOne(e => e.Contact).OwnsOne(e => e.Address);
modelBuilder.Entity<Author>().Ignore(e => e.Contact);

base.OnModelCreating(modelBuilder);
}
}
Loading