这里是一个简单的实体:
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);