仅更新未设置为null的EF实体上的某些属性

时间:2012-01-25 09:57:22

标签: c# json entity-framework dto

我有一个浏览器发送JSON,但它只包含已更改的给定模型的属性。因此,一旦WCF DataContractJsonSerializer完成它的工作,我就有了一个对象,它可能只填充了ID和Description字段。

将其附加到DbContext将导致更新描述字段,但所有其他字段在数据库中设置为其类型的默认值。这是因为如果WCF没有看到JSON中指定的属性,那么它将跳过它,这意味着实例中的属性将根据自动实现的属性使用类型默认值。

所以这意味着我需要决定哪些字段已经传递而没有访问JSON本身。事实上,它可能是线上的XML,所以我可以使用的是这个部分序列化的对象。

最合乎逻辑的是使用 null 作为特殊值,这意味着此属性尚未被序列化。所以在POCO模型的构造函数中,我将所有属性设置为 null

在Update方法中,我将此序列化对象用作存根。我必须遍历每个属性,如果值未设置为 null ,那么我将其状态设置为已修改。据我所知,这是没有任何副作用,但我不确定这是做这样的事情的方式。

它添加的一个限制是客户端不能再故意将属性设置为null,因为该更新将丢失。解决此问题的一种方法是使用一个特殊的int值,该值可以设置为在数据库中表示null,也可以是一个空字符串,表示数据库中的null,并在更新中包含代码以查找这些特殊值,然后设置实体属性为空。远非理想,可能容易出错。

以下是我目前处理更新的代码。我会 非常 欣赏有关更好,也许更明显的做法的建议。

To Summerise:如何判断模型实例上的哪些属性已由DataContractSerializer / DataContractJsonSerializer设置,哪些属性仅使用其构造函数的默认值。使用特殊值是有问题的,因为客户端可能想要将某些内容设置为空字符串,或者设置为0或-1或实际上为null。

public T Update(T obj)
{
    var entity = ds.Attach(obj);

    // For each property in the model
    foreach (var p in typeof(T).GetProperties())
    {
        // Get the value of the property
        var v = p.GetValue(obj, null);

        // Assume null means that the property wasn't passed from the client
        if (v == null)
            continue;

        // Set this property on the entity to modified unless it's ID which won't change
        if (p.Name != "ID")
            dc.Entry(entity).Property(p.Name).IsModified = true;
    }

    dc.SaveChanges();
    return entity;
}

更新:使用Hammerstein的答案来获得自我跟踪的模型,我已经更新了我的更新功能,如下所示。不幸的是,由于我在模型上使用了必需属性进行预保存验证,因此当使用包含非修改值的空值的存根实例时,EF会引发抖动。你会认为EF会认识到某些字段被设置为不被修改,但唉它不会这样,我不得不进行读取,然后更新它。实际上,这可能是一个很好的候选人,可以发布一个单独的问题,试图避免阅读。

public virtual T Update(T obj)
{
    var entity = ds.Find(obj.ID);

    ((TrackedModel)obj).Modified.ForEach(p => { 
        var prop = dc.Entry(entity).Property(p.PropertyName);
        prop.CurrentValue = p.NewValue;
        prop.IsModified = true;
    });

    dc.SaveChanges();
    return entity;
}

1 个答案:

答案 0 :(得分:2)

我对此问题的解决方案是跟踪的更改模型,我创建了一个包含字符串列表的抽象基类,然后对于我的模型上的每个属性,我调用了一个方法NotifyChanged(“MyProperty”),它将属性添加到列表。

因为模型绑定只会设置已回发的字段,所以您应该获得更改的字段的准确列表。

然后我遍历列表,并在我的实体上设置值。

不干净,但它符合我的目的。

更新:我的解决方案确实要求我远离自动属性并手写它们。在setter中,设置值后,我调用NotifyChanged。我正在使用MVC常规模型绑定,我不认为我有一个将对象作为JSON和反序列化传递的工作示例。您可以查看JSON.NET,控制序列化/反序列化我相信您可以告诉它忽略默认属性值等。