实体框架直接更新虚拟财产而不创建新记录

这是一个简单的实体:

public class Customer : Entity
{
    public virtual Location Location { get; set; }
}

现在假设我们已经有客户:

var customer = new Customer() {Location = new Location("China")};

现在我们想更新他的位置:

var customer = context.Customers.First(x => x.Location.Country == "China");
customer.Location = new Location("America");
context.SaveChanges();

现在当我查看数据库时,位置记录“中国”还没有被删除:数据库现在有两个位置记录与一个客户记录关联。

这个问题的原因是我在Customer.Location属性上使用了virtual关键字,并且当我从数据库查询客户实体时,我没有使用Include方法来加载Location属性,并且我也没有使用任何访问懒惰加载它。 因此EF无法跟踪和了解应该删除的China Location实体。

我认为我用于更新虚拟财产的方法符合直觉。 我想更新一个属性,然后只是使用更新指令“entity.xxx = ...”,加载被迫使用某些访问属性或方法调用的同时加载“entity.xxx”并不直观。

所以我正在寻找一些更好的方法来直接替换实体的虚拟财产。 有什么建议么?


解决方案更新

我找到了两种方法来做到这一点,

首先你可以使用识别关系( 推荐 )。

另一种方法是使用ObjectContext.DeleteObject方法,下面是示例代码:

public static class EFCollectionHelper
{
    public static void UpdateCollection<T, TValue>(this T target, 
Expression<Func<T, IEnumerable<TValue>>> memberLamda, TValue value)where T : Entity
    {
        var memberSelectorExpression = (MemberExpression)memberLamda.Body;
        var property = (PropertyInfo)memberSelectorExpression.Member;

        var oldCollection = memberLamda.Compile()(target);
        oldCollection.ClearUp();

        property.SetValue(target, value, null);
    }

    public static void ClearUp<T>(this IEnumerable<T> collection)
    {
        //Convert your DbContext to IObjectContextAdapter
        var objContext = ((IObjectContextAdapter) Program.DbContext).ObjectContext;
        for (int i = 0; i < collection.Count(); i++)
        {
            objContext.DeleteObject(collection.ElementAt(i));
        }
    }
}

然后你可以简单地编写代码:

customer.UpdateCollection(x => x.Locations, null);

不完全确定你想要什么,但这是我得到的。

你现在得到两个地点的原因是你使用new Location("American"); 您实际上添加了对新位置的引用(EF不知道中国是否被另一位客户使用,并且在该类型的查询中不会删除它)

现在如果你说。

customer.Location.Country = "America"

中国将被美国覆盖,因为我们现在正在处理特定Location的财产。

阅读关于这个问题的评论,所以有点额外

如果您想完全更新位置( new Location("Some new location") )。 那么你会这样做。

Location oldLocation = customer.Location;
Location newLocation = new Location("America");
//Check if the new location country !exist
if(!context.Locations.Any(a=> a.Country == newLocation.Country))
{
    //If it don't exist then add it (avoiding the location duplicate)
    customer.Location = newLocation;
    //If its important to delete the old location then do this
    //(important to do after you removed the dependency, 
    //thats why its after the new location is added)
    context.Locations.Remove(oldLocation)
    //Finally Save the changes
    context.SaveChanges();
}

另一种更新实体的方法是使用Entry.OriginalValues.SetValues方法:

var currentLocation = context.Customers.First(x => x.Location.Country == "China").Select(c => c.Location);
context.Entry(currentLocation).OriginalValues.SetValues(newLocation) ;
context.SaveChanges();
链接地址: http://www.djcxy.com/p/33585.html

上一篇: Entity Framework update virtual property directly without creating new record

下一篇: Changes to DbContext not saved