【问题标题】:What is the proper way to inject a data access dependency for lazy loading?为延迟加载注入数据访问依赖项的正确方法是什么?
【发布时间】:2010-09-12 22:01:37
【问题描述】:

当我进行延迟加载时,注入数据访问依赖项的正确方法是什么?

例如我有以下类结构

class CustomerDao : ICustomerDao
  public Customer GetById(int id) {...}

class Transaction {
  int customer_id; //Transaction always knows this value
  Customer _customer = null;
  ICustomerDao _customer_dao;
  Customer GetCustomer() {
    if(_customer == null)
      _customer = _customer_dao.GetById(_customer_id);
    return _customer
  }

如何将 _customer_dao 的引用放入事务对象中?如果我希望事务至少看起来像 POCO,那么为构造函数要求它似乎没有任何意义。让 Transaction 对象直接引用 Inversion of Control Container 可以吗?这似乎也很尴尬。

像 NHibernate 这样的框架如何处理这个问题?

【问题讨论】:

    标签: inversion-of-control lazy-loading


    【解决方案1】:

    我建议一些不同的东西...... 使用延迟加载类:

    public class Lazy<T>
    {
       T value;
       Func<T> loader;
    
       public Lazy(T value) { this.value = value; }
       public Lazy(Func<T> loader { this.loader = loader; }
    
       T Value
       {
         get 
        {
           if (loader != null)
           {
             value = loader();
             loader = null;
           }
    
           return value;
        }
    
        public static implicit operator T(Lazy<T> lazy)
        {
            return lazy.Value;
        }
    
        public static implicit operator Lazy<T>(T value)
        {
            return new Lazy<T>(value);
        }
    }
    

    一旦你得到它,你就不需要再在你的对象中注入 dao 了:

    public class Transaction
    {
        private static readonly Lazy<Customer> customer;
    
        public Transaction(Lazy<Customer> customer)
        {
          this.customer = customer;
        }
    
        public Customer Customer
        {
           get { return customer; } // implicit cast happen here
        }
    }
    

    当创建一个未绑定到数据库的事务对象时:

    new Transaction(new Customer(..)) // implicite cast 
                                      //from Customer to Lazy<Customer>..
    

    从存储库中的数据库重新生成事务时:

    public Transaction GetTransaction(Guid id)
    {
       custmerId = ... // find the customer id 
       return new Transaction(() => dao.GetCustomer(customerId));
    }
    

    发生了两件有趣的事情: - 您的域对象可以在有或没有数据访问的情况下使用,它变得对数据访问无知。唯一的小转折是允许传递一个提供对象而不是对象本身的函数。 - Lazy 类是内部可变的,但可以用作不可变值。 readonly 关键字保持其语义,因为它的内容不能从外部更改。

    当您希望该字段可写时,只需删除 readonly 关键字即可。分配新值时,由于隐式转换,将使用新值创建一个新的 Lazy。

    编辑: 我在这里写了一篇博客:

    http://www.thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance

    【讨论】:

    • 无论如何,正确定义聚合并避免延迟加载总是更好...
    【解决方案2】:

    我通常像上面那样在构造函数中进行依赖注入,但是只有在像下面这样调用“get”时才采取行动,从而使延迟加载更进一步。不确定这是否是您正在寻找的纯方法,但它确实在 1 步中消除了“脏”构造函数 DI/Lazy Loading ;)

    public class Product
    {
        private int mProductID;
        private Supplier mSupplier;
        private ISupplierService mSupplierService;
    
        public Product()
        {
          //if you want your object to remain POCO you can use dual constr
          //this constr will be for app use, the next will be for testing
        } 
    
        public Product(ISupplierService SupplierService)
        {
            mSupplierService = SupplierService;
        }
    
        public Supplier Supplier {
            get {
                if (mSupplier == null) {
                    if (mSupplierService == null) {
                        mSupplierService = new SupplierService();
                    }
                    mSupplier = mSupplierService.GetSupplierByProductID(mProductID);
                }
                return mSupplier;
            }
            set { mSupplier = value; }
        }
    }
    

    【讨论】:

    • 可以更优雅,但这通常是我有时间的实现。如果您喜欢这种事情,它还会让您的代码对一些更高级别的库开放,以通过反射处理延迟加载。
    • 同上:注入服务(尤其是 DAO)不是一个好主意。它使对象在没有依赖关系的情况下无法使用,并且很难测试。它打破了顽固的无知。
    • 我个人尽量避免出现属性 (mSupplier) 可能 被加载但可能不会加载的情况。每次使用实体时都会让我担心:是否有人不完整地加载它,即mSupplier 为空,但它不应该是?我需要对mSupplier 进行空检查吗?这些空检查是否有机会触发延迟加载? (诚​​然,我仍在寻找圣杯。)
    【解决方案3】:

    我对 POCO 这个术语不是很熟悉,但我读过的定义似乎通常遵循对象独立于某些更大框架的精神。

    也就是说,无论你如何分割它,如果你正在执行依赖注入,你将与那些功能被注入的类进行协作,以及将依赖对象粘在其中的东西,无论它是完整的注入框架或只是一些汇编程序类。

    对我来说,将 IOC 容器的引用注入到类中似乎很奇怪。我更喜欢在构造函数中进行注入,代码如下所示:

    public interface IDao<T>
    {
        public T GetById(int id);
    }
    
    
    public interface ICustomerDao : IDao<Customer>
    {
    }
    
    public class CustomerDao : ICustomerDao
    {
        public Customer GetById(int id) 
        {...}
    }
    
    public class Transaction<T> where T : class
    {
    
        int _id; //Transaction always knows this value
        T _dataObject;
        IDao<T> _dao;
    
        public Transaction(IDao<T> myDao, int id)
        {
            _id = id;
            _dao = myDao;
        }
    
        public T Get()
        {
            if (_dataObject == null)
                _dataObject = _dao.GetById(_id);
            return _dataObject;
        }
    }
    

    【讨论】:

    • 所以你基本上是说我应该通过构造函数注入它?也许吧,但看起来很尴尬。
    • 注入服务(尤其是 DAO)不是一个好主意。它使对象在没有依赖关系的情况下无法使用,并且很难测试。它打破了顽固的无知。
    猜你喜欢
    • 2012-11-17
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 2016-09-15
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    相关资源
    最近更新 更多