【问题标题】:Is there a way to find all Entities that have had their relationships deleted?有没有办法找到所有已删除关系的实体?
【发布时间】:2011-12-16 20:11:08
【问题描述】:

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

但实体框架让这变得困难。我可以在不引用 ObjectContext 的情况下插入集合(在我的业务层中):

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

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

但是要从集合中删除一个项目,我需要一个 ObjectContext 的引用。 (我是guide to deleting EF Entities 中的案例#1。)如果我这样做:

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

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

所以,我的选择是:

  1. 将委托传递给将调用实体框架 ObjectContext Delete 方法的业务逻辑。
  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 有一些基本的东西我不明白。)

【问题讨论】:

标签: c# .net entity-framework entity-framework-4


【解决方案1】:

EF 的正确解决方案是链接文章中的第 3 点。这意味着将 FK 传播到主体实体到依赖实体的 PK。这将形成一个名为identifying relation 的东西,当它从父实体中删除时,它会自动删除依赖实体。

如果你不想改变你的模型并且仍然想以持久无知的方式实现它,你可能可以,但它只适用于independent associations。一些至少适用于我的简单测试解决方案的初始实现:

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 属性已经意味着您在模型中包含了额外的持久性依赖项。

【讨论】:

  • 感谢您的回答。听起来我很不走运,因为我只是使用从数据库生成时的默认值。那似乎是FK协会。感谢您的博客文章阐明了 EF 中的两种类型的关联。 (我不知道有不止一个!)我要多挖一点,看看我是否能找到一些方法来检测 FK 关系已被删除。 (我宁愿不改变我的项目来尝试使用独立关联,特别是因为它们只支持一对多关系。)
  • 作为一个附带问题,您如何才能拥有“一对多”关系并拥有识别关系?我知道对“一对多”建模的唯一方法是将“一”表的 PK 作为“多”表中的一列并将其用作 FK(返回“一”表)。有没有办法对识别关系进行建模,以便我拥有一对多关系,允许删除引用并自动删除“多”表中的关联条目?
  • 您是否检查了我描述识别关系的链接答案。当然,您可以将它与一对多一起使用。您只需要在由其自己的密钥和 FK 组成的依赖实体上进行复合 PK。
  • 啊!谢谢你。我觉得很愚蠢,因为我没有想到这一点。 (在我的工作中,我们有一个反复合 PK 的污点,所以我不经常考虑这个选项。)
【解决方案2】:

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

    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);

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

【讨论】:

  • 这看起来好像可行,但我必须将它连接到每个集合。然后,当其他人添加集合时,他们必须记住也将其连接起来。 (最终有人会忘记并发布一个错误。)
猜你喜欢
  • 1970-01-01
  • 2021-01-24
  • 2011-04-13
  • 2017-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多