Architecture 当您需要进行延迟加载(并使用IOC容器)时,如何避免循环数据访问对象依赖关系?
注意:下面的例子是C#,但这个问题不应该特别针对任何语言 因此,我正在使用的变体构建一个对象域。对于那些不熟悉它的人,为了节省一些阅读时间,我们的想法只是为每个域对象提供一个数据访问对象接口,负责向持久层加载数据或从持久层加载数据。所有可能需要加载/保存给定对象的东西都会接受该对象的数据访问接口作为依赖项。因此,例如,我们可以在以下情况下,产品将根据需要延迟加载购买它的客户:Architecture 当您需要进行延迟加载(并使用IOC容器)时,如何避免循环数据访问对象依赖关系?,architecture,dependency-injection,Architecture,Dependency Injection,注意:下面的例子是C#,但这个问题不应该特别针对任何语言 因此,我正在使用的变体构建一个对象域。对于那些不熟悉它的人,为了节省一些阅读时间,我们的想法只是为每个域对象提供一个数据访问对象接口,负责向持久层加载数据或从持久层加载数据。所有可能需要加载/保存给定对象的东西都会接受该对象的数据访问接口作为依赖项。因此,例如,我们可以在以下情况下,产品将根据需要延迟加载购买它的客户: public class Product { private ICustomerDao _customerDao;
public class Product {
private ICustomerDao _customerDao;
private Customer _customer;
public Product(ICustomerDao customerDao) {_customerDao = customerDao;}
public int ProductId {get; set;}
public int CustomerId {get; set;}
public Customer Customer {
get{
if(_customer == null) _customer = _customerDao.GetById(CustomerId);
return _customer;
}
}
public interface ICustomerDao {
public Customer GetById(int id);
}
在两个对象需要能够彼此加载之前,这一切都很好。例如,多对一关系,如上所述,产品需要能够延迟加载其客户,但客户也需要能够获得其产品的列表
public class Customer {
private IProductDao _productDao;
private Product[] _products;
public Customer(IProductDao productDao) {_productDao = productDao;}
public int CustomerId {get; set;}
public Product[] Products {
get{
if(_products == null) _products = _productDao. GetAllForCustomer(this);
return _products;
}
}
public interface IProductDao {
public Product[] GetAllForCustomer(Customer customer);
}
我知道这是一个非常普遍的情况,但我在这方面相对较新。我的绊脚石是在实现数据访问对象时要做什么。因为客户依赖于IPProductDAO,所以CustomerDao实现也必须依赖于IPProductDAO,反之亦然,ProductDao必须依赖于ICCustomerDAO
public class CustomerDao : ICustomerDao {
private IProductDao _productDao;
public CustomerDao(IProductDao productDao) {_productDao = productDao;}
public Customer GetById(int id) {
Customer c = new Customer(_customerDao);
// Query the database and fill out CustomerId
return c;
}
}
public class ProductDao : IProductDao {
private ICustomerDao _customerDao;
public ProductDao (ICustomerDao customerDao) {_customerDao = customerDao;}
public Product[] GetAllForCustomer(Customer customer) {
// you get the idea
}
}
这就是问题所在。如果没有IPProductDAO,则不能实例化CustomerDao,反之亦然。我的控制反转容器(Castle Windsor)碰到循环依赖项并阻塞
我已经提出了一个暂时的解决方案,它涉及到延迟加载DAO对象本身(我将把它作为一个答案发布),但我不喜欢它。这个问题经过时间考验的解决方案是什么
编辑:以上是我实际使用的架构的简化,我不建议有人实际将DAO传递给对象。更接近我实际操作的更好的实现类似于NHibernate的工作方式,其中实际对象非常简单,上面的实际上是代理对象,它们继承并覆盖适当的字段。延迟加载应该由持久层管理,而不是由存储库管理
此外,您的客户和产品对象不应该有任何对存储库的引用,这似乎。。。错。您不应该有这样的循环依赖项。这是错误的 如果您只需要在一段时间内出错就可以克服困难,那么请尝试使用服务定位器:一个了解容器并在使用依赖项时解析依赖项的类,而不是在实例化DAO类时解析依赖项的类
您可能想查看Oren Eini在Rhino.Tools中的服务定位器实现(它是一个名为IoC的静态类)我的解决方案,并且让我再次声明,我不太喜欢让每个数据访问对象直接从容器中延迟加载它所依赖的DAO。为了简单起见,我将CustomerDao和ProductDao都从BaseDao对象继承,然后按如下方式实现它:
public abstract class BaseDao() {
private ICustomerDao _customerDao;
protected ICustomerDao _CustomerDao {
get {
if(_customerDao == null) _customerDao = IoC.Container.Resolve<ICustomerDao>();
return _customerDao;
}
private IProductDao _productDao;
protected IProductDao _ProductDao {
get {
if(_productDao == null) _productDao = IoC.Container.Resolve< IProductDao >();
return _productDao;
}
我不喜欢这个,因为
- a) 现在,每个DAO都依赖于 容器
- b) 你不能再这样了 从构造器中说出每个 道靠
- c) 每把刀 至少需要一个隐含的 相互依赖如果你 使用这样的基类,或者您可以 将延迟加载移出 基类,但必须复制 大量的代码
- d) 单元测试 需要创建和预填充 容器
public class CustomerDao : ICustomerDao {
private IProductDao _productDao;
public IProductDao ProductDao {
get { return _productDao; }
set { _productDao = value; }
}
public CustomerDao() { }
public Customer GetById(int id) {
Customer c = new Customer(_productDao);
// Query the database and fill out CustomerId
return c;
}
}
public class ProductDao : IProductDao {
private ICustomerDao _customerDao;
public ProductDao() { }
public ICustomerDao CustomerDao {
get { return _customerDao; }
set { _customerDao = value; }
}
public Product[] GetAllForCustomer(Customer customer) {
return null;
}
}
正如其他海报所建议的那样,你可能想重新思考你的架构——看起来你在给自己制造麻烦 另请注意: 通过更改数据访问对象 依赖于属性而不是 温莎将 在实例化每个后自动填充它们 完全反对
小心点。在这样做时,您基本上告诉Windsor这些依赖项是可选的(不同于通过构造函数注入的依赖项)。这似乎是个坏主意,除非这些依赖项是真正可选的。如果温莎不能满足所需的依赖性,你希望它呕吐。这是一种简化。从技术上讲,他们没有。我被禁止使用NHibernate,但它的工作方式与实际对象是POCO的情况类似,这些代理覆盖了必要的方法,从来没有被直接实例化过。你说没有这样的循环依赖是什么意思?在我的例子中,客户引用产品,反之亦然,你能告诉我吗?这是真的,体系结构很笨拙,但编写框架代码更糟糕,NHibernate不是一个选项。目前,我实际上已经将代码移动到一个完全独立的延迟加载代理层,该层的工作方式类似于NHibernate。
public class CustomerDao : ICustomerDao {
private IProductDao _productDao;
public IProductDao ProductDao {
get { return _productDao; }
set { _productDao = value; }
}
public CustomerDao() { }
public Customer GetById(int id) {
Customer c = new Customer(_productDao);
// Query the database and fill out CustomerId
return c;
}
}
public class ProductDao : IProductDao {
private ICustomerDao _customerDao;
public ProductDao() { }
public ICustomerDao CustomerDao {
get { return _customerDao; }
set { _customerDao = value; }
}
public Product[] GetAllForCustomer(Customer customer) {
return null;
}
}