EF6 - 如何以优化的方式加载特定示例的实体

本文关键字:实体 加载 方式 优化 EF6 | 更新日期: 2023-09-27 17:56:13

我有Users - Rules many-to-many关系的表。我想删除某个用户的一些规则,并在同一步骤/数据库事务中更新该用户的一个字段,所以我在BL中有以下伪代码:

this.UnitOfWork.ExecuteTransaction(() =>
{
   // delete rules
   foreach (var rule in ruleList)
       repoRules.DeleteRulesForUser(user, rule);
   // update user field
   repoUser.UpdateUserField(user);
}

在 EF 存储库中:

public void DeleteRulesForUser(User user, Rule rule)
{
   // check user
   EFUser u = this.dbContext.EFUsers.Find(user.UID);
   if (u == null)
      throw new DBModuleException();
   // check rule
   EFRule r = this.dbContext.EFRules.Find(rule.UID);
   if (r == null)
      throw new DBModuleException();

   // detach previous entities, so that we can attach with relation 
   this.dbContext.ObjectContext.Detach(u);
   this.dbContext.ObjectContext.Detach(r);
   // prepare entities
   var efUser = new EFUser { US_UID = user.UID };
   var efRule = new EFRule { RU_UID = rule.UID };
   efRule.Users.Add(efUser);
   // attach
   this.dbContext.EFRules.Attach(efRule);
   // delete relation
   efRule.Users.Remove(efUser);
}
public void UpdateUserField(User user)
{
    // get updating user
    EFUser efUser = this.dbContext.EFUsers.FirstOrDefault(u => u.UID == user.UID);
    if (efUser == null)
       throw new DBModuleException("Not found!");
    // perform update
    efUser.US_Field = user.Field;
}

我的工作单元实现(仅适用于 EF6 或更高版本):

public override void ExecuteTransaction(Action transAction)
{
    // make sure to make IsolationLevel flexible later
    using (var dbTransaction = this.dbContext.Database
                              .BeginTransaction(IsolationLevel.ReadCommitted)) 
    {
       try
       {
           transAction();
           // save changes if not done yet
           this.dbContext.SaveChanges();
           // commit transaction
           dbTransaction.Commit();
       }
       catch (System.Data.DataException ex)
       {
           // rollback
           dbTransaction.Rollback();
           // handling exception ceremony comes here
       }
    }

我的问题是DeleteRulesForUser仅将该用户附加到UID(如方法中指定),但是稍后在UpdateUserField EF仅向该用户提供UID,而不是获取用户的完整数据,因为FirstOrDefault调用数据库。我认为这就是 EF 缓存的工作方式(?无论如何,更新操作失败,我想学习如何最好地解决这个问题。

我看到有时需要加载只有 UID 的实体,但有时需要加载完整的数据。

我想在方法结束时分离实体DeleteRulesForUser但我认为对性能不利,因为 BL 中有时会有 for 循环。我还想也许我应该重写我的 Update 方法,但还不知道这样做的好方法。

你会推荐什么?

仅供参考:我禁用了延迟加载。

注意:BL 不使用 EF 类,而是使用专有域类(稍后添加了 EF 支持)。不要批评现有的架构,我想要一个解决方案。

编辑1:当我阅读更多时,也许AutoDetectChangesEnabled可以提供帮助,但我需要更多信息来找出答案。

EF6 - 如何以优化的方式加载特定示例的实体

如果您需要删除数千条规则,我建议删除实体框架并使用 ADO.NET 和 sql 语句(或存储过程),这更快、更简单。请参阅批量更新/删除 EF5。

也没有理由附加或分离对象。如果使用 EF,请在需要时创建上下文并尽快释放它。

没有理由将对象与对象状态管理器分离。 仅仅通过查看代码,它对你想要做的事情没有多大意义。 也许你对 EF 没有太多经验,但我建议在使用 EF 之前先阅读它的工作原理。

只需加载 User 实体,并在批处理操作的结果集中包含规则:var dbUser = ctx.Users.Include(x => x.Rules).FirstOrDefault(x => x.UID == user.UID);

现在,您已经拥有完全填充的用户对象以及与之关联的所有规则,您可以找到要删除的规则:var ruleToDelete = dbUser.Rules.FirstOrDefault(x => x.UID == rule.UID);

现在,您拥有了要删除的用户的规则,并且不会花费您返回数据库的行程。 现在,您可以删除规则:ctx.Rules.Remove(ruleToDelete);

现在,当您调用ctx.SaveChanges()更改将被推送回数据库。 您确实需要了解,实体框架会自动将所有insertupdatedelete语句包装在事务中。 此外,EF 不支持批处理insertupdatedelete操作。

//Get the user from the db, should check for null after this
var dbUser = ctx.Users.Include(x => x.Rules).FirstOrDefault(x => x.UID == user.UID);
//Get the rule from the user object you want to remove
var ruleToDelete = dbUser.Rules.FirstOrDefault(x => x.UID == rule.UID);
//Set the state of the object to "Deleted" in the state manager so it will issue a delete statement
ctx.Rules.Remove(ruleToDelete);
//Save the changes to the database in a transaction
ctx.SaveChanges()