更改EntityFramework中的实体

时间:2009-04-29 15:52:29

标签: c# .net wpf entity-framework

我有以下情况:

  1. 实体从数据库加载。
  2. 其中一个以表格(WPF UserControl)呈现给用户,用户可以在其中编辑该实体的属性。
  3. 用户可以决定将更改应用于实体或取消编辑。
  4. 我如何使用EntityFramework实现类似的功能?

    我的问题是,当我将UI直接绑定到实体的属性时,每个更改都会立即应用于实体。我想将此延迟到用户按下OK并且已成功验证的那一刻。

    我考虑过使用NoTracking加载实体并在验证分离的实体后调用ApplyPropertyChanges,但我不完全确定正确的方法。 MSDN上的EntityFramework的文档非常稀疏。

    我能想到的另一种方法是使用RefreshStoreWins实体,但我不喜欢在取消时重置更改,而不是在Ok时应用更改。

    有没有人有一个好的教程或样本?

3 个答案:

答案 0 :(得分:3)

您所说的一个选项是执行无跟踪查询。

ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First(c => c.ID == 232);

然后,客户可以根据需要在内存中修改'customer',并且在上下文中实际上没有发生任何事情。

现在,当您想要实际进行更改时,您可以执行此操作:

// get the value from the database
var original = ctx.Customers.First(c => c.ID == customer.ID);
// copy values from the changed entity onto the original.
ctx.ApplyPropertyChanges(customer); .
ctx.SaveChanges();

现在,如果您因性能或并发原因而对查询感到不舒服,可以在ObjectContext中添加一个新的扩展方法AttachAsModified(...)。

看起来像这样:

public static void AttachAsModified<T>(
    this ObjectContext ctx, 
    string entitySet, 
    T entity)
{
    ctx.AttachTo(entitySet, entity);

    ObjectStateEntry entry = 
            ctx.ObjectStateManager.GetObjectStateEntry(entity);

    // get all the property names
    var propertyNames = 
            from s in entry.CurrentValues.DataRecordInfo.FieldMetadata
            select s.FieldType.Name;

    // mark every property as modified    
    foreach(var propertyName in propertyNames)
    {
        entry.SetModifiedProperty(propertyName);
    }
}

现在您可以编写如下代码:

ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First();
// make changes to the customer in the form
ctx.AttachAsModified("Customers", customer);
ctx.SaveChanges();

现在你没有并发或冗长的查询。

现在唯一的问题是处理FK属性。您可以在此查看我的帮助提示索引:http://blogs.msdn.com/alexj/archive/2009/03/26/index-of-tips.aspx

希望这有帮助

亚历

答案 1 :(得分:1)

执行此操作的常规方法是绑定到实现IEditableObject的内容。如果和如何适应实体框架,我不确定。

答案 2 :(得分:1)

我也建议IEditableObject,另外还有IDataErrorInfo。

我这样做的方式是,我基本上有一个实体的视图模型,它将实体作为构造函数参数(基本上是一个包装器对象)。

在BeginEdit中,我将实体属性复制到我的viewmodel,因此,如果我执行CancelEdit,则仅在ViewModel中更改数据,并且原始实体未更改。在EndEdit中,我只是再次将ViewModel属性应用于实体,或者仅在验证成功时才应用。

对于验证,我使用IDataErrorInfo的方法。我只是实现IDataErrorInfo.Error,以便它通过IDataErrorInfo [string columnName]检查每个属性名称并连接最终的错误消息。如果它是空的,一切都很好。 (不确定错误是否意味着以这种方式使用,但我这样做)

如果我有其他实体附加到我的原始实体,例如Customer.Orders,我将它们创建为原始实体的ViewModel中的嵌套ViewModel。原始的ViewModel在它自己的那些方法的实现中调用它的subModels的Begin-,Cancel-,EndEdit / Error方法。

这是一个更多的工作,但我认为这是值得的,因为在BeginEdit和EndEdit之间,你可以非常肯定没有你注意到它没有任何改变。拥有支持INotifyPropertyChanged的属性的代码片段也有很大帮助。