有没有办法找到已经删除关系的所有实体? [英] Is there a way to find all Entities that have had their relationships deleted?

查看:115
本文介绍了有没有办法找到已经删除关系的所有实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图没有我的商业逻辑知道我的数据层的内部工作,反之亦然。



但实体框架正在努力。我可以插入一个集合(在我的业务层),而不引用ObjectContext:

  order.Containers.Add容器{ContainerId = containerId,Order = order}); 

当时候执行 SaveChanges()



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

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

然后当我调用 SaveChanges()得到一个例外,告诉我,我需要删除对象以及参考。



所以,我看到的选项是:


  1. 将代理传递给我的业务逻辑,它将调用Entity Framework ObjectContext Delete方法。

  2. 或者(我希望)找到一种方法,让所有已经删除其引用的实体被删除,并实际删除它们。(在调用 SaveChanges()在我的数据层。)

有没有人知道这样做?



更新:



我尝试过:

  //在调用Save Changes时添加事件
this.ObjectContext.SavingChanges + = OnSavingChanges;

...

  void OnSavingChanges(object sender,EventArgs e)
{
var objectStateEntries = ObjectContext.ObjectStateManager
.GetObjectStateEntries(EntityState.Deleted);

foreach(objectStateEntries中的var objectStateEntry)
{
if(objectStateEntry.IsRelationship)
{
//找到一些删除相关实体的方法
}
}
}

但是,即使我删除了关系,删除的项目的集合是空的。



(我尝试查看所有的项目,我的关系不在那里。显然有一些根本的,我没有得到关于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; e.IsRelationship))
{
var entry = GetEntityEntryFromRelation(relationEntry,0);
//查找关系的表示
IRelatedEnd relatedEnd = entry.RelationshipManager
.GetAllRelatedEnds()
.First(r => r.RelationshipSet == relationEntry.EntitySet);

RelationshipType relationshipType = relatedEnd.RelationshipSet.ElementType;
if(!SkipDeletion(relationshipType))
{
//现在我们知道模型不一致,许多方面的实体必须被删除
如果(!(relatedEnd是EntityReference) )//相关结束是很多方
{
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);
返回条目;
}

private bool SkipDeletion(RelationshipType relationshipType)
{
return
//多对多
relationshipType.RelationshipEndMembers.All (
r => r.RelationshipMultiplicity == RelationshipMultiplicity.Many)||
// ZeroOrOne-to-many
relationshipType.RelationshipEndMembers.Any(
r => r.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);
}
}

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



外援协会的情况可能会更糟,因为在国家经理中找不到任何删除的关系。您将不得不手动跟踪对集合或键的更改,并将其进行比较以找到差异(我不知道如何以通用方式执行)外键关联IMHO需要识别关系。使用FK属性已经意味着您在模型中包含了额外的持久性依赖。


I am trying to not have my Business Logic know the inner workings of my Data Layer and vica versa.

But Entity Framework is making that hard. I can insert into a collection (in my Business Layer) without a reference to the ObjectContext:

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

And that saves fine when it comes time to do a SaveChanges() in the Data Layer.

But to delete an item from a collection I need a reference to the ObjectContext. (I am case #1 in this guide to deleting EF Entities.) If I just do this:

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

Then when I call SaveChanges() I get an exception telling me that I need to delete the object as well as the reference.

So, my options as I see it are:

  1. To pass a delegate to my Business Logic that will call the Entity Framework ObjectContext Delete method.
  2. Or (I am hoping) find a way to get all entities that have had their reference deleted and actually delete them. (Right before calling SaveChanges() in my data layer.)

Does anyone know a way to do that?

UPDATE:

I tried this:

// 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
       }
   }
}

But none even though I deleted a relationship, the set of deleted items is empty.

(I tried viewing all the items too and my relationship is not in there. Clearly there is something fundamental that I don't get about ObjectStateManager.)

解决方案

The correct solution for EF is point 3. from the linked article. It means propagating FK to principal entity into PK for dependent entity. This will form something called identifying relation which automatically deletes dependent entity when it is removed from the parent entity.

If you don't want to change your model and still want to achieve that in persistence ignorant way you probably can but it will work only for independent associations. Some initial implementation which works at least for my simple tested solution:

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

To make it work your entities must be enabled for dynamic change tracking (all properties must be virtual and entity must be proxied) or you must manually call DetectChanges.

In case of foreign key associations the situation will be probably much worse because you will not find any deleted relation in the state manager. You will have to track changes to collections or keys manually and compare them to find discrepancies (I'm not sure how to do it in generic way) Foreign key association IMHO requires the identifying relation. Using FK properties already means that you included additional persistence dependency into your model.

这篇关于有没有办法找到已经删除关系的所有实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆