3

注意:以下示例是 C#,但此问题不应特定于任何语言。

因此,我正在使用S# Architecture的变体构建对象域。对于那些不熟悉它的人,为了节省一些阅读时间,这个想法很简单,就是为每个负责加载到/从持久层加载的域对象都有一个数据访问对象接口。可能需要加载/保存给定对象的所有内容都接受该对象的数据访问接口作为依赖项。因此,例如,我们可以有以下内容,其中产品将根据需要延迟加载购买它的客户:

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);
}

我知道这是一个非常普遍的情况,但我在这方面相对较新。我的绊脚石是实现数据访问对象时要做什么。因为 Customer 依赖于 IProductDao,所以 CustomerDao 实现也必须,反之亦然,ProductDao 必须依赖于 ICustomerDao。

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
      }
 }

在这里我们遇到了问题。没有 IProductDao 就不能实例化 CustomerDao,反之亦然。我的控制容器(温莎城堡)的反转遇到了循环依赖和阻塞。

我提出了一个临时解决方案,其中涉及延迟加载 DAO 对象本身(我将发布此作为答案),但我不喜欢它。这个问题有哪些久经考验的解决方案?

编辑:以上是我实际使用的架构的简化,我不建议有人实际将 DAO 传递给对象。更接近我实际所做的更好的实现类似于 NHibernate 的工作方式,其中实际对象非常简单,上面实际上是继承和覆盖适当字段的代理对象。

4

5 回答 5

2

正如其他海报所建议的那样,您可能需要重新考虑您的架构 - 看起来您正在让自己变得艰难。

另请注意:

通过将数据访问对象依赖项更改为属性而不是构造函数依赖项,Windsor 将在完全实例化每个对象后自动填充它们。

当心。在执行此操作时,您基本上已经告诉 Windsor 这些依赖项是可选的(与通过构造函数注入的依赖项不同)。这似乎是个坏主意,除非这些依赖确实是可选的。如果 Windsor 无法满足所需的依赖项,您希望它呕吐。

于 2008-11-14T21:30:44.447 回答
1

延迟加载应该由您的持久层管理,而不是您的存储库。

此外,您的 Customer 和 Product 对象不应该有任何对存储库的引用,这似乎......错了。

于 2008-11-07T19:44:54.857 回答
1

我刚刚发现了一种至少适用于温莎城堡的明显更好的方法。通过将数据访问对象依赖项更改为属性而不是构造函数依赖项,Windsor 将在完全实例化每个对象后自动填充它们。

所以以下工作就好了:

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;
    }

}
于 2008-11-08T01:10:53.170 回答
0

你根本不应该有这样的循环依赖。这是错误的。

如果您只需要在一段时间内犯错以克服困难,那么请尝试使用服务定位器:一个了解容器并在使用它们而不是在实例化 DAO 类时解决依赖关系的类。

您可能想查看 Oren Eini 在 Rhino.Tools 中的服务定位器实现(它是一个名为 IoC 的静态类)

于 2008-11-07T19:45:32.217 回答
0

我的解决方案,让我再次声明,我不是它的忠实拥护者,让每个数据访问对象延迟加载它直接从容器中依赖的 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;
    }

}

进而

public class CustomerDao : BaseDao, ICustomerDao {
      public Customer GetById(int id) {
          Customer c = new Customer(_CustomerDao);
          // Query the database and fill out CustomerId
          return c;
      }
 }
public class ProductDao : BaseDao, IProductDao {
      public Product[] GetAllForCustomer(Customer customer) {
          // use the base class's _ProductDao to instantiate Products
      }
 }

我不喜欢这个,因为

  • a) 每个 DAO 现在都依赖于容器
  • b) 你不再能从构造函数中分辨出每个 Dao 依赖于谁
  • c) 如果您使用这样的基类,则每个 DAO 至少需要相互隐式依赖,或者您可以将延迟加载移出基类,但必须复制大量代码。
  • d) 单元测试需要创建并预填充Container
于 2008-11-07T19:46:23.747 回答