2014-04-02 94 views
1

我有嵌套集合一個實體如下更新斷開連接的對象圖

public class Profile 
{ 
    [Required] 
    public String Id { get; set; } 
    public String Name { get; set; } 
    public virtual ICollection<Plugin> Plugins { get; set; } 
} 

public class Plugin 
{ 
    [Required] 
    public int Id { get; set; } 
    public String Name { get; set; } 
    public virtual ICollection<Counter> Counters{ get; set; } 
} 

public class Counter 
{ 
    [Required] 
    public int Id { get; set; } 
    public int SomeVal { get; set; } 
} 

我想開發一個程序(即給出一個配置文件列表時)插入/更新/刪除整個嵌套的對象圖

即基本上我想要做刪除更新和插入,情景模式,插件和櫃檯,適用於我可以

由於型材的新斷開的列表(NewProfiles)和一個clientId最簡單,最有效的方式

using (var dc = new JCEContext()) 
{ 

    var existingProfiles = dc.Profiles.AsNoTracking().Where(x => Id == ClientId).ToList(); 
    var addedProfiles = NewProfiles.Except(existingProfiles, x => x.Id).ToList(); 
    var deletedTeachers = existingProfiles.Except(Profiles, x => x.Id); 
    var modifiedProfiles = dcmProfiles.Except(addedProfiles, x => x.Id); 

    addedProfiles.ToList().ForEach(tchr => dc.Entry(tchr).State = EntityState.Added); 
    deletedTeachers.ToList().ForEach(tchr => dc.Entry(tchr).State = EntityState.Deleted); 

    foreach (var profile in modifiedProfiles) 
    { 

     var entity = dc.Profiles.Find(profile.Id); 

     if (entity == null) 
      continue; 

     var entry = dc.Entry(entity); 

     entry.CurrentValues.SetValues(profile); 

    } 
    dc.SaveChanges(); 
} 

助手功能

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other,Func<T, TKey> getKey) 
{ 
    return from item in items 
      join otherItem in other on getKey(item) 
      equals getKey(otherItem) into tempItems 
      from temp in tempItems.DefaultIfEmpty() 
      where ReferenceEquals(null, temp) || temp.Equals(default(T)) 
      select item; 
} 

上述程序將只更新頂級對象

我將如何去運用我這裏有邏輯,對象圖的休息嗎?即插入更新和刪除插件以及相關的計數器?

注意:多數這段代碼的某處發現我已經適應

+0

GraphDiff是這個工具。 –

+0

我已經看過,但是我無法完全弄清楚如何讓它工作,沒有很多從地面零開始的人的文檔,是否有可能發佈一個示例代碼的答案那是我需要的球的一部分? –

+0

我在StackOverflow中看到過示例代碼,請在Google中嘗試「site:stackoverflow.com GraphDiff」。 –

回答

1

我決定用GraphDiff解決問題,下面是完整的解決方案。我願意發佈一個更優雅的解決方案。

public Boolean SubmitProfiles(String dcmClientId, List<DcmProfile> dcmProfiles) 
{ 

    try 
    { 

     using (var dc = new JCEContext()) 
     { 
      var existing = dc.DcmProfiles.AsNoTracking().Where(x => x.DcmClientId == dcmClientId).ToList(); 
      var added = dcmProfiles.Except(existing, x => x.Id).ToList(); 
      var deleted = existing.Except(dcmProfiles, x => x.Id).ToList(); 
      var modified = dcmProfiles.Except(added, x => x.Id); 

      // Update modified profiles 
      foreach (var dcmProfile in modified) 
       dc.UpdateGraph(dcmProfile, map => map 
        .OwnedCollection(profile => profile.Plugins, with => with 
         .OwnedCollection(plugin => plugin.Counters))); 

      // Add new profiles 
      added.ForEach(profile => dc.Entry(profile).State = EntityState.Added); 

      // Delete nonexistent profiles 
      deleted.ForEach(profile => dc.Entry(profile).State = EntityState.Deleted); 

      dc.SaveChanges(); 

     } 

     return true; 

    } 
    catch (Exception ex) 
    { 
     Log.ErrorException(ex.Message, ex); 
     return false; 
    } 

} 

的下面是一個擴展方法,該方法比較來自兩個列表的值以及從一個其中不存在於其他返回實體。

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKey) 
{ 
    return from item in items 
      join otherItem in other on getKey(item) 
      equals getKey(otherItem) into tempItems 
      from temp in tempItems.DefaultIfEmpty() 
      where ReferenceEquals(null, temp) || temp.Equals(default(T)) 
      select item; 

} 
+1

小心不要從互聯網撿起壞習慣。 'try {} catch(Exception ex){Log(ex); return false;}無論發生什麼可怕的事情,無論程序的狀態如何,''都會繼續執行你的程序。它也只記錄堆棧跟蹤_up到'catch'塊 - 你不會知道誰是所謂的方法。 –

+0

感謝您的迴應,在這種情況下,它是一個web服務調用,只需要返回一個結果,而沒有任何異常。然而,我一直在琢磨應用程序體系結構中任何給定級別的最佳異常處理模式。我認爲我真的需要對此進行更多的研究 –

+0

請參閱[A:處理異常,這是一個好方法嗎?](http://stackoverflow.com/questions/2469822/handling-exceptions-is-this-a-good三通/ 2469864#2469864)。 –

1

從互聯網上一些更多的在互聯網上的例子:Reattaching Entity Graphs with the Entity FrameworkDbContext Merge for detached Entities(德國)。

這兩個鏈接背後的基本思想是使用表達式來定義要包含到更新中的路徑。

如果你不想定義這些路徑,那麼你我會建議爲你的pocos使用一個公共的基類或接口。 然後使用像這樣(未經):這裏

public static void MergeObject<T>(this DataContext dc, T modifiedItem) 
    { 
     MergeObjectGraph<T>(dc, new List<T>() { modifiedItem }); 
    } 

    public static void MergeObjectGraph<T>(this DataContext dc, IEnumerable<T> modifiedCollection) 
    { 
     var existingItems = dc.Set<T>.AsNoTracking().ToList(); 
     var addedItems = modifiedCollection.Except(existingItems, x => x.Id).ToList(); 
     var deletedItems = existingItems.Except(modifiedCollection, x => x.Id); 

     addedItems.ToList().ForEach(tchr => dc.Entry(tchr).State = EntityState.Added); 
     deletedItems.ToList().ForEach(tchr => dc.Entry(tchr).State = EntityState.Deleted); 

     foreach (var item in modifiedCollection) 
     { 
      var navigationProperties = profile.GetType().GetProperties().Where(p => p.PropertyType.Equals(YourBaseType)).ToList(); 
      var nestedCollections = profile.GetType().GetProperties().Where(p => p.IsGenereicType && p.GetGenericTypeDefinition() == typeof(ICollection<>)); 

      foreach (var navProp in navigationProperties) 
      { 
       var p = navProp.GetValue(item,null); 
       dc.MergeObject(navProp); //need to call this by reflection 
      } 

      foreach (var nested in nestedCollections) 
      { 
       var coll = nested.GetValue(item,null); 
       dc.MergeObjectGraph(coll); //need to call this by reflection 
      } 
      var entity = dc.Set<T>.Find(item.Id); 

      if (entity == null) 
       continue; 

      var entry = dc.Entry(entity); 

      entry.CurrentValues.SetValues(item); 
     } 
    } 

問題:深度嵌套的圖形性能,你不想去觸摸和循環引用可能接觸的物品。

+0

很好的回答,我也喜歡你對代碼所做的事情,明天生病通過文章,並相應地標記,謝謝 –

相關問題