C# 实体框架-仅更新不为null的值
这对我来说有点新。我被要求编写一个ETL程序,将两个数据集加载到同一个表中。数据集1已完成并包含表的所有数据。然而,Dataset2只包含需要覆盖到第一个数据集上的更改。注意: //数据集1:Widgets表C# 实体框架-仅更新不为null的值,c#,linq,entity-framework,C#,Linq,Entity Framework,这对我来说有点新。我被要求编写一个ETL程序,将两个数据集加载到同一个表中。数据集1已完成并包含表的所有数据。然而,Dataset2只包含需要覆盖到第一个数据集上的更改。注意: //数据集1:Widgets表 +----+------+------+------+------+ | ID | COL1 | COL2 | COL3 | COL4 | +----+------+------+------+------+ | 1 | abcd | abcd | abcd | abcd | +----
+----+------+------+------+------+
| ID | COL1 | COL2 | COL3 | COL4 |
+----+------+------+------+------+
| 1 | abcd | abcd | abcd | abcd |
+----+------+------+------+------+
| 2 | abcd | abcd | abcd | abcd |
+----+------+------+------+------+
//数据集2:Widgets\u更改表
+----+------+------+------+------+
| 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多个字段,有更多的文件传入,这些文件遵循相同的方法。完整的数据集伴随着diff数据集,但具有完全不同的模式。显然,我更希望有一些可移植的东西,我可以直接运行文件,而不必对每个数据集逐个属性进行硬编码比较
是否有一种方法可以遍历对象的属性并更新非空值?首先,您需要使用类似的方法来选择要更新的实体:
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();
}
}
您可能希望为此使用反射。循环遍历每个小部件/差异的所有属性/字段,获取该属性/字段的值,如果差异为null,则使用原始值
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).
}
@p、 s.w.g的答案很好,但是当我尝试实现它时,我遇到了几个错误,例如,你不能用obj检查null。Equalsnull,null没有Equals方法 这是@p.s.w.g伟大答案的一个完整的可复制的解决方案,作为副产品 静态泛型方法InjectNonNull获取要更新的源实体和带有null的目标sparce实体,并仅传输目标实体上的非null属性
private static class PropertyLister<T1, T2>
{
public static readonly IEnumerable<Tuple<PropertyInfo, PropertyInfo>> PropertyMap;
static PropertyLister()
{
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();
}
}
public static T InjectNonNull<T>(T dest, T src)
{
foreach (var propertyPair in PropertyLister<T, T>.PropertyMap)
{
var fromValue = propertyPair.Item2.GetValue(src, null);
if (fromValue != null && propertyPair.Item1.CanWrite)
{
propertyPair.Item1.SetValue(dest, fromValue, null);
}
}
return dest;
}
您可以使用反射来实现这一点,使用PropertyInfo来获取属性My Goods。这比我所希望的要好得多。谢谢大家!@ajax81乐于帮助,乐于编码:这正是我想要的。非常感谢。注意,如果widget和widget_diff属于不同的类型,那么这将不起作用。您需要为每个对象使用适当的PropertyInfo。+1是的,我不久前确实看到了这个问题。toValue.Equalsnull应为toValue!=null或Object.EqualstoValue,null。我已经更正了我的答案的完整性,但是我添加propertyPair.Item1.CanWrite也是一个好主意。