实体框架6:检测关系变化 [英] Entity Framework 6: detect relationship changes

查看:83
本文介绍了实体框架6:检测关系变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的DbContext子类中,我已经覆盖了SaveChanges()方法,所以我可以实现一种类似Trigger的功能(在更改实际保存之前)。
现在,在其中一些触发器中,有必要检测某些关系是否发生变化,无论多对多,一对一/零等。


$ b $我已经阅读了互联网上的一些帖子,其中包括本网站上的一些帖子,提到DbContext API没有揭示任何获取关系信息的方法。
但是,ObjectContext应该能够。



我的SaveChanges方法:

 code> public override int SaveChanges()
{
IEntity entity;
ChangeTracker.DetectChanges();

var stateManager =((IObjectContextAdapter)this).ObjectContext.ObjectStateManager;
var added = stateManager.GetObjectStateEntries(EntityState.Added).ToList();
var updated = stateManager.GetObjectStateEntries(EntityState.Modified).ToList();
var deleted = stateManager.GetObjectStateEntries(EntityState.Deleted).ToList();
varchanged = stateManager.GetObjectStateEntries(EntityState.Unchanged).ToList();

while((entity = _entitiesRequiringTriggering.FirstOrDefault(x => x.Value).Key)!= null)
{
_entitiesRequiringTriggering [entity] = false;
var entry = ChangeTracker.Entries< IEntity>()。SingleOrDefault(x => x.State!= EntityState.Unchanged&& x.Entity == entity);
if(entry == null)continue;
var trigger = Triggers.Triggers.GetTriggerForEntity(entry.Entity,this);
if(trigger == null)continue;
trigger.BeforeSave(entry.Entity);
switch(entry.State)
{
case EntityState.Added:
trigger.BeforeAdd(entry.Entity);
break;
case EntityState.Modified:
trigger.BeforeUpdate(entry.Entity);
break;
case EntityState.Deleted:
trigger.BeforeDelete(entry.Entity);
break;
}
}
return base.SaveChanges();
}

请注意添加的四个变量更新已删除不变
根据我到目前为止发现的,GetObjectStateEntries应该返回一个ObjectStateEntry的集合,它有一个属性IsRelationship。



我运行以下测试应用程序中的代码:

  using(var db = container.Resolve< IDatabaseContext>())
{
var cus = db.Query< Customer>()。Single(x => x.Id == 1);
var newAddress = db.Query< Address>()。Single(x => x.Id == 5);

cus.Address = newAddress; //还将外键属性Customer.AddressId设置为新的对应值
db.SaveChanges();
}

当我在SaveChanges之后检查代码后,我得到了预期的:
一个导致更新的列表,Customer对象。
但是我没有得到一个ObjectStateEntry的关系(一对一)Customer_Address。



我需要像以前一样检测描述当关系发生变化时。
对于正常的标量属性,您可以执行以下操作:

  var changed = DbEntry.Property(x =>名称).OriginalValue == DbEntry.Property(x => x.Name).CurrentValue; 

但是对于引用属性不明显。
任何想法?

解决方案

您可以使用这个 ExtensionMethod 获取更改的关系列表

  public static class DbContextExtensions 
{
public static IEnumerable< Tuple< ; object,object>> GetRelationships(
this DbContext context)
{
return GetAddedRelationships(context)
.Union(GetDeletedRelationships(context));
}

public static IEnumerable< Tuple< object,object>> GetAddedRelationships(
this DbContext context)
{
return GetRelationships(context,EntityState.Added,(e,i)=> e.CurrentValues [i]);
}

public static IEnumerable< Tuple< object,object>> GetDeletedRelationships(
this DbContext context)
{
return GetRelationships(context,EntityState.Deleted,(e,i)=> e.OriginalValues [i]);
}

private static IEnumerable< Tuple< object,object>> GetRelationships(
这个DbContext上下文,
EntityState relationshipState,
Func< ObjectStateEntry,int,object> getValue)
{
context.ChangeTracker.DetectChanges();
var objectContext =((IObjectContextAdapter)context).ObjectContext;

return objectContext.ObjectStateManager
.GetObjectStateEntries(relationshipState)
.Where(e =&e; e.IsRelationship)
.Select(
e => Tuple.Create(
objectContext.GetObjectByKey((EntityKey)getValue(e,0)),
objectContext.GetObjectByKey((EntityKey)getValue(e,1))));
}
}


in my DbContext subclass I have overridden the SaveChanges() method so I can implement a sort of Trigger-like functionality (before the changes are actually saved). Now, in some of these triggers it is necessary to detect whether certain relationships have changed, regardless of many-to-many, one-to-one/zero etc.

I have read a number of posts on the internet, including some on this site, that mention that the DbContext API doesn't expose any means of getting relationship info. However, ObjectContext should be able to.

My SaveChanges method:

public override int SaveChanges()
{
    IEntity entity;
    ChangeTracker.DetectChanges();

    var stateManager = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager;
    var added = stateManager.GetObjectStateEntries(EntityState.Added).ToList();
    var updated = stateManager.GetObjectStateEntries(EntityState.Modified).ToList();
    var deleted = stateManager.GetObjectStateEntries(EntityState.Deleted).ToList();
    var unchanged = stateManager.GetObjectStateEntries(EntityState.Unchanged).ToList();

    while ((entity = _entitiesRequiringTriggering.FirstOrDefault(x => x.Value).Key) != null)
    {
        _entitiesRequiringTriggering[entity] = false;
        var entry = ChangeTracker.Entries<IEntity>().SingleOrDefault(x => x.State != EntityState.Unchanged && x.Entity == entity);
        if (entry == null) continue;
        var trigger = Triggers.Triggers.GetTriggerForEntity(entry.Entity, this);
        if (trigger == null) continue;
        trigger.BeforeSave(entry.Entity);
        switch (entry.State)
        {
            case EntityState.Added:
                trigger.BeforeAdd(entry.Entity);
                break;
            case EntityState.Modified:
                trigger.BeforeUpdate(entry.Entity);
                break;
            case EntityState.Deleted:
                trigger.BeforeDelete(entry.Entity);
                break;
        }
    }
    return base.SaveChanges();
}

Note the four variables added, updated, deleted and unchanged. According to what I've found so far, the GetObjectStateEntries is supposed to return a collection of ObjectStateEntry, which has a property IsRelationship.

I run the following code in a test application:

using (var db = container.Resolve<IDatabaseContext>())
{
    var cus = db.Query<Customer>().Single(x => x.Id == 1);
    var newAddress = db.Query<Address>().Single(x => x.Id == 5);

    cus.Address = newAddress; //also sets the foreign key property Customer.AddressId to its new corresponding value
    db.SaveChanges();
}

When I inspect the code in SaveChanges after that call, I get what was expected: one result in the updated list, the Customer object. But at no point do I ever get an ObjectStateEntry for the relationship (one-to-one) Customer_Address.

I need to be able to detect as previously described when the relationship has changed. For normal scalar properties you would do this:

var changed = DbEntry.Property(x => x.Name).OriginalValue == DbEntry.Property(x => x.Name).CurrentValue;

But for reference properties that doesn't work obviously. Any ideas?

解决方案

You can use this ExtensionMethod to get a list of relationships that changed

public static class DbContextExtensions
{
    public static IEnumerable<Tuple<object, object>> GetRelationships(
        this DbContext context)
    {
        return GetAddedRelationships(context)
                .Union(GetDeletedRelationships(context));
    }

    public static IEnumerable<Tuple<object, object>> GetAddedRelationships(
        this DbContext context)
    {
        return GetRelationships(context, EntityState.Added, (e, i) => e.CurrentValues[i]);
    }

    public static IEnumerable<Tuple<object, object>> GetDeletedRelationships(
        this DbContext context)
    {
        return GetRelationships(context, EntityState.Deleted, (e, i) => e.OriginalValues[i]);
    }

    private static IEnumerable<Tuple<object, object>> GetRelationships(
        this DbContext context,
        EntityState relationshipState,
        Func<ObjectStateEntry, int, object> getValue)
    {
        context.ChangeTracker.DetectChanges();
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;

        return objectContext.ObjectStateManager
                            .GetObjectStateEntries(relationshipState)
                            .Where(e => e.IsRelationship)
                            .Select(
                                    e => Tuple.Create(
                                            objectContext.GetObjectByKey((EntityKey)getValue(e, 0)),
                                            objectContext.GetObjectByKey((EntityKey)getValue(e, 1))));
    }
}

这篇关于实体框架6:检测关系变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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