有关实体框架核心7.x每个.NET开发人员应知道的提示和窍门
#database #microsoft #entityframework

首先,一些常见的技巧

急切的加载:要与父母同时加载相关数据,请使用Include方法。

var blogs = context.Blogs
    .Include(blog => blog.Posts)
    .ToList();

影子属性:阴影属性是在实体类中未定义但在EF核心模型中定义的属性。

modelBuilder.Entity<Blog>()
    .Property<DateTime>("LastUpdated");

并发控制:当多个用户更新同一记录时处理冲突很重要。您可以使用[ConcurrencyCheck]数据注释属性。

public class Blog
{
    public int BlogId { get; set; }
    [ConcurrencyCheck]
    public string Url { get; set; }
}

数据库播种。您可以在EF Core中播种到数据库

modelBuilder.Entity<Blog>()
.HasData(new Blog {BlogId = 1, Url = "http://sample.com"});

禁用自动检测更改:为了更好地性能,请考虑在不需要的情况下关闭自动电视机行为。

context.ChangeTracker.AutoDetectChangesEnabled = false;
var products = context.Products.AsNoTracking().ToList();

流媒体而不是缓冲:流传输允许您在数据库中读取数据时处理数据,而不是首先在内存中对其进行缓冲。这可以帮助减少记忆使用并提高性能。

using var context = new MyDbContext();
using var stream = context.Blogs.AsNoTracking().Select(b => b.Url)
.AsStream();
await stream.CopyToAsync(Response.Body);

编译的查询:编译的查询可以通过缓存查询执行计划来帮助提高性能。

private static readonly Func<MyDbContext, int, Blog> _blogById =
    EF.CompileQuery((MyDbContext context, int id) =>
        context.Blogs.FirstOrDefault(b => b.Id == id));

var blog = _blogById(context, 1);

inmemory数据库用于测试的提供商:inmemory数据库提供商允许您创建一个内存数据库以进行测试。它提供了一种轻巧,快速测试您的代码的方法,而无需真正的数据库。这是使用内存提供商的一个示例:

services.AddDbContext<MyDbContext>(options =>
    options.UseInMemoryDatabase("TestDatabase"));

数据库视图:数据库视图可以提供您数据的简化和优化表示。

modelBuilder.Entity<Blog>()
    .ToView("View_Blogs")
    .HasNoKey();

查询过滤器:查询过滤器允许您将全局过滤器应用于查询,这对于实现软删除或多租户方案很有用。这是使用查询过滤器的示例:

modelBuilder.Entity<Blog>()
    .HasQueryFilter(b => !b.IsDeleted);

dbContext池池可以通过在请求中重复使用相同的上下文实例来提高Web应用程序的性能。这减少了创建和处置DBContext实例的开销。要在ASP.NET Core应用程序中启用DBContext池,请在启动类中配置服务时使用AddDbContextPool方法:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContextPool<MyDbContext>(options =>
    {
        options.UseSqlServer(
        Configuration.GetConnectionString("DefaultConnection")
        );
    });
}

assplitquery 方法可以在包括多个导航属性时避免笛卡尔爆炸问题。此方法配置EF核心通过单独的数据库查询加载查询结果中的集合:

var customersWithOrdersAndProducts = context.Customers
    .Include(c => c.Orders)
        .ThenInclude(o => o.Products)
    .AsSplitQuery()
    .ToList();

现在让我们深入研究EF Core 7特定功能

插入一排

var blog = new Blog { Name = "MyBlog" };
ctx.Blogs.Add(blog);
await ctx.SaveChangesAsync();

在EF Core 6.0中,此代码将启动事务,执行命令,然后提交事务。在EF Core 7.0中,该交易被删除,从而在Localhost上的性能提高了25%,并且在远程服务器上提高了45%。

插入多行

for (var i = 0; i < 4; i++)
{
    var blog = new Blog { Name = "Foo" + i };
    ctx.Blogs.Add(blog);
}
await ctx.SaveChangesAsync();

在EF Core 6.0中,这将使用合并语句插入四行,这比四个单独的插入语句要快得多。在EF Core 7.0中,删除了交易,并且还删除了临时表,从而在远程服务器上的性能提高了61%,而Localhost的提高了74%。

使用 hilo 整数密钥的功能:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>().Property(b => b.Id).UseHiLo();
}

一旦启用了Hilo,SaveChanges输出就会有效,并且类似于GUID方案。

SQL Server临时表。 EF Core 7.0现在支持SQL Server时间表。这使您可以在数据库中直接保留数据更改的历史

modelBuilder.Entity<YourEntity>()
    .UseTemporalTable();

编译模型。 EF Core 7.0引入了编译的模型,可以显着提高启动性能。您可以使用它:

var options = new DbContextOptionsBuilder<YourContext>()
    .UseModel(YourCompiledModel.Instance)
    .Options;

table per-type(tpt)映射。 EF Core 7.0引入了对台式(TPT)映射的支持。

modelBuilder.Entity<YourBaseEntity>()
    .ToTable("YourBaseTable");

modelBuilder.Entity<YourDerivedEntity>()
    .ToTable("YourDerivedTable");

sqlite在线模式迁移。 EF Core 7.0引入了对SQLite在线模式迁移的支持。

var options = new DbContextOptionsBuilder<YourContext>()
.UseSqlite("YourConnectionString", b => 
b.MigrationsAssembly("YourAssembly"))
    .Options;

executeUpdate executedElete 方法。 EF Core 7介绍了两种新方法,即ExecuteUpdate和executedElete,它们执行了记录的更改和删除,而无需加载内存中的实体,从而导致性能提高。但是,必须明确指定更改,因为它们未由EF Core自动检测到。

await db.Posts
    .Where(p => p.Id == "3fa85f64-5717-4562-b3fc-2c963f66afa6")
    .ExecuteUpdateAsync(s => s
    .SetProperty(b => b.AuthorName, "John Smith")
    .SetProperty(b => b.Title, b =>  "EF7 is here!")
    .SetProperty(b => b.Text, b =>  "\t")
    .SetProperty(b => 
     b.LastUpdateDate, "2022-30-11 17:29:46.5028235"));
await db.Posts
.Where(p => p.Id == "3fa85f64-5717-4562-b3fc-2c963f66afa6")
.ExecuteDeleteAsync();

JSON列。 EF7对JSON列具有本机支持,该列允许将.NET类型映射到JSON文档。这包括可以在聚合中使用的LINQ查询,并将转换为JSON的适当查询构建体。也可以更新并保存更改为JSON文档。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Author>().OwnsOne(
        author => author.Contact, ownedNavigationBuilder =>
        {
            ownedNavigationBuilder.ToJson();
            ownedNavigationBuilder
            .OwnsOne(contactDetails => contactDetails.Address);
        });
}
var authorsByCity = await db.Authors.Where(author => author
.Contact.Address.City == city).ToListAsync();
var authorExists = await db.Authors
.Where(author => author.Id == id)
.FirstOrDefaultAsync();
authorExists.Contact.Address.Street = "1523 Stellar Dr";
await db.SaveChangesAsync();

新查询选项。 EF7为Groupby等查询带来了新功能。下面的示例显示了如何使用Groupby扩展方法完成分组:

var groupByAuthor = db.Posts.GroupBy(p => p.AuthorName).ToList();

批量更新和删除。 EF Core 7介绍了执行批量更新和删除的能力。这使您可以表达类似于LINQ查询的内容,以将更改直接推向数据库。新的ExecutedElete和ExecuteUpdate方法以与要应用LINQ执行方法相同的方式附加到LINQ查询。

context.People
    .Where(p => p.PersonId == 1)
    .ExecuteDelete();
context.People
    .Where(p => p.LastName == "Lehrman")
    .ExecuteUpdate(s => s.SetProperty(c => c.LastName, c => "Lerman"));

映射存储过程。 EF Core 7介绍了将存储过程映射到实体的能力。这使您可以使用存储过程在调用Savechanges时执行数据库操作。新的插入程序,更新stordorderprocedure和DeleteSorderStorderProcedure方法允许您将存储的过程映射到实体。

modelBuilder.Entity<Person>()
    .InsertUsingStoredProcedure("PeopleInsert",
        spbuilder => spbuilder
            .HasParameter(p => p.PersonId, pb => pb.IsOutput()
            .HasName("id"))
            .HasParameter(p => p.FirstName)
            .HasParameter(p => p.LastName)
    );