inversion-of-control - injection - inyeccion de dependencias spring autowired
¿Cuál es la forma correcta de inyectar una dependencia de acceso a datos para la carga diferida? (3)
¿Cuál es la forma correcta de inyectar una dependencia de acceso a datos cuando realizo una carga diferida?
Por ejemplo, tengo la siguiente estructura de clase
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
}
¿Cómo obtengo la referencia a _customer_dao en el objeto de transacción? Requirirlo para el constructor parece que realmente no tendría sentido si quiero que la transacción parezca un POCO. ¿Está bien que el objeto Transacción haga referencia directamente a la Inversión del Contenedor de Control? Eso también parece incómodo también.
¿Cómo manejan esto marcos como NHibernate?
No estoy muy familiarizado con el término POCO, pero las definiciones que he leído parecen en general seguir el espíritu del objeto siendo independiente de un marco más amplio.
Dicho esto, no importa cómo lo cortes, si realizas una inyección de dependencia, vas a tener colaboraciones con aquellas clases cuya funcionalidad está inyectada, y algo que depende de los objetos, ya sea una inyección completa. marco o simplemente alguna clase de ensamblador.
A mí me parece extraño inyectar una referencia a un contenedor del COI en una clase. Prefiero que mis inyecciones ocurran en el constructor con un código que se ve así:
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;
}
}
Por lo general, hago la inyección de dependencia en el constructor como lo hiciste anteriormente, pero tomo la carga perezosa un paso más allá al actuar solo cuando se llama al "obtener" como lo hago a continuación. No estoy seguro si este es el enfoque puro que está buscando, pero elimina el constructor "sucio" DI / Lazy Loading en 1 paso;)
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; }
}
}
Sugiero algo diferente ... Use una clase de carga perezosa:
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);
}
}
Una vez que lo obtiene, ya no necesita inyectar el dao en su objeto:
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
}
}
Al crear un objeto de Transcatación que no está vinculado a la base de datos:
new Transaction(new Customer(..)) // implicite cast
//from Customer to Lazy<Customer>..
Al regenerar una transacción de la base de datos en el repositorio:
public Transaction GetTransaction(Guid id)
{
custmerId = ... // find the customer id
return new Transaction(() => dao.GetCustomer(customerId));
}
Suceden dos cosas interesantes: - Sus objetos de dominio se pueden usar con o sin acceso a datos, se convierte en acceso de datos ignorante. El único pequeño giro es permitir pasar una función que da el objeto en lugar del objeto en sí. - La clase Lazy es internamente mutable pero puede usarse como un valor inmutable. La palabra clave readonly conserva su semántica, ya que su contenido no se puede cambiar de forma externa.
Cuando desee que el campo sea de escritura, simplemente elimine la palabra clave de solo lectura. cuando se asigna un nuevo valor, se creará un nuevo Lazy con el nuevo valor debido al lanzamiento implícito.
Editar: publiqué sobre esto aquí:
http://www.thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance