是否有办法找到所有关系已被删除的实体?

本文关键字:删除 实体 有关系 是否 | 更新日期: 2023-09-27 18:18:10

我试图不让我的业务逻辑知道我的数据层的内部工作,反之亦然。

但是实体框架使这变得困难。我可以插入一个集合(在我的业务层),而不需要引用ObjectContext:

order.Containers.Add(new Container { ContainerId = containerId, Order = order });

当需要在数据层做SaveChanges()时,这可以节省很多时间。

但是要从集合中删除一个项,我需要一个对ObjectContext的引用。(我是本指南中删除EF实体的案例1。)如果我这样做:

 delContainers.ForEach(container => order.Containers.Remove(container));

然后,当我调用SaveChanges()时,我得到一个异常,告诉我需要删除对象以及引用。

所以,我看到的选项是:

  1. 传递一个委托给我的业务逻辑,它将调用实体框架ObjectContext删除方法。
  2. 或者(我希望)找到一种方法来获得所有已删除其引用的实体,并实际删除它们。(在我的数据层调用SaveChanges()之前)

有人知道怎么做吗?

更新:

我试过了:

// Add an event when Save Changes is called
this.ObjectContext.SavingChanges += OnSavingChanges; 

void OnSavingChanges(object sender, EventArgs e)
{
   var objectStateEntries = ObjectContext.ObjectStateManager
                                  .GetObjectStateEntries(EntityState.Deleted);
                                                         
   foreach (var objectStateEntry in objectStateEntries)
   {
       if (objectStateEntry.IsRelationship)
       {
            // Find some way to delete the related entity
       }
   }
}

但是没有,即使我删除了一个关系,被删除的项集是空的。

(我试着查看所有的项目,我的关系不在那里。显然,关于ObjectStateManager有一些基本的东西我没有弄明白

是否有办法找到所有关系已被删除的实体?

EF的正确解是第3点。从链接的文章。它意味着将对主体实体的FK传播到从属实体的PK。这将形成一种称为识别关系的东西,当依赖实体从父实体中移除时,它会自动删除依赖实体。

如果你不想改变你的模型,并且仍然想要以持久化的方式实现它,你可能可以,但它只适用于独立的关联。一些最初的实现,至少对我的简单测试解决方案有效:

public partial class YourObjectContext
{
    public override int SaveChanges(SaveOptions options)
    {
        foreach (ObjectStateEntry relationEntry in ObjectStateManager
                                             .GetObjectStateEntries(EntityState.Deleted)
                                             .Where(e => e.IsRelationship))
        {
            var entry = GetEntityEntryFromRelation(relationEntry, 0);
            // Find representation of the relation 
            IRelatedEnd relatedEnd = entry.RelationshipManager
                                          .GetAllRelatedEnds()
                                          .First(r => r.RelationshipSet == relationEntry.EntitySet);
            RelationshipType relationshipType = relatedEnd.RelationshipSet.ElementType;
            if (!SkipDeletion(relationshipType))
            {
                // Now we know that model is inconsistent and entity on many side must be deleted
                if (!(relatedEnd is EntityReference)) // related end is many side
                {
                    entry = GetEntityEntryFromRelation(relationEntry, 1);
                }
                if (entry.State != EntityState.Deleted)
                {
                    context.DeleteObject(entry.Entity);
                }
            }
        }
        return base.SaveChanges();
    }
    private ObjectStateEntry GetEntityEntryFromRelation(ObjectStateEntry relationEntry, int index)
    {
        var firstKey = (EntityKey) relationEntry.OriginalValues[index];
        ObjectStateEntry entry = ObjectStateManager.GetObjectStateEntry(firstKey);
        return entry;
    }
    private bool SkipDeletion(RelationshipType relationshipType)
    {
        return
            // Many-to-many
            relationshipType.RelationshipEndMembers.All(
                r => r.RelationshipMultiplicity == RelationshipMultiplicity.Many) ||
            // ZeroOrOne-to-many 
            relationshipType.RelationshipEndMembers.Any(
                r => r.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);
    }
}

要使其工作,您的实体必须启用动态变化跟踪(所有属性必须是虚拟的,实体必须被代理),或者您必须手动调用DetectChanges

对于外键关联,情况可能会更糟,因为您不会在状态管理器中找到任何已删除的关系。您必须手动跟踪对集合或键的更改,并对它们进行比较以发现差异(我不确定如何以通用方式进行比较)。使用FK属性意味着您在模型中包含了额外的持久性依赖项。

一种方法是在数据层中编写一个更改处理程序:

    private void ContainersChanged(object sender,
        CollectionChangeEventArgs e)
    {
        // Check for a related reference being removed. 
        if (e.Action == CollectionChangeAction.Remove)
        {
            Context.DeleteObject(e.Element);
        }
    }

你可以在很多地方连接它——在你的对象的构造函数或存储库get或SavingChanges或任何地方:

    entity.Containers.AssociationChanged += new CollectionChangeEventHandler(ContainersChanged);

现在你可以从其他地方删除关联,它将"级联"到实体。