c# - Entity Framework - 只更新不为空的值

标签 c# linq entity-framework

这对我来说有点新鲜。我被要求编写一个 ETL 程序,将两个数据集加载到同一个表中。数据集 #1 是完整的,包含该表的所有数据。然而,数据集 #2 仅包含需要叠加到第一个数据集上的更改。观察:

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

+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1  | abcd | abcd | abcd | abcd |
+----+------+------+------+------+
| 2  | abcd | abcd | abcd | abcd |
+----+------+------+------+------+

//数据集 #2:Widgets_Changes 表

+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1  |      | efgh |      | ijkl |
+----+------+------+------+------+
| 2  | mnop |      | qrst |      |
+----+------+------+------+------+

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

+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1  | abcd | efgj | abcd | ijkl |
+----+------+------+------+------+
| 2  | mnop | abcd | qrst | abcd |
+----+------+------+------+------+

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

// 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();
}

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

有没有一种方法可以遍历两个对象的属性并更新不为空的值?

最佳答案

首先,您需要使用类似这样的方法来选择要更新的实体:

var widget      = db.Widgets.First(p => p.ID == 1);
var widget_diff = db.Widgets_Changes.First(p => p.ID == 1);

现在您可以简单地使用反射来更新所有字段:

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

这可以通过预先构建属性列表来加快一点,因此您只需使用反射一次:

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);
    }
}

如果您有很多这样的实体类型,您甚至可能想考虑将其变成一个通用实用程序:

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();
    }
}

关于c# - Entity Framework - 只更新不为空的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17385472/

相关文章:

c# - 查找鼠标指针所在的监视器/屏幕

.net - 存储过程和 LINQ、Dbml 文件无法解释结果集

IEnumerable<T> 的 foreach 的 LINQ 等效项

c# - 包括过滤器子集合

entity-framework - F# 中的 Entity Framework 和匿名类型

c# - 在 WindowsFormsHost 里面用鼠标移动 WPF Window

c# - 检测按钮上的 Enter 键按下 c#

c# - 在我自己的类上使用集合初始值设定项

c# - 传输 LINQ 数据对象

c# - 在 Entity Framework 中使用存储过程的乐观并发