2011-04-12 91 views
0

我想要一個支持類來幫助多對多關係。Linq to SQL:多對多支持類

正如我現在看到的那樣,它可能是一個雙重泛型類,您可以在一個或兩個圍繞關係的實體局部類中定義它。

獲得它允許訪問其他表而不必特別提及關係表應該很容易。然而,添加或從集合中刪除有點棘手。您也可以在關係表中添加一行,並根據所做的操作將其提交或刪除。

這可以通過傳遞到這個泛型類的函數來完成嗎?

像這樣的類是否存在,如果不存在,是否可以完成?

回答

2

您可以創建一個IManyToManySet<TEntity>接口,該接口可以從您的多個屬性返回並具有ManyToManySet<TSource, TCross, TDestination>實現以及查詢插入和刪除功能。

的界面看起來可能是這樣:

public interface IManyToManySet<TEntity> : IEnumerable<TEntity> 
    where TEntity : class 
{ 
    int Count { get; } 
    void Add(TEntity entity); 
    bool Remove(TEntity entity); 
    void AddRange(IEnumerable<TEntity> collection); 
} 

和實現可能是這樣的:

public class ManyToManySet<TSource, TCross, TDestination> 
    : IManyToManySet<TDestination>, IEnumerable<TDestination> 
    where TDestination : class 
    where TSource : class 
    where TCross : class 
{ 
    private TSource source; 
    private EntitySet<TCross> crossSet; 
    private Func<TCross, TDestination> destinationSelector; 
    private Func<TSource, TDestination, TCross> crossFactory; 

    public ManyToManySet(TSource source, 
     EntitySet<TCross> crossSet, 
     Func<TCross, TDestination> destinationSelector, 
     Func<TSource, TDestination, TCross> crossFactory) 
    { 
     this.source = source; 
     this.crossSet = crossSet; 
     this.destinationSelector = destinationSelector; 
     this.crossFactory = crossFactory; 
    } 

    public int Count 
    { 
     get { return this.crossSet.Count; } 
    } 

    public void Add(TDestination entity) 
    { 
     var newEntity = this.crossFactory(this.source, entity); 
     this.crossSet.Add(newEntity); 
    } 

    public bool Remove(TDestination entity) 
    { 
     var existingEntity = (
      from c in this.crossSet 
      where this.destinationSelector(c) == entity 
      select c) 
      .SingleOrDefault(); 

     if (existingEntity != null) 
     { 
      return this.crossSet.Remove(existingEntity); 
     } 

     return false; 
    } 

    public void AddRange(IEnumerable<TDestination> collection) 
    { 
     foreach (var entity in collection) 
     { 
      this.Add(entity); 
     } 
    } 

    public IEnumerator<TDestination> GetEnumerator() 
    { 
     return this.crossSet.Select(this.destinationSelector) 
      .GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

您需要提供幾件事情在此實現:

  1. TSource實例,該實例指向定義屬性的實體。
  2. 指向定義交叉表的實體列表的EntitySet<TCross>
  3. 投影功能,可讓您將TCross轉換爲TDestination
  4. 工廠功能允許您根據TSourceTDestination創建新的TCross

翻譯這一個實際的例子(使用ProductOrder),會給你的Order實體以下屬性:

private IManyToManySet<Product> products; 
public IManyToManySet<Product> Products 
{ 
    get 
    { 
     if (this.products != null) 
     { 
      this.products = new ManyToManySet<Order, OrderProduct, Product>(
       this, this.OrderProducts, op => op.Product, 
       (o, p) => new OrderProduct { Order = o, Product = p }); 
     } 

     return this.products; 
    } 
} 

而下面的屬性在Product實體:

private IManyToManySet<Order> orders; 
public IManyToManySet<Order> Orders 
{ 
    get 
    { 
     if (this.orders == null) 
     { 
      this.orders = new ManyToManySet<Product, OrderProduct, Order>(
       this, this.OrderProducts, op => op.Order, 
       (p, o) => new OrderProduct { Order = o, Product = p }); 
     } 

     return this.orders; 
    } 
} 

IManyToManySet<T>接口實際上是多餘的,因爲您可以直接返回ManyToMany<TSource, TCross, TDestination>。然而,該接口隱藏了TSourceTCross類型參數,這使得該屬性的用戶可讀性更高。

請注意,此實現具有與LINQ to SQL的EntitySet<T>相同的加載行爲;當它被使用時,它會將完整的一組對象加載到內存中。就像在集合上使用whereFirstEntitySet<T>一樣,仍然從數據庫加載完整的集合。你需要意識到這一點。

不過,LINQ to SQL理解LINQ查詢中的EntitySet<T>屬性。在LINQ查詢中有一個IManyToManySet<T>將失敗。

我希望這會有所幫助。

+0

我有一個小問題是刪除關係,因爲該操作試圖在交叉表中將外鍵清零,而不是僅刪除該行。我發現我可以在關聯上設置一個DeleteOnNull,以確保當從父項目entitySet中移除時該行將被刪除。 – 2011-04-13 14:03:57

+0

當一個計劃聚集在一起時,我喜歡它! – Steven 2011-04-13 14:34:57

1

很難(甚至不可能)創建LINQ到SQL,感覺像一些本土的解決方案,因爲這樣的解決方案必須在以下幾個方面工作:

  1. 許多收集應建模爲另一個很多實體的財產。
  2. 它必須支持插入,更新和刪除。
  3. 它應該在編寫LINQ查詢時工作。

很容易爲第1點做出解決方案。例如,帶有Product類的模型與Order具有多對多關係。您可以定義在Order類以下屬性:

public IEnumerable<Product> Products 
{ 
    get { return this.OrderProducts.Select(op => op.Product); } 
} 

然而,這並不與點2和3的工作,而我們可以做一個普通的集合,它允許插入,LINQ to SQL的將永遠無法翻譯將此屬性用於SQL查詢。例如,下面的LINQ查詢看起來無辜:

var bigOrders = 
    from order in context.Orders 
    where order.Products.Any(p => p.Price > 100) 
    select order; 

不幸的是,這個查詢將失敗,因爲LINQ to SQL的不知道如何Products屬性SQL映射。

如果您本地需要此功能,則應考慮遷移到實體框架(4.0或更高版本)。 EF本身支持這一點。

+0

好的我將在下一個項目中使用EF。但是,讓我們說我不想以有限的方式對待另一個。基本上得到關係另一端的對象列表(例子中的產品),並能夠直接插入,更新和刪除那些會自動創建關係的集合。這可以通過將Order類中的函數傳遞到處理添加的自定義集合類來完成嗎? – 2011-04-12 11:50:54

+0

@Ingó:看到我的第二個答案。我寫了一個可以做到這一點的泛型類的實現。 – Steven 2011-04-12 15:14:33