Entity Framework的最佳实践一

  • Entity Framework的最佳实践一已关闭评论
  • 161 次浏览
  • A+
所属分类:.NET技术
Entity Framework (EF) Core 是轻量化、可扩展、开源和跨平台版的常用 Entity Framework 数据访问技术。
EF Core 可用作对象关系映射程序 (O/RM)

 

创建DbContext 对象

DbContext的生存期

DbContext 的生存期从创建实例时开始,并在释放实例时结束。

DbContext生成

  • 通过依赖关系
为每个请求创建一个 ApplicationDbContext 实例,并传递给控制器,以在请求结束后释放前执行工作单元
 public void ConfigureServices(IServiceCollection services) {     services.AddControllers();     services.AddDbContext<ApplicationDbContext>(         options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection")); } public class ApplicationDbContext : DbContext {     public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)         : base(options)     {     } } public class MyController {     private readonly ApplicationDbContext _context;     public MyController(ApplicationDbContext context)     {         _context = context;     } }

  • 通过关键字 “new”
 public class ApplicationDbContext : DbContext {     private readonly string _connectionString;     public ApplicationDbContext(string connectionString)     {         _connectionString = connectionString;     }     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)     {         optionsBuilder.UseSqlServer(_connectionString);     } } //或者 public class ApplicationDbContext : DbContext {     public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)         : base(options)     {     } } var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()     .UseSqlServer(@"Server=(localdb)mssqllocaldb;Database=Test")     .Options;  using var context = new ApplicationDbContext(contextOptions);

 

DBContext的属性

DbContextOptionsBuilder

所有DBContext配置的起始点都是 DbContextOptionsBuilder。 可以通过三种方式获取此生成器:
  • 在 AddDbContext 和相关方法中
  • 在 OnConfiguring 中
  • 使用 new 显式构造

DbContextOptions

允许多个具体子类使用其不同的泛型 DbContextOptions 的实例初始化DBContext
 public sealed class ApplicationDbContext1 : ApplicationDbContextBase {     public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)         : base(contextOptions)     {     } } public sealed class ApplicationDbContext2 : ApplicationDbContextBase {     public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)         : base(contextOptions)     {     } }    protected ApplicationDbContext(DbContextOptions contextOptions)         : base(contextOptions)     {     }

 

DBContext的两个关键方法

OnConfiguring配置方法

DBContext的配置入口
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)     {         //配置数据库提供程序,还有其他数据库配置如下图:         optionsBuilder.UseSqlServer(@"Server=(localdb)mssqllocaldb;Database=Test");         //日志记录配置,通过在控制台输出         optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information).EnableSensitiveDataLogging();         //关于其他DBContext配置,参看下图     }     protected override void OnModelCreating(ModelBuilder modelBuilder)     {         base.OnModelCreating(modelBuilder, typeof(BaseEntity));     }

关于其他数据库提供程序
数据库系统
配置示例
NuGet 程序包
SQL Server 或 Azure SQL
.UseSqlServer(connectionString)
Azure Cosmos DB
.UseCosmos(connectionString, databaseName)
SQLite
.UseSqlite(connectionString)
EF Core 内存中数据库
.UseInMemoryDatabase(databaseName)
PostgreSQL*
.UseNpgsql(connectionString)
MySQL/MariaDB*
.UseMySql(connectionString)
Oracle*
.UseOracle(connectionString)
关于DbContextOptionsBuilder 的更多配置
DbContextOptionsBuilder 方法
作用
了解更多
设置查询的默认跟踪行为
获取 EF Core 日志的一种简单方法
注册Microsoft.Extensions.Logging工厂
在异常和日志记录中包括应用程序数据
更详细的查询错误(以性能为代价)
忽略或引发警告和其他事件
注册 EF Core 侦听器
使用动态代理进行延迟加载
使用动态代理进行更改跟踪
即将推出...

 

OnModelCreating配置方法

实体的配置入口方法
      protected override void OnModelCreating(ModelBuilder modelBuilder)     {          modelBuilder.Entity<AuditEntry>();     }

 
使用 fluent API 配置模型替代OnModelCreating
关于实体的配置,还可使用Fluent api 可在上下文中替代 OnModelCreating 方法,并使用 Fluent API 来配置模型。 此配置方法最为有效,并可在不修改实体类的情况下指定配置。 Fluent API 配置具有最高优先级,并将替代约定和数据注释。 配置按调用方法的顺序应用,如果存在任何冲突,最新调用将替代以前指定的配置。
查看代码

 modelBuilder.Entity<Blog>()             .Property(b => b.Url)             .IsRequired();  //代替修改实体 还有这种等等             [NotMapped] public class BlogMetadata protected override void OnModelCreating(ModelBuilder modelBuilder) {     modelBuilder.Ignore<BlogMetadata>(); }

 

DBContext的结构

模型

模型由实体类和表示数据库会话的上下文对象构成。 上下文对象允许查询并保存数据
     public class BloggingContext : DbContext {     public DbSet<Blog> Blogs { get; set; }     public DbSet<Post> Posts { get; set; }      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)     {         optionsBuilder.UseSqlServer(             @"Server=(localdb)mssqllocaldb;Database=Blogging;Trusted_Connection=True");     } }  public class Blog {     public int BlogId { get; set; }     public string Url { get; set; }     public int Rating { get; set; }     public List<Post> Posts { get; set; } }

 

实体

在上下文中包含一种类型的 DbSet 意味着它包含在 EF Core 的模型中,或者在OnModelCreating 指定的;我们通常将此类类型称为实体

加入实体的三种方式

  • 在上下文的 DbSet 属性中公开。
  • 通过实体 Blog.Posts 的导航属性发现的。
  • 在 OnModelCreating 中指定的。
 internal class MyContext : DbContext {      public DbSet<Blog> Blogs { get; set; }      protected override void OnModelCreating(ModelBuilder modelBuilder)     {          modelBuilder.Entity<AuditEntry>();     } }  public class Blog {     public string Url { get; set; }      public List<Post> Posts { get; set; } }  public class Post {      public string Content { get; set; }  }

 

实体的属性约束

关于属性的约束,查看一下链接 实体属性 - EF Core | Microsoft Learn
以下列出常用
等等
 
[NotMapped]
 
[Column("blog_id")]
指定数据库列映射,数据库列与实体列不一致
[Column(TypeName = "varchar(200)")]
 
[MaxLength(500)]
 
[Required]
 
[Comment("The URL of the blog")]
描述
[Column(Order = 2)]
 

 

实体状态变化规则

DBContext上下文为跟踪状态时,实体变化如下;

 

EntityState的5个状态解释:

 
Entity Framework的最佳实践一
 
成员名称
说明
Detached
对象存在,但没有被跟踪。 在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态。 An entity is also in this state after it has been removed from the context by calling the Detach method or if it is loaded by using a NoTrackingMergeOption. 没有 ObjectStateEntry 实例与状态为 Detached 的对象关联。
Unchanged
自对象附加到上下文中后,或自上次调用 SaveChanges 方法后,此对象尚未经过修改。
Added
对象为新对象,并且已添加到对象上下文,但尚未调用 SaveChanges 方法。 在保存更改后,对象状态将更改为 Unchanged。 状态为 Added 的对象在 ObjectStateEntry 中没有原始值。
Modified
对象上的一个标量属性已更改,但尚未调用 SaveChanges 方法。 在不带更改跟踪代理的 POCO 实体中,调用 DetectChanges 方法时,已修改属性的状态将更改为 Modified。 在保存更改后,对象状态将更改为 Unchanged。
Deleted
删除状态

 

手动设置实体状态

  _mIPODataDBContext.Entry(t_TaskOrderFiles).Property(x => x.Url).IsModified = true;  _mIPODataDBContext.SaveChanges();

 

DBContext的查询方式

跟踪和不跟踪查询(AsNoTracking)

 //跟踪 var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1); blog.Rating = 5; context.SaveChanges(); //不跟踪 var blogs = context.Blogs     .AsNoTracking()     .ToList(); //在上下文实例级别取消跟踪 context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

 

导航查询(Include,ThenInclude)

   //同级Include,子级,更深级别的关联 ThenInclude   var blogs = context.Blogs         .Include(blog => blog.Posts)         .ThenInclude(post => post.Author)         .ToList();

 

Inner Join查询

通过linq表达式
 var query = from photo in context.Set<PersonPhoto>()             join person in context.Set<Person>()                 on new { Id = (int?)photo.PersonPhotoId, photo.Caption }                 equals new { Id = person.PhotoId, Caption = "SN" }             where b.ProjectOrderId == Guid.Parse(proIdParam.Value) && p.IsDel == false             select new { person, photo };              SELECT 

.[PersonId],

.[Name],

.[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo] FROM [PersonPhoto] AS [p0] INNER JOIN [Person] AS

ON ([p0].[PersonPhotoId] =

.[PhotoId] AND ([p0].[Caption] = N'SN'))

 
通过lamada表达式
 var query = from b in context.Set<Blog>()             from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)             select new { b, p };              SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], 

.[PostId],

.[AuthorId],

.[BlogId],

.[Content],

.[Rating],

.[Title] FROM [Blogs] AS [b] INNER JOIN [Posts] AS

ON [b].[BlogId] =

.[BlogId]

 

Left Join查询

lamada表达式
 var query = from b in context.Set<Blog>()             join p in context.Set<Post>()                 on b.BlogId equals p.BlogId into grouping             from p in grouping.DefaultIfEmpty()             select new { b, p };

 
linq表达式
 var query2 = from b in context.Set<Blog>()              from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()              select new { b, p };  SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], 

.[PostId],

.[AuthorId],

.[BlogId],

.[Content],

.[Rating],

.[Title] FROM [Blogs] AS [b] LEFT JOIN [Posts] AS

ON [b].[BlogId] =

.[BlogId]

 

GroupJoin查询

 var query = from b in context.Set<Blog>()             join p in context.Set<Post>()                 on b.BlogId equals p.BlogId into grouping             select new { b, Posts = grouping.Where(p => p.Content.Contains("EF")).ToList() };

 

GroupBy查询

 var query = from p in context.Set<Post>()             group p by p.AuthorId             into g             where g.Count() > 0             orderby g.Key             select new { g.Key, Count = g.Count() };              SELECT 

.[AuthorId] AS [Key], COUNT(*) AS [Count] FROM [Posts] AS

GROUP BY

.[AuthorId] HAVING COUNT(*) > 0 ORDER BY

.[AuthorId]

支持的其他聚合运算符
.NET
SQL
Average(x => x.Property)
AVG(Property)
Count()
COUNT(*)
LongCount()
COUNT(*)
Max(x => x.Property)
MAX(Property)
Min(x => x.Property)
MIN(Property)
Sum(x => x.Property)
SUM(Property)

 

纯SQL查询

  var rowsModified = context.Database.ExecuteSql($"UPDATE [Blogs] SET [Url] = NULL");    var overAverageIds = context.Database     .SqlQuery<int>($"SELECT [BlogId] AS [Value] FROM [Blogs]")     .Include(b => b.Posts)     .Where(id => id > context.Blogs.Average(b => b.BlogId))     .ToList();