首先,我不使用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沒有事務所的一個實例:
- 放FirmRepository中的用戶創建代碼。我不喜歡這個。爲什麼FirmRepository知道關於創建用戶實例的任何信息?這對我來說是違反SoC的。
- 不使用服務定位器模式。如果我走這條路,我覺得這是非常難以測試的。此外,具有顯式依賴性的對象的構造函數使這些依賴性顯而易見。
- 屬性注入而不是構造函數注入。這並不能解決問題,無論在FirmProxy中如何注入依賴關係,在創建FirmProxy時仍需要IUserRepository的一個實例。
- 必須「貶低」公司或用戶對象並在用戶身上公開FirmID,例如,而不是公司。如果我只是在做id,那麼從UserRepository中加載公司的要求就會消失,但隨着它能夠向Firm對象請求任何具有給定User實例上下文的內容的豐富性。
- 使用ORM。再次,我想要這樣做,但我做不到。沒有ORM的。這是規則(是的,這是一個糟糕的規則)
- 保留所有我的注入能力的依賴關係注入從最低級別的應用程序,這是UI(在我的情況下,一個.NET Web項目)注入的依賴項。在FirmProxy中沒有作弊和使用IoC代碼來爲我提供適當的依賴關係。無論如何,這基本上是使用服務定位器模式。
我想到NHiberante和Enitity Framework,看來他們沒有問題想出如何生成一個簡單的例子,我已經提出了SQL。
其他任何人有任何其他的想法/方法/等......這將幫助我實現我想要做的事情沒有ORM?
或者也許有不同的/更好的方法來解決這個問題?希望我不想失去的是能夠從用戶訪問公司,或獲得給定公司的用戶列表
投票移動到http://programmers.stackexchange.com/ – MattDavey 2012-03-09 16:35:21
@ indiecodemonkey你找到了解決方案嗎? – 2015-07-13 09:45:38