首页 文章

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

提问于
浏览
4

这是一个简单的实体:

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 属性上使用虚拟关键字,当我从数据库查询客户实体时,我没有使用 Include 方法加载Location属性,我也没有使用任何访问懒惰加载它 . 因此EF无法跟踪并知道应该删除 China Location实体 .

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

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


Solutions update

我发现有两种方法可以轻松完成

首先,您可以使用Identifying relationrecommend ) .

您可以使用 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);

2 回答

  • 1

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

    您现在获得两个位置的原因是因为您使用 new Location("American"); 实际上添加了对新位置的引用(EF不知道 China 是否被其他客户使用,并且在该类型的查询中永远不会删除它)

    现在,如果你说 .

    customer.Location.Country = "America"
    

    China 将被 America 覆盖,因为我们正在使用特定的 Location 属性 .

    Read the coments on the question so a little extras

    如果要完全更新位置( 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();
    }
    
  • 4

    更新实体的另一种方法是使用 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();
    

相关问题