9

首先,我不使用ORM,也不允許使用ORM。我必須使用ADO.NET手動推送我的存儲庫。如何打破版本庫之間的循環依賴關係

我有兩個對象:

public class Firm 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public virtual IEnumerable<User> Users { get; set; } 
} 

public class User 
{ 
    public Guid Id { get; set; } 
    public string Username { get; set; } 
    public Firm Firm { get; set; } 
} 

注意引用對方,一個企業擁有用戶的列表,每個用戶只有一個事務所。

現在我要設計我的資料庫:

public interface IFirmRepository 
{ 
    IEnumerable<Firm> FindAll(); 
    Firm FindById(Guid id); 
} 

public interface IUserRepository 
{ 
    IEnumerable<User> FindAll(); 
    IEnumerable<User> FindByFirmId(Guid firmId); 
    User FindById(Guid id); 
} 

到目前爲止,一切都很好。我想從UserRepository中爲每個用戶加載公司。 FirmRepository知道如何從持久性創建一個公司,所以我不想用FirmRepository保留這些知識。

public class UserRepository : IUserRepository 
{ 
    private IFirmRepository _firmRepository; 

    public UserRepository(IFirmRepository firmRepository) 
    { 
     _firmRepository = firmRepository; 
    } 

    public User FindById(Guid id) 
    { 
     User user = null; 
     using (SqlConnection connection = new SqlConnection(_connectionString)) 
     { 
      SqlCommand command = connection.CreateCommand(); 
      command.CommandType = CommandType.Text; 
      command.CommandText = "select id, username, firm_id from users where u.id = @ID"; 
      SqlParameter userIDParam = new SqlParameter("@ID", id); 
      command.Parameters.Add(userIDParam); 
      connection.Open(); 
      using (SqlDataReader reader = command.ExecuteReader()) 
      { 
       if (reader.HasRows) 
       { 
        user = CreateListOfUsersFrom(reader)[0]; 
       } 
      } 
     } 
     return user; 
    } 

    private IList<User> CreateListOfUsersFrom(SqlDataReader dr) 
    { 
     IList<User> users = new List<User>(); 
     while (dr.Read()) 
     { 
      User user = new User(); 
      user.Id = (Guid)dr["id"]; 
      user.Username = (string)dr["username"]; 
      //use the injected FirmRepository to create the Firm for each instance of a User being created 
      user.Firm = _firmRepository.FindById((Guid)dr["firm_id"]); 
     } 
     dr.Close(); 
     return users; 
    } 

} 

現在,當我通過UserRepository加載任何用戶時,我可以要求FirmRepository爲我建立用戶公司。到目前爲止,這裏沒什麼太瘋狂的。

現在的問題。

我想從我的FirmRepository中加載用戶列表。 UserRepository知道如何從持久性創建User,所以我想用UserRepository保留這些知識。因此,我將一個對IUserRepository的引用傳遞給FirmRepository:

public class FirmRepository : IFirmRepository 
{ 
    private IUserRepository 
    public FirmRepository(IUserRepository userRepository) 
    { 

    } 
} 

但是現在我們遇到了問題。 FirmRepository依賴於IUserRepository的實例,UserRepository現在依賴於IFirmRepository的一個實例。所以一個Repository不能在沒有其他實例的情況下創建。

如果我保持IoC容器沒有這個等式(我應該,b/c單元測試不應該使用IoC容器),我無法完成我想要做的事情。

沒問題,但是,我只是創建一個FirmProxy來延遲從公司加載用戶集合!這是一個更好的主意,我不希望所有的用戶在我去找公司或公司名單時加載所有的用戶。

public class FirmProxy : Firm 
{ 
    private IUserRepository _userRepository; 
    private bool _haveLoadedUsers = false; 
    private IEnumerable<User> _users = new List<User>(); 

    public FirmProxy(IUserRepository userRepository) 
     : base() 
    { 
     _userRepository = userRepository; 
    } 

    public bool HaveLoadedUser() 
    { 
     return _haveLoadedUsers; 
    } 

    public override IEnumerable<User> Users 
    { 
     get 
     { 
      if (!HaveLoadedUser()) 
      { 
       _users = _userRepository.FindByFirmId(base.Id); 
       _haveLoadedUsers = true; 
      } 
      return _users; 
     } 
    } 

} 

所以,現在我有一個很好的代理對象來促進延遲加載。所以,當我從持久性創建FirmRepository中的公司時,我會返回一個FirmProxy。

public class FirmRepository : IFirmRepository 
{ 

    public Firm FindById(Guid id) 
    { 
     Firm firm = null; 
     using (SqlConnection connection = new SqlConnection(_connectionString)) 
     { 
      SqlCommand command = connection.CreateCommand(); 
      command.CommandType = CommandType.Text; 
      command.CommandText = "select id, name from firm where id = @ID"; 
      SqlParameter firmIDParam = new SqlParameter("@ID", id); 
      command.Parameters.Add(firmIDParam); 
      connection.Open(); 
      using (SqlDataReader reader = command.ExecuteReader()) 
      { 
       if (reader.HasRows) 
       { 
        firm = CreateListOfFirmsFrom(reader)[0]; 
       } 
      } 
     } 
     return firm; 
    } 

private IList<Firm> CreateListOfFirmsFrom(SqlDataReader dr) 
{ 
    IList<FirmProxy> firms = new List<FirmProxy>([need an IUserRepository instance here!!!!]); 
    while (dr.Read()) 
    { 

    } 
    dr.Close(); 
    return firms; 
} 

但是,這仍然是行不通的!

爲了返回一個FirmProxy而不是一個公司,我需要能夠在我的FirmRepository類中新建一個FirmProxy。那麼,FirmProxy需要一個IUserRepository實例b/c UserRepository包含了如何從持久性創建一個User對象的知識。由於FirmProxy需要一個IUserRepository,我的FirmRepository現在也需要一個IUserRepository,我馬上就回到原點!

因此,鑑於此長篇大論,說明/源代碼,我怎麼能去能創建一個從FirmRepository用戶的實例,並從UserRepository沒有事務所的一個實例:

  1. 放FirmRepository中的用戶創建代碼。我不喜歡這個。爲什麼FirmRepository知道關於創建用戶實例的任何信息?這對我來說是違反SoC的。
  2. 不使用服務定位器模式。如果我走這條路,我覺得這是非常難以測試的。此外,具有顯式依賴性的對象的構造函數使這些依賴性顯而易見。
  3. 屬性注入而不是構造函數注入。這並不能解決問題,無論在FirmProxy中如何注入依賴關係,在創建FirmProxy時仍需要IUserRepository的一個實例。
  4. 必須「貶低」公司或用戶對象並在用戶身上公開FirmID,例如,而不是公司。如果我只是在做id,那麼從UserRepository中加載公司的要求就會消失,但隨着它能夠向Firm對象請求任何具有給定User實例上下文的內容的豐富性。
  5. 使用ORM。再次,我想要這樣做,但我做不到。沒有ORM的。這是規則(是的,這是一個糟糕的規則)
  6. 保留所有我的注入能力的依賴關係注入從最低級別的應用程序,這是UI(在我的情況下,一個.NET Web項目)注入的依賴項。在FirmProxy中沒有作弊和使用IoC代碼來爲我提供適當的依賴關係。無論如何,這基本上是使用服務定位器模式。

我想到NHiberante和Enitity Framework,看來他們沒有問題想出如何生成一個簡單的例子,我已經提出了SQL。

其他任何人有任何其他的想法/方法/等......這將幫助我實現我想要做的事情沒有ORM?

或者也許有不同的/更好的方法來解決這個問題?希望我不想失去的是能夠從用戶訪問公司,或獲得給定公司的用戶列表

+0

投票移動到http://programmers.stackexchange.com/ – MattDavey 2012-03-09 16:35:21

+0

@ indiecodemonkey你找到了解決方案嗎? – 2015-07-13 09:45:38

回答

5

您需要更清楚地瞭解您的聚合根對象,即焦點每個存儲庫。您不會爲數據庫中的每個表創建存儲庫,但這不是目標。其目的是識別需要使用的聚合根對象,包括子表/記錄,即用戶(公司,地址,訪問權限)。這就是您的存儲庫將返回到您的應用程序。

您遇到的困難是向您顯示您的存儲庫結構不正確。任何時候我發現我的代碼變得太難了,它會引起我的警覺,說我可能做錯了,我以此爲生;)

+1

+1 - 在這種情況下,公司和用戶都是聚合根。在這些場景中,我通常在兩個根之間有一個軟引用(通過引用鍵),如果我需要一個將它們連接在一起的數據結構,我會明確地做它(可以是查詢對象或視圖模型等)。 – MattDavey 2012-03-09 16:33:05

+0

我看到公司是聚合根,並且該用戶屬於該根。但與此同時,我並不認爲公司是一個可以證明我可以與用戶合作,並且完全不與用戶公司打交道的證據。然而,當我堅持用戶回到數據庫,在agg根的例子中,我必須堅持所有的企業,所有的用戶。 MattDavey說他們都是根源,這很有趣。他把我引回到軟引用,我希望我可以遠離,我喜歡使用來自每個實體的對象引用。 – 2012-03-09 16:58:38

+1

我通常關於聚合根的方式是通過'級聯刪除'的規則。如果你刪除了一個聚合根,那麼下面的所有東西都應該被刪除。如果你刪除一家公司,是否所有的用戶都被刪除? – MattDavey 2012-03-09 17:42:45