2012-11-23 44 views
4

我在我的EntityFramework支持的存儲庫中有一個重新啓動的代碼塊,我想以某種方式通用化並作爲方法調用,因此重複使用代碼而不是重複它。重構代碼到一個通用的方法

當前碼塊看起來像這樣:

 // Archive deleted MyItems sections 
     _t.MyItems.Where(x => x.ValidTo == null && !team.MyItems.Contains(x)).ToList().ForEach(x => x.ValidTo = DateTime.Now); 

     // Add or update MyItems sections 
     foreach (var MyItemsSection in team.MyItems) 
     { 
      if (MyItemsSection.Id == default(int)) 
      { 
       MyItemsSection.ValidFrom = DateTime.Now; 
       _t.MyItems.Add(MyItemsSection); 
      } 
      else 
      { 
       var _MyItemsSection = _t.MyItems.FirstOrDefault(x => x.Id == MyItemsSection.Id); 
       context.Entry(_MyItemsSection).CurrentValues.SetValues(MyItemsSection); 
      } 
     } 

_t是的EntityFramework連接對象圖,而team是已被斷開,並可能從外部更新的對象圖的相同類型。這裏的目標是同步兩個對象圖,以便更改得以持續。

我需要_t.MyItems和team.MyItems,其中MyItems要genericised通過使同樣的方法適用於MyOtherItems和MySocks,MyUnderPants等

這是在所有可能的?

+1

如果我們假設'MyItems'是'T'的集合,那麼所有其他的'T'都有屬性'Id'和'ValidTo'嗎?這似乎是使這個更通用,並與不同的集合工作的關鍵點 – Jamiec

+0

我認爲你應該發佈這codereview:http://codereview.stackexchange.com/ – maxbeaudoin

+0

是的,所有有Id和ValidTo,但上下文。 Entry().CurrentValues.SetValues()仍然需要工作,所以基於這兩個屬性的接口將不起作用。雖然我不知道如何去做,但我很確定我也需要傳入類型。 – Moo

回答

1

在回答我自己的問題,這裏就是答案 - 我錯過了什麼,你可以要求傳入類型爲實現特定的接口,而且還有它可作爲事實鍵入想要的。

所以,這裏是我想出了:

public void UpdateEntities<TEntity>(ICollection<TEntity> pocoCollection, ICollection<TEntity> dbCollection) 
     where TEntity : class, IEntity 
    { 
     // Archive deleted entities 
     dbCollection.Where(x => x.ValidTo == null && !pocoCollection.Contains(x)).ToList().ForEach(x => x.ValidTo = DateTime.Now); 

     // Add or update entities 
     foreach (var entity in pocoCollection) 
     { 
      if (entity.Id == default(int)) 
      { 
       entity.ValidFrom = DateTime.Now; 
       dbCollection.Add(entity); 
      } 
      else 
      { 
       var _entity = dbCollection.FirstOrDefault(x => x.Id == entity.Id); 
       context.Entry(_entity).CurrentValues.SetValues(entity); 
      } 
     } 
    } 

的一部分,我一直在尋找的是where TEntity : class, IEntity

在這個解決方案,我必須確保我的實體實現接口IEntity,它僅僅是:

public interface IEntity 
{ 
    int Id { get; set;} 
    DateTime ValidFrom { get; set; } 
    DateTime? ValidTo { get; set; } 
} 

這允許編譯器退出抱怨使用這些屬性的,而我仍然可以使用的實際類型所以實體框架也很滿意,並且對發生的事情不再感到困惑。

希望這可以幫助別人。

1

有兩種選擇:您可以將對象限制爲包含要在泛型方法中訪問的屬性和方法的已知基本類型,也可以使用謂詞執行選擇。

約束:

// base type 
interface IFoo { 
    int ID { get; set; } 
} 

    // generic method 
    public List<T> Update<T>(List<T> graph1, List<T> graph2) where T : IFoo { 
    var update = graph1.Intersect(graph2, (g1, g2) => { g1.ID == g2.ID }).ToList(); 
    return update; 
    } 

謂詞:

public void Update<T, U>(T _t, T team, Func<T, IList<U>> selector) 
{ 
    var _tItems = selector(_t); 
    var teamItems = selector(team); 

    // Archive deleted MyItems sections 
    _tItems.Where(x => x.ValidTo == null && !teamItems.Contains(x)).ToList().ForEach(x => x.ValidTo = DateTime.Now); 

    // Add or update MyItems sections 
    foreach (var MyItemsSection in teamItems) 
    { 
     if (MyItemsSection.Id == default(int)) 
     { 
      MyItemsSection.ValidFrom = DateTime.Now; 
      _tItems.Add(MyItemsSection); 
     } 
     else 
     { 
      var _MyItemsSection = _tItems.FirstOrDefault(x => x.Id == MyItemsSection.Id); 
      context.Entry(_MyItemsSection).CurrentValues.SetValues(MyItemsSection); 
     } 
    } 
} 

    //Usage: 
    Update(_t, team, (t => t.MyItems)); 

不過話又說回來,是什麼讓你寫它接受列表作爲參數的方法?

public void Update<T>(IList<T> _tItems, IList<T> teamItems)

+0

約束到一個基類型是行不通的,因爲它們會有所不同 - 我想泛化的結構更多的是EF如何工作的結果,而不是我自己的結果,所以EF仍然必須能夠做到這一點。我不知道你是否注意到,但我的代碼示例有三件事情 - 存檔已刪除的項目,添加新項目和更新現有項目。 EF會因各種原因合併對象圖形。 – Moo

+1

@Moo - 你可以將它們限制在一個基本接口上,你可以通過修改T4模板或者在一個新的文件中使用partial類對象的另一部分來添加這些接口。這根本不會影響EF,如果EF已經生成了接口需要的屬性,則不必執行其他任何操作。 – Bobson

+0

我明白,但不會context.Entry().CurrentValues.SetValues()得到一個類型,而不是它期望的類型(即實體)?因爲你可以將實體傳遞給Entry()和SetValues(),所以我不完全相信傳入約束類型不會導致EF完全錯過這個事實,即我傳遞了兩種實體類型它知道。那有意義嗎? – Moo

相關問題