2014-04-02 68 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

很好的回答,我也喜欢你对代码所做的事情,明天生病通过文章,并相应地标记,谢谢 –

相关问题