2009-06-04 119 views
12

在具有ORM的應用程序中,在類之間具有雙向映射是非常常見的情況。像這樣:保持類之間的雙向關係

public class Product 
{ 
    private List<Price> HistoricPrices { get; private set;} 
} 

public class Price 
{ 
    private Product Product { get; set; } 
} 

在代碼中維護這種關係是否有公認的方法?所以當我向產品添加價格時,產品屬性會自動設置?

理想情況下,我正在尋找一個易於重複使用的解決方案。將一些東西添加到集合中然後手動設置相反的關係似乎是錯誤的。

請注意,這不是一個關於如何模擬產品和價格的問題,這是一個如何建立雙向關係模型的問題。有很多情況下這是完全合理的。

+0

也許應該選擇一個不同的例子,因爲這個例子沒有證明這種關係的需要。 – 2009-06-04 14:13:45

回答

18

首先,我認爲你現在是混亂的例子 - 這是罕見的像價格將被建模爲對象或者參考具有價格的實體。但我認爲這個問題是合法的 - 在ORM世界中,這有時被稱爲圖一致性。據我所知沒有一個明確的方法來解決這個問題,有幾種方法。

首先讓我們來稍微改變的例子:

public class Product 
{ 
    private Manufacturer Manufacturer { get; private set;} 
} 

public class Manufacturer 
{ 
    private List<Product> Products { get; set; } 
} 

所以每個產品都有一個製造商,而每個廠商可以有相關的產品清單。該模型面臨的挑戰是,如果Product類和Manufacturer類保持彼此不連接的引用,則更新一個可以使另一個無效。

有解決這一問題的幾種方法:

  1. 消除循環引用。這解決了這個問題,但使得對象模型不那麼富有表現力和難以使用。

  2. 更改代碼以便製造商的產品和產品列表中的製造商參考號是反身。換句話說,改變一個會影響另一個。這通常需要一些代碼,setter和collection來攔截變化,並將它們反射到中。

  3. 管理另一個屬性。因此,您不必在產品中存儲對製造商的參考,而是通過搜索所有制造商來計算它,直到找到擁有您的產品。相反,您可以在產品類中保留對製造商的引用,並動態構建產品列表。在這種方法中,你通常會將關係的一方變爲只讀。順便說一句,這是標準的關係數據庫方法 - 實體通過在一個地方管理的外鍵相互引用。

  4. 這兩個這兩個類的外部關係,並在一個單獨的對象(通常稱爲ORM中的數據上下文)中管理它。當產品想要返回製造商時,它會詢問DataContext。當製造商想要返回產品清單時,它也是一樣的。在內部,有很多方法來實現數據上下文,一組雙向字典並不少見。

最後,我會提到,您應該考慮使用ORM工具(如NHibernate或CSLA),它可以幫助您管理圖形一致性。這通常不是一個容易正確解決的問題 - 一旦你開始探索諸如多對多關係,一對一關係和延遲加載對象等情況,它很容易變得非常複雜。您最好使用現有的庫或產品,而不是創建自己的機制。

這裏有一些links在NHibernate中討論bidirectional associations,你可能會覺得有用。

下面是使用方法#3直接管理關係的代碼示例 - 這通常是最簡單的方法。請注意,只有關係的一方是可編輯的(在本例中是製造商) - 外部消費者不能直接設置產品的製造商。

而不是「設置」產品上的價格
public class Product 
{ 
    private Manufacturer m_manufacturer; 

    private Manufacturer Manufacturer 
    { 
     get { return m_manufacturer;} 
     internal set { m_manufacturer = value; } 
    } 
} 

public class Manufacturer 
{ 
    private List<Product> m_Products = new List<Product>(); 

    public IEnumerable<Product> Products { get { return m_Products.AsReadOnly(); } } 

    public void AddProduct(Product p) 
    { 
     if(!m_Products.Contains(p)) 
     { 
     m_Products.Add(p); 
     p.Manufacturer = this; 
     } 
    } 

    public void RemoveProduct(Product p) 
    { 
     m_Products.Remove(p); 
     p.Manufacturer = null; 
    } 
} 
+0

感謝您的明確答覆。爲了迴應你所說的關於允許ORM管理它,這是我已經做的。目前使用nhibernate。但是我想知道如何自己管理它,因爲我的測試失敗了,除非對象被持久化並從存儲庫讀回。之前保存關係是一種方式,保存後是2種方式。這對我來說並不合適。 – 2009-06-04 14:44:06

2

這不是模擬此問題的正確方法。

一個Product應該有一個Price,一個Price不該有Product

public class Product 
{ 
    public Price CurrentPrice {get; private set; } 
    public IList<Price> HistoricPrices { get; private set;} 
} 

public class Price { } 

在特定的設置,這是什麼意思爲一個PriceProduct?在我上面創建的班級中,您將能夠處理Product班級內的所有定價。

+0

如果價格將被命名爲「ProductPrice」,這將是正確的方法。無論如何 - 我想這不是他問題的重點。 – 2009-06-05 13:34:09

0

我首先想到的是,在用於添加價格,添加一行代碼,像這樣的功能/特性:

public void addPrice(Price p) { 
    //code to add price goes here 
    p.Product = this; 
} 
1

在我添加「鏈接」的方法,即過去的,你鏈接的產品和價格。

public void linkPrice(Price toLink) { 
    this.price = toLink; 
    toLink.setProduct(this); 
} 

(如果你使用setPrice要做到這一點,那麼setProduct也將這樣做,他們將永遠互相稱呼在第二行,所以我創建了一個清晰的聯繫方法,而不是使用getter和setter方法的。事實上,二傳可以保護個包。

因人而異,你的情況可能會有所不同。

0

在過去,我已經做了這樣的事情,當我需要保持這種關係......

public class Owner 
{ 
    public List<Owned> OwnedList { get; set; } 
} 

public class Owned 
{ 
    private Owner _owner; 

    public Owner ParentOwner 
    { 
     get 
     { 
      if (_owner == null) 
       _owner = GetParentOwner(); // GetParentOwner() can be any method for getting the parent object 
      return _owner; 
     } 
    } 
} 
0

我現在正在研究類似這個問題,我在LBushkin的答案中使用了#3。

我有一個數據存儲對象,包含所有數據類的列表。數據類在其中具有用於引用其他類的ID,並且我將回調到數據存儲類以獲取參考項目。

我發現這非常有用,因爲我需要能夠過濾數據,並在從數據存儲請求數據時完成。