Подтвердить что ты не робот

Каков надлежащий способ ввода зависимости доступа к данным для ленивой загрузки?

Каков правильный способ ввода зависимости доступа к данным при ленивой загрузке?

Например, у меня есть следующая структура класса

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. Можно ли напрямую ссылаться на объект транзакции Inversion of Control Container? Это также кажется неудобным.

Как работают такие среды, как NHibernate?

4b9b3361

Ответ 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
    }
}

При создании объекта 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

Ответ 2

Я обычно делаю инъекцию зависимостей в конструкторе, как и у вас, но делайте ленивую загрузку еще дальше, действуя только тогда, когда вызывается "get", как у меня ниже. Не уверен, что это чистый подход, который вы ищете, но он устраняет "грязный" конструктор DI/Lazy Загрузка за 1 шаг;)

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

Ответ 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;
    }
}