【问题标题】:When overriding SaveChanges and removing the EF relationship is null覆盖 SaveChanges 并删除 EF 关系时为 null
【发布时间】:2016-11-11 10:00:42
【问题描述】:

在我的多对一关系中,我试图删除其中一个子对象并在我覆盖的 SaveChanges 中保留审计跟踪。

执行 EntityState.Modified 或 EntityState.Added 时,file.Entity.Product 不为空,但执行 .Deleted 时,EF 似乎甚至在调用 base.Savechanges() 之前就积极地从实体中删除了关系。

当我在文件(子)上调用 .Remove 后,有什么方法可以在我的 SaveChanges 覆盖中检索与此文件关联的产品?

var files = from e in ChangeTracker.Entries<SavedFile>()
                    where e.State != EntityState.Unchanged
                    select e;
if (file.State == EntityState.Deleted)
            {
                var text = "File Deleted: " + file.Entity.FriendlyFileName;
                // file.Entity.Product is null
                var updatedProduct = new Update { Product = file.Entity.Product, UpdateDateTime = DateTime.Now, UpdateText = text, User = HttpContext.Current.Request.LogonUserIdentity.Name };
                Updates.Add(updatedProduct);
            }

【问题讨论】:

  • 我猜有一个foreach 循环在files 上分配file 的值,对吧?
  • 如果您使用流体映射,您可以将必填字段存储在属性中并将其标记为 NotMapped 或 Ingnore

标签: c# asp.net entity-framework-6


【解决方案1】:

正如您在ChangeTracker 的条目中找到文件一样,您可以找到它的产品,假设FileProduct 有一个外键关联,即它有一个ProductID 除了Product 导航属性之外的属性:

if (file.State == EntityState.Deleted)
{
    var text = "File Deleted: " + file.Entity.FriendlyFileName;
    var productEntry = ChangeTracker.Entries<Product>()
                                    .FirstOrDefault(p => p.Entity.ID == file.ProductID);
    if (productEntry != null)
    {
        var updatedProduct = new Update { Product = productEntry.Entity, UpdateDateTime = DateTime.Now, UpdateText = text, User = HttpContext.Current.Request.LogonUserIdentity.Name };
        Updates.Add(updatedProduct);
    }
}

当实体是来自本地缓存的查询时(例如DbSet.Local),EF 不会显示已删除的实体是预期行为。因此它还必须打破关联以防止这些实体出现在导航属性中。如果你删除一个文件,EF 不会在context.Files.Local 中显示它,但它在product.Files 之类的导航属性中也不会显示(关联的另一端file.Product 也不会显示)。

【讨论】:

  • 这样你又在查询数据库了!
  • 一点也不。我正在查询ChangeTracker.Entries&lt;Product&gt;()
  • 否决票已锁定,请编辑此答案以便能够投票
  • 我有效地使用了你的方法,但我必须做一些调整才能让它发挥作用。我必须打开预先加载,设置外键关联,并且我必须加载所有条目以尝试将它们转换为“产品”,因为调用 .Entries() 总是会导致 null。我将在星期一发布我的完整解决方案。
  • 好的,谢谢。我很想知道为什么Entries&lt;Product&gt;() 没有按预期工作。这意味着不存在未更改的Products (?)。但是他们不应该需要审计——好吧,我会看到......
【解决方案2】:

我认为删除后没有办法访问关系

但是,你为什么不使用软删除,我认为这是最好的做法,如果被错误删除,你可以随时返回删除的记录(通过标记 IsDeleted = false,由具有权限的管理员)

希望对你有帮助

【讨论】:

    【解决方案3】:

    如果您只需要 Product 属性,您可以在其定义中使用一些自定义逻辑来捕获它:

    class File {
       ...
       private Product product;
       private Product lastProduct;
    
       public Product Product {
          get { return product; }
          set {
              if (product == value) return;
              lastProduct = Product;
              product = value;
          }
       }
    
       [NotMapped]
       public Product LastProduct {
          get { return lastProduct; }
       }
       ...
    }
    

    【讨论】:

      【解决方案4】:

      Gert Arnold 提供的答案应该有效 - 但不确定检查是否有意义 FirstOrDefault(p => p.State != EntityState.Unchanged 因为相关实体可能仍处于未更改状态。

      如果没有任何效果,尝试在重写的 SaveChanges 方法中重新加载实体。

      this.Entry((file.Entity as <<Your Entity Type>>)).Reload();
      //do your operation/audit log
      //then reset the Entity State to Deleted
      this.Entry((file.Entity)).State = EntityState.Deleted;
      

      编辑:或仅显式加载所需的相关实体

      this.Entry(file.Entity as <Type>).Reference(e=> e.Product).Load();
      

      这会在重新加载时导致额外的数据库命中。

      编辑:另一个选项,如果没有找到其他好的选项,请再次选择

      如何在这种情况下执行原始 SQL 插入查询来填充审计表?

      【讨论】:

      • 加载实体会产生各种副作用。在保存更改的同时执行此操作不是一个好主意。如果这无关紧要,这也会使SaveChanges 成为一项潜在的昂贵操作。
      • @GertArnold- 没错,我的意思是只对已删除的实体执行此操作..这就是为什么我有点指定 if nothing works;正在考虑一个最坏的情况,即延迟加载被禁用并且相关实体没有急切/显式加载;那么在这种情况下,可能击中 db 是唯一的选择。正如我所提到的,您的答案应该有效;尝试对您的答案进行投票,但在这里作为新手无法做到这一点......似乎我需要一些分数才能投票:)
      • 我认为查询 ChangeTracker 会失败的另一种情况是断开连接。获取详细信息的上下文可能在 get 操作之后被释放,并且在执行删除操作时,它可能位于不同的上下文中。那么如果我们只做 context.Entry() 会怎样。状态 = EntityState.Deleted?不确定相关的实体/导航属性详细信息是否会出现在更改跟踪器中......只是大声思考......
      • 如果 OP 谈到 积极地从实体中删除关系,我假设关系首先存在。如果没有,他们无论如何都必须从数据库中获取实体才能删除它。
      猜你喜欢
      • 1970-01-01
      • 2012-05-05
      • 2015-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多