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

C# 仅更新EF实体上未设置为null的某些属性,c#,json,entity-framework,dto,C#,Json,Entity Framework,Dto,我有一个浏览器发送JSON,但它只包含已更改的给定模型的属性。因此,一旦WCF DataContractJsonSerializer完成了它的工作,我就有了一个对象,它可能只填充了ID和Description字段 将其按原样附加到DbContext将导致“描述”字段被更新,但所有其他字段在数据库中都被设置为其类型的默认值。这是因为如果WCF没有看到JSON中指定的属性,那么它将跳过它,这意味着实例中的属性将根据自动实现的属性使用类型默认值 因此,这意味着我需要决定哪些字段在没有访问JSON本身的

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

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

因此,这意味着我需要决定哪些字段在没有访问JSON本身的情况下被传递。事实上,它可以是网络上的XML,所以我只能从这个部分序列化的对象开始工作

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

在Update方法中,我使用这个序列化对象作为存根。我必须遍历每个属性,如果该值未设置为null,则将其状态设置为modified。据我所知,这是工作没有任何副作用,但我只是不确定这是这样做的方式

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

下面是我目前必须处理更新的代码。我将非常感谢您给我的建议,这是一种更好、也许更明显的方法

总结:如何判断模型实例上的哪些属性是由DataContractSerializer/DataContractJsonSerializer设置的,哪些只是使用其构造函数中的默认值。使用特殊值是有问题的,因为客户端可能希望将某些内容设置为空字符串,或设置为0或-1,甚至设置为null

更新:使用下面的Hammerstein的答案来创建自跟踪模型,我更新了我的更新功能,如下所示。不幸的是,由于我在模型上使用了Required属性来进行预保存验证,当使用存根实例时,EF抛出了一个抖动,该存根实例包含未修改值的null。你可能会认为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;
}

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

由于模型绑定只会设置已回传的字段,因此您应该获得已更改字段的准确列表

然后我在列表中循环,并设置实体上的值

不干净,但它对我有用


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

这是一个好问题,好吧,除了一次调用单个属性的更新之外,没有什么好方法可以做到这一点(即,当您将其从json中拉出时,更新实体上的属性,然后在最后提交)+制作您自己的自跟踪模型的有趣想法。我想可能您没有将其序列化。我使用的是DataContractJsonSerializer/DataContractSerializer,它只能真正设置属性。因此我想我接下来的选择是使用调用“NotifyChanged”方法的自定义序列化程序,或者更好的方法选项是不在模型中使用自动实现的属性,并且有一个setter,除了设置其成员之外,还可以添加到NotifyChanged。您认为呢?这个解决方案有一些不太理想的地方,比如失去自动实现的属性,但这只是我想要懒惰和更干净的代码。这个解决方案是d谢谢你的发帖!
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;
}