实体框架 - 仅更新非空值

Dan*_*abo 7 c# linq entity-framework

这对我来说有点新意.我被要求编写一个ETL程序,将两个数据集加载到同一个表中.数据集#1已完成,包含表的所有数据.但是,数据集#2仅包含需要覆盖到第一个数据集的更改.注意:

//数据集#1:小部件表

+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1  | abcd | abcd | abcd | abcd |
+----+------+------+------+------+
| 2  | abcd | abcd | abcd | abcd |
+----+------+------+------+------+
Run Code Online (Sandbox Code Playgroud)

//数据集#2:Widgets_Changes表

+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1  |      | efgh |      | ijkl |
+----+------+------+------+------+
| 2  | mnop |      | qrst |      |
+----+------+------+------+------+
Run Code Online (Sandbox Code Playgroud)

//预期结果:包含所有更改的小部件

+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1  | abcd | efgj | abcd | ijkl |
+----+------+------+------+------+
| 2  | mnop | abcd | qrst | abcd |
+----+------+------+------+------+
Run Code Online (Sandbox Code Playgroud)

显而易见的方法(我试图避免)是将每个小部件拉出第一个表并进行逐个属性比较:

// Simplified example:
using ( var db = new MyEntityDatabase() ){

   var widget      = from p in db.Widgets select p where p.ID == 1;
   var widget_diff = from p in db.Widgets_Changes select p where p.ID == 1

   widget.COL1 = widget_diff.COL1 ?? widget.COL1;
   widget.COL2 = widget_diff.COL2 ?? widget.COL2;
   widget.COL3 = widget_diff.COL3 ?? widget.COL3;
   // ...etc

   db.saveChanges();
}
Run Code Online (Sandbox Code Playgroud)

但是,此特定数据集中有超过200个字段,更多文件传入遵循相同的方法(完整数据集伴随diff数据集)但完全具有不同的模式.显然,我宁愿有一些便携式的东西,我可以只运行文件,而不必为每个数据集硬编码逐个属性的比较.

有没有办法可以遍历两个对象的属性并更新非空值?

p.s*_*w.g 10

首先,您需要使用类似的东西来选择要更新的实体:

var widget      = db.Widgets.First(p => p.ID == 1);
var widget_diff = db.Widgets_Changes.First(p => p.ID == 1);
Run Code Online (Sandbox Code Playgroud)

现在您只需使用反射来更新所有字段:

foreach(var fromProp in typepf(Widget).GetProperties())
{
    var toProp = typeof(Widget_Change).GetProperty(fromProp.Name);
    var toValue = toProp.GetValue(widget_diff, null);
    if (toValue != null)
    {
        fromProp.SetValue(widget, toValue, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过预先构建属性列表可以加快一点,所以你只需要使用一次反射:

public static class WidgetUtil
{
    public static readonly IEnumerable<Tuple<PropertyInfo, PropertyInfo>> PropertyMap;

    static Util()
    {
        var b = BindingFlags.Public | BindingFlags.Instance;
        PropertyMap = 
            (from f in typeof(Widget).GetProperties(b)
             join t in typeof(WidgetChange).GetProperties(b) on f.Name equals t.Name
             select Tuple.Create(f, t))
            .ToArray();
    }
}

...

foreach(var propertyPair in WidgetUtil.PropertyMap)
{
    var toValue = propertyPair.Item2.GetValue(widget_diff, null);
    if (toValue != null)
    {
        propertyPair.Item1.SetValue(widget, toValue, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您有许多这样的实体类型,您甚至可以考虑将其变为通用实用程序:

public static class WidgetUtil<T1, T2>
{
    public static readonly IEnumerable<Tuple<PropertyInfo, PropertyInfo>> PropertyMap;

    static WidgetUtil()
    {
        var b = BindingFlags.Public | BindingFlags.Instance;
        PropertyMap = 
            (from f in typeof(T1).GetProperties(b)
             join t in typeof(T2).GetProperties(b) on f.Name equals t.Name
             select Tuple.Create(f, t))
            .ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ati*_*ita 5

您可能希望使用反射.循环遍历每个窗口小部件/差异的所有属性/字段,获取该属性/字段的值,如果差异为空,则使用原始值.

using(var db = new MyEntityDatabase())
{
    var widget      = from p in db.Widgets select p where p.ID == 1;
    var widget_diff = from p in db.Widgets_Changes select p where p.ID == 1;

    var properties = typeof(MyWidgetType).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach(var property in properties)
    {
         //widget.column = widget_diff.column ?? widget.colum;
         property.SetValue(property.GetValue(widget_diff) ?? property.GetValue(widget), widget);
    }

    //You can do the same for fields here if the entity has any fields (probably not).
}
Run Code Online (Sandbox Code Playgroud)