使用 Entity Framework Core 8.0 — 正确的方法!— ASP.NET 核心

作者:微信公众号:【架构师老卢】
3-26 10:8
31

概述:长期以来,使用实体框架 (EF) 一直是应用程序设计人员面临的挑战。这是因为大多数应用程序设计人员被实体框架的 ORM 性质分散了注意力,并且无法超越它。Entity Framework 8.0 是一个经过深思熟虑的库,在过去十年中不断发展,具有许多有用的功能。工具箱中最新升级的功能是精心设计的数据库迁移。在应用程序中使用实体框架之前,我们需要了解三个重要的策略:1 — 延迟加载2 — CRUD 操作3 — 使用 TransactionScope1 — 延迟加载在生产就绪型应用程序中使用 Entity Framework 8.0 的第一步是使用_延迟加载_。Entity Framework 中

长期以来,使用实体框架 (EF) 一直是应用程序设计人员面临的挑战。这是因为大多数应用程序设计人员被实体框架的 ORM 性质分散了注意力,并且无法超越它。

Entity Framework 8.0 是一个经过深思熟虑的库,在过去十年中不断发展,具有许多有用的功能。工具箱中最新升级的功能是精心设计的数据库迁移。

在应用程序中使用实体框架之前,我们需要了解三个重要的策略:

1 — 延迟加载

2 — CRUD 操作

3 — 使用 TransactionScope

1 — 延迟加载

在生产就绪型应用程序中使用 Entity Framework 8.0 的第一步是使用_延迟加载_。

Entity Framework 中的延迟加载意味着我们只加载需要使用和操作的数据。

当然,Entity Framework 也提供了 Eager Loading,以一种快速而肮脏的方式对新应用程序_进行快速原型设计_,但它的使用确实会带来副作用。

事实是,Eager Loading 没有经过优化,无法用于高可用性生产就绪型应用程序。

在 Entity Framework 8.0 中,默认情况下启用延迟加载。

下面是我们如何在将 Entity Framework 8.0 的 DbContext 注入任何类构造函数之前对其进行初始化:

var myConnectionString = "/* some EF 8.0 connection string here */";   
  
services.AddDbContext<MyDbContext>(options =>   
  options.UseSqlServer(myConnectionString), ServiceLifetime.Scoped);

2 — CRUD 操作

第二种策略是改进 CRUD 操作的使用。

以下是如何使用 Entity Framework 优化 CRUD 操作:

创建:保存并忘记

若要使用 Entity Framework 创建记录,请按照下列步骤操作:

1 — 创建实体对象。

2 — 将实体对象添加到 DbContext。

3 – 保存 DbContext 更改。

4 — 将实体对象与 DbContext 状态跟踪分离。

上面,前三个步骤非常标准。不过,这是值得回顾的第四步。

这里的想法是,一旦我们通过将实体对象添加到 DbContext 创建了新的数据库记录,我们必须禁用该对象的更改跟踪,否则实体框架将不会释放该对象并造成不利影响,例如无法在其他地方操作新记录。

以下是我们如何实现上述四个步骤来创建记录:

// Class: CreateUser

public string Username{ get; set; }  
  
public int Execute(MyDbContext dataContext)  
{  
  // Create Entity Object  
  var rec = new UserDb();  
  rec.Username = this.Username;  
  
  // Add Entity Object to DbContext  
  dataContext.Add(rec);  
  
  // Save Changes (This will create a new record in database)  
  dataContext.SaveChanges();  
  
  // Detach Entity Object to release/unload database record from DbContext  
  dataContext.Entry(rec).State = EntityState.Detached;  
    
  // return newly created record Id  
  return rec.UserId;  
}

检索:加载到映射

若要检索现有记录,请始终仅加载所需的记录。加载后,将它们映射到标准 POCO,并使用这些 POCO 来避免 DbContext 更改跟踪导致的记录锁定。这样,我们的 DbContext 将永远不会锁定数据库记录。

下面是如何使用 Entity Framework 检索记录并将其映射到 POCO:

// Class: GetUser

public int UserId { get; set; }  
  
public UserData Execute(MyDbContext dataContext)  
{  
  // Get Record  
  var data = (from a in dataContext.Users  
              where a.UserId == this.UserId  
              select a).FirstOrDefault();  
  
  // did we find the record?  
  if (data != null)  
  {  
    // Map Entity Object to POCO  
    var obj = this.Mapper.Map<UserData>(data);  
  
    // Return POCO to use  
    return obj;  
  }  
  
  return null;  
}

更新:加载、保存和释放

在 Entity Framework 中更新记录应与创建和检索记录的方式相同。

以下是使用实体框架更新记录的步骤:

  • 加载数据
  • 修改数据
  • 保存数据
  • 通过不重复使用修改的记录来释放数据。

如前所述,实体框架跟踪对已修改的实体所做的更改。因此,一旦修改并保存了现有实体,我们就不应该重复使用它们。

下面是如何在 Entity Framework 8.0 中执行更新操作的:

// Class: SaveUser

public int UserId { get; set; }  
public string Username{ get; set; }    
  
public bool Execute(MyDbContext dataContext)  
{  
  // Retrieve Record  
  var rec = (from a in dataContext.Users  
            where a.UserId == this.UserId  
             select a).FirstOrDefault();  
  
  if (rec != null)  
  {  
    // Update Record  
    rec.UserId = this.UserId;  
    rec.Username = this.Username;  
  
    // Save Record Changes  
    dataContext.SaveChanges();  
  
    return true;  
  }  
  
  return false;  
}

如果您觉得需要使用更新的实体对象,则不要使用实体对象,而是将实体对象映射到 POCO,并在需要使用的位置引用该 POCO。

删除:加载、删除和忘记

实体框架中的删除与更新非常相似。

以下是我们在 Entity Framework 中删除对象的方法:

// Class: DeleteUser

public int UserId { get; set; }  
  
public bool Execute(MyDbContext dataContext)  
{  
  // Retrieve Record  
  var rec = from a in dataContext.PhoneNumbers  
            where a.UserId == this.UserId  
            select a;  
  
  // Delete Record  
  dataContext.RemoveRange(rec);  
    
  // Save Changes  
  dataContext.SaveChanges();  
  
  return true;  
}

3 — 使用 TransactionScope

第三个也是最重要的策略是将上述所有 CRUD 操作包装在 TransactionScope 中。

TransactionScope 是一个 .NET 类,一旦使用“_using”_块实例化,它就会将 using 块中包含的所有 CRUD 操作封装在一个事务下。它通过检测事务管理器的可用性来实现。

using (var transactionScope = new TransactionScope())  
{  
    /*  
 Multiple SQL CRUD Operations where  
 */  
  
    transactionScope.Complete();  
}

在上面的示例中,创建一个 TransactionScope 实例足以将 using 区块中的所有 CRUD 操作包装在一个事务下。我们必须在 TransactionScope 上调用 Complete() 函数来提交更改,否则即使我们单独调用了每个 CRUD 操作的 DbContext.SaveChanges(),所有更改也会回滚。

对于实体框架,TransactionScope 必须与以下配置一起使用,否则我们可能会遇到过早中止的事务。

var transactionOptions =  
  new TransactionOptions  
  {  
    IsolationLevel = IsolationLevel.ReadCommitted,  
    Timeout = TimeSpan.FromSeconds(30)  
  };  
  
var transactionFlow = TransactionScopeAsyncFlowOption.Enabled;  
  
using(var scope = new TransactionScope(transactionOptions, transactionFlow))  
{  
  
  /* Some CRUD Operations here */  
  
}

上面是我们如何为 Entity Framework 8.0 配置 TransactionScope。

将一切结合在一起

最后,我们可以总结上述知识,编写一个创建、检索、更新并在最后删除用户记录的 UnitOfWork

var transactionOptions =  
  new TransactionOptions  
  {  
    IsolationLevel = IsolationLevel.ReadCommitted,  
    Timeout = TimeSpan.FromSeconds(30)  
  };  
  
var transactionFlow = TransactionScopeAsyncFlowOption.Enabled;  
  
using (scope = TransactionScope(transactionOptions, transactionFlow))  
{  
  
  // Create a User  
  new CreateUser   
  {   
    Username = "testuser"  
  }.Execute(this.MyDbContext);  
    
  // Retrieve a User  
  var user = new GetUser  
    {  
      UserId = 1  
    }.Execute(this.MyDbContext);  
  
  // Update a User  
  new UpdateUser  
  {  
    UserId = 1,  
    UserName = "testuser-2"  
  }.Execute(this.MyDbContext);  
  
  // Delete a User  
  new DeleteUser  
  {  
    UserId = 1  
  }.Execute(this.MyDbContext);  
  
  // Commit Transaction  
  transactionScope.Complete();  
}
相关留言评论
昵称:
邮箱:
阅读排行