Entity Framework Core 使用指南

Entity Framework Core(EF Core) 是一个轻量级的、可扩展的开源ORM(对象关系映射)框架,它允许开发者通过 POCO 类与数据库实体进行映射,并通过 DbContext 与数据库进行交互。本文将详细介绍如何使用 EF Core 进行数据库操作,包括创建类库、配置 DbContext、实现CRUD操作、查询、事务处理、日志记录和处理并发冲突。

创建类库并添加 NuGet 包

首先,需要创建一个类库,并添加以下 NuGet 包:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Relational
  • Microsoft.EntityFrameworkCore.SqlServer

接下来,创建一个代表数据库实体的类:

public class Actor { public int Id { get; set; } public string Name { get; set; } }

然后,创建一个继承自 DbContext 的类:

public class Database : DbContext { public Database(DbContextOptions options) : base(options) { } public DbSet Actors { get; set; } }

创建ASP.NET CoreWeb 应用程序和 API 控制器

为了实现CRUD操作,需要创建一个ASP.NET CoreWeb 应用程序和一个 API 控制器。以下是一些基本的 CRUD 操作示例:

[HttpGet] public async Task GetList() { var entities = await context.Actors.ToListAsync(); var outputModel = entities.Select(entity => new { entity.Id, entity.Name, }); return Ok(outputModel); } [HttpGet("{id}", Name = "GetActor")] public IActionResult GetItem(int id) { var entity = context.Actors.Where(e => e.Id == id).FirstOrDefault(); if (entity == null) return NotFound(); var outputModel = new { entity.Id, entity.Name, }; return Ok(outputModel); } [HttpPost] public async Task Create([FromBody]ActorCreateInputModel inputModel) { if (inputModel == null) return BadRequest(); var entity = new Actor { Name = inputModel.Name }; this.context.Actors.Add(entity); await this.context.SaveChangesAsync(); var outputModel = new { entity.Id, entity.Name }; return CreatedAtRoute("GetActor", new { id = outputModel.Id }, outputModel); } [HttpPut("{id}")] public IActionResult Update(int id, [FromBody]ActorUpdateInputModel inputModel) { if (inputModel == null || id != inputModel.Id) return BadRequest(); var entity = new Actor { Id = inputModel.Id, Name = inputModel.Name }; this.context.Actors.Update(entity); this.context.SaveChanges(); return NoContent(); } [HttpDelete("{id}")] public IActionResult Delete(int id) { var entity = context.Actors.Where(e => e.Id == id).FirstOrDefault(); if (entity == null) return NotFound(); this.context.Actors.Remove(entity); this.context.SaveChanges(); return NoContent(); }

配置 DbContext

在 Startup 类中配置 DbContext:

public void ConfigureServices(IServiceCollection services) { var connection = "Data Source=..."; services.AddDbContext(options => options.UseSqlServer(connection)); services.AddMvc(); }

Entity Framework Core(EF) 是一个ORM,它通过使用 POCO 类映射数据库实体,并通过 DbContext 与它们进行交互,使得与数据库的工作变得更加简单。

如代码所示,最佳(且可测试)的配置 DbContext 子类的方式是在其构造函数中注入 DbContextOptions。NuGet 包 Microsoft.EntityFrameworkCore 提供了一个扩展方法 AddDbContext 来设置自定义 DbContext(如解决方案部分所示)。

DbContext 的子类将有一个 DbSet 实体,通过它可以添加、更新和删除数据库记录。DbContext 上的 SaveChanges() 方法触发数据库更改,这意味着也可以在一个事务中添加/更新/删除多个项目:

this.context.Actors.Add(entity1); this.context.Actors.Add(entity2); this.context.Actors.Add(entity3); await this.context.SaveChangesAsync();

注意:SaveChanges() 方法有同步和异步版本。

使用 LINQ,可以查询 DbSet,这是 EF 的一个非常强大的特性。以下是一个更复杂的查询示例,用于检索电影及其导演和演员:

var entities = from movie in this.context.Movies join director in this.context.Directors on movie.DirectorId equals director.Id select new { movie.Id, movie.Title, movie.ReleaseYear, movie.Summary, Director = director.Name, Actors = ( from actor in this.context.Actors join movieActor in this.context.MovieActors on actor.Id equals movieActor.ActorId where movieActor.MovieId == movie.Id select actor.Name + " as " + movieActor.Role) };

注意:这个查询可以用几种不同的方式编写,包括使用导航属性。然而,由于没有在这里讨论它们,使用了简单的 LINQ。

注意:ToList() 方法有同步和异步版本。

DbContext 上的 SaveChanges() 方法提供了事务性设施,但也可以通过使用 DbContext 上的 DatabaseFacade 类型显式创建事务。例如,下面首先保存电影(以获取其 Id),然后保存演员:

using (var transaction = this.context.Database.BeginTransaction()) { try { // build movie entity this.context.Movies.Add(movieEntity); this.context.SaveChanges(); foreach (var actor in inputModel.Actors) { // build actor entity this.context.MovieActors.Add(actorEntity); } this.context.SaveChanges(); transaction.Commit(); } catch (System.Exception ex) { transaction.Rollback(); } }

可以使用ASP.NET Core日志记录功能来查看/记录发送到 SQL Server 的 SQL。为了做到这一点,需要使用 ILoggerProvider 和 ILogger 接口:

public class EfLoggerProvider : ILoggerProvider { public ILogger CreateLogger(string categoryName) { if (categoryName == typeof(IRelationalCommandBuilderFactory).FullName) { return new EfLogger(); } return new NullLogger(); } public void Dispose() { } #region " EF Logger " private class EfLogger : ILogger { public IDisposable BeginScope(TState state) => null; public bool IsEnabled(LogLevel logLevel) => true; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { Console.WriteLine(formatter(state, exception)); } } #endregion #region " Null Logger " private class NullLogger : ILogger { public IDisposable BeginScope(TState state) => null; public bool IsEnabled(LogLevel logLevel) => false; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { } } #endregion }

现在可以将这个日志记录器添加到工厂中:

public Startup(ILoggerFactory loggerFactory) { loggerFactory.AddProvider(new EfLoggerProvider()); }

通过命令行运行,将获得生成的 SQL:

注意:使用 DbContextOptionsBuilder 上的 EnableSensitiveDataLogging() 方法可以启用参数的日志记录,默认情况下,它们不会显示。

可以使用 ETag 来实现乐观并发,如这里所讨论的。然而,有时想要更多地控制过程,EF 提供了另一种选择。首先,在数据库(和实体 POCO)中创建一个字段,作为并发令牌,并用 [Timestamp] 属性注释:

public class Actor { public int Id { get; set; } public string Name { get; set; } [Timestamp] public byte[] Timestamp { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().Property(actor => actor.Timestamp).ValueGeneratedOnAddOrUpdate().IsConcurrencyToken(); } try { this.context.Actors.Update(entity); this.context.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { var inEntry = ex.Entries.Single(); var dbEntry = inEntry.GetDatabaseValues(); if (dbEntry == null) return StatusCode(StatusCodes.Status500InternalServerError, "Actor was deleted by another user"); var inModel = inEntry.Entity as Actor; var dbModel = dbEntry.ToObject() as Actor; var conflicts = new Dictionary(); if (inModel.Name != dbModel.Name) conflicts.Add("Actor", $"Changed from '{inModel.Name}' to '{dbModel.Name}'"); return StatusCode(StatusCodes.Status412PreconditionFailed, conflicts); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485