2008-09-24 8 views
11

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

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

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, обрабатывают это?

ответ

1

Обычно я использую инъекцию зависимостей в конструкторе, как и вы, но выполняйте ленивую загрузку дальше, действуя только тогда, когда вызывается «get», как у меня внизу. Не уверен, если это чистый подход вы ищете, но это не исключает «грязный» конструктор DI/отложенную загрузку в 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; } 
    } 
} 
+0

Может быть более элегантным, но это, как правило, реализация у меня есть время для. Он также оставляет ваш код открытым для какой-либо библиотеки более высокого уровня, чтобы обрабатывать ленивую загрузку путем отражения, если вы в этом верите. – ojrac 2009-02-06 22:51:33

+2

То же замечание, что и выше: Инъекционные услуги (и особенно DAO) - это не очень хорошая идея. Это делает объект непригодным для использования без зависимости и делает его очень трудным для тестирования. Это сильно нарушает постоянство невежества. – thinkbeforecoding 2009-08-14 14:42:42

+0

Я лично стараюсь избегать сценария, в котором может быть загружено свойство (`mSupplier`) *, но может и не быть. Это меня беспокоит каждый раз, когда я использую объект: кто-то загрузил его не полностью, т. Е. `MSupplier` имеет значение null, но это не должно было быть? Нужны ли нулевые проверки на `mSupplier`? У этих нулевых проверок есть вероятность запуска ленивой загрузки? (По общему признанию, я все еще ищу святого Грааля.) – Timo 2016-11-09 10:49:30

1

Я не очень знаком с термином 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; 
    } 
} 
7

я предлагаю что-то другое ... Используйте ленивый класс нагрузки:

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

После того, как вы получите его, вы не нужно вводить дао в вас объект больше:

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

Два происходят интересные вещи: - Ваши объекты домена могут быть использованы с или без доступа к данным, становится данных Acces невежественны. Единственное маленькое завихрение состоит в том, чтобы позволить передать функцию, которая дает объект вместо самого объекта. - Класс Lazy является внутренне изменчивым, но может использоваться как неизменяемое значение. Ключевое слово readonly сохраняет свою семантику, поскольку ее содержимое не может быть изменено внешним.

Если вы хотите, чтобы поле было доступно для записи, просто удалите ключевое слово readonly. при назначении нового значения новый Lazy будет создан с новым значением из-за неявного приведения.

Edit: Я писал о нем здесь:

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

 Смежные вопросы

  • Нет связанных вопросов^_^