为延迟加载注入数据访问依赖项的正确方法是什么?
延迟加载时注入数据访问依赖项的正确方法是什么?
例如我有下面的类结构
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的引用到事务对象中?如果我想让Transaction至少看起来像POCO,那么对于构造函数而言,这似乎没有任何意义。可以让Transaction对象直接引用"控制容器的反转"吗?这也似乎很尴尬。
像NHibernate这样的框架如何处理呢?
解决方案
我通常像上面一样在构造函数中进行依赖项注入,但是通过仅当像下面我这样调用" get"时才采取行动来进一步延迟加载。不确定这是否是我们要寻找的纯方法,但是它确实消除了"脏"的构造函数DI /延迟加载,只需一步;
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; } } }
我对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; } }
我建议有所不同...
使用延迟加载类:
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 } }
创建未绑定到数据库的Transcation对象时:
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