2013-06-29 23 views
6

Bu, benim için yeni bir bittir. Aynı tabloya iki veri kümesi yükleyen bir ETL programı yazmam istendi. Veri kümesi # 1 tamamlandı ve tablo için tüm verileri içerir. Ancak, Veri Kümesi # 2, yalnızca ilk veri kümesine eklenmesi gereken değişiklikleri içerir. Gözlemleyin:Entity Framework - Yalnızca sıfır olmayan değerleri güncelleştirin

// Veri kümesi # 1: Widget'lar Tablo

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

// Veri kümesi # 2: Widgets_Changes Tablo

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

// Beklenen Sonuç: Tüm Değişiklikler ile Widget'lar

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

Belirgin yaklaşım (kaçınmaya çalıştığım) her bir aracı ilk tablodan çıkarmak ve bir özellik-b y-mülk karşılaştırması:

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

Ancak, bu aynı metodoloji (fark veri kümesi eşliğinde tam veri kümesi) uymaları fakat tamamen farklı bir şema var, gelen fazla dosyalarla bu özel veri kümesindeki 200'den fazla alanlar vardır. Açıkçası, her veri kümesi için özellik-özellik karşılaştırmaları için sabit kodlar yapmak yerine dosyaları çalıştırabileceğim taşınabilir bir şey olmasını tercih ederim.

Her iki nesnenin özellikleriyle ve null olmayan güncelleme değerleriyle yineleyebileceğim bir yol var mı?

+0

Bunu yapmak için yansımayı kullanabilirsiniz, özellikleri almak için PropertyInfo özelliğini kullanın –

cevap

9

Öncelikle, güncelleştirmek istediğiniz varlıkları seçmek için böyle bir şey kullanmak gerekir:

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

sadece tüm alanları güncellemek için yansıma kullanabilirsiniz:

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

Bu can Ön taraftaki özelliklerin bir listesini oluşturarak bir miktar oluşturulsun, böylece yalnızca bir kez yansımayı kullanmalısınız:

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); 
    } 
} 
Eğer bu tür birçok varlık türleri varsa

, hatta genel bir yardımcı program içine bu yapım düşünebilirsiniz:

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

My goodness. Bu umduğum her şeyden çok daha iyi. Teşekkür ederim! –

+0

@ ajax81 Yardımlara sevindim, Mutlu kodlama :) –

+0

Günümü sen yaptın! +1 –

4

Bunun için yansıma kullanmak isteyebilirsiniz. Her pencere öğesi/fark için tüm özellikler/alanlar arasında geçiş yapın, farkın boş olması durumunda o özellik/alanın değerini alın, ardından orijinal değeri kullanın.

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). 
} 
+0

Tam olarak aradığım şey. Teşekkür ederim! –

+0

Eğer 'widget' ve 'widget_diff' farklı türde ise, bunun işe yaramayacağını unutmayın. Her nesne için uygun 'PropertyInfo' kullanmanız gerekecektir. –

2

pswg cevabı büyük @ denedim ancak zaman böyle sen obj.Equals (boş) ile null kontrol edemez örneğin gibi çeşitli böcek (karşılaşılan bunu uygulamak, boş değil Eşittir yöntemine sahip olmak. İşte

güncelleştirmek istediğiniz kaynak varlığı ve hedef alır InjectNonNull Statik jenerik metodu "sparce (bir yan ürün olarak) @ pswg harika cevabın bir "tam kopya-pastable çözüm" olduğunu "nulls ile varlık ve yalnızca hedef varlık üzerindeki sıfır olmayan özellikleri aktarır.

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; 
    } 
+0

+1 Evet, aslında bu sorunu biraz önce gördüm. toValue.Equals (null) '' toValue! = null' veya 'Object.Equals (toValue, null)' olmalıdır. Tamlığım için cevabımı düzelttim, ama 'propertyPair.Item1.CanWrite' eklemek de iyi bir fikir. –