【问题标题】:Changing Entities in the EntityFramework更改 EntityFramework 中的实体
【发布时间】:2009-04-29 15:52:28
【问题描述】:

我有以下场景:

  1. 从数据库中加载实体。
  2. 其中一个以表单(WPF 用户控件)的形式呈现给用户,用户可以在其中编辑该实体的属性。
  3. 用户可以决定将更改应用到实体或取消编辑。

我将如何使用 EntityFramework 实现类似的功能?

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

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

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

谁有好的教程或示例?

【问题讨论】:

    标签: c# .net wpf entity-framework


    【解决方案1】:

    一个选项是你所说的做一个不跟踪查询。

    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

    希望对你有帮助

    亚历克斯

    【讨论】:

    • 我目前使用 ApplyPropertyChanges 方法实现了它,但附加修改后的实体看起来也很有趣。谢谢输入!
    【解决方案2】:

    我也建议使用 IEditableObject,另外还建议使用 IDataErrorInfo。

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

    在 BeginEdit 中,我将实体属性复制到我的视图模型中,因此如果我执行 CancelEdit,则数据仅在 ViewModel 中更改,而原始实体没有更改。在 EndEdit 中,我只是再次将 ViewModel 属性应用于实体,或者当然只有在验证成功时。

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

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

    这需要更多的工作,但我认为这是值得的,因为在 BeginEdit 和 EndEdit 之间,您可以确定没有任何变化而您不会注意到它。为启用 INotifyPropertyChanged 的​​属性编写代码 sn-p 也有很大帮助。

    【讨论】:

      【解决方案3】:

      执行此操作的正常方法是绑定到实现IEditableObject 的东西。我不确定它是否以及如何适合实体框架。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多