- 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个状态解释:
成员名称
|
说明
|
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();