24 dic 2010

Unity - Pasar cadenas de conexión al constructor

Actualmente me encuentro desarrollando la arquitectura de un sistema orientado al dominio (Domain Driven Design), bajo C# en Visual Studio 2010, utilizando el nuevo Entity Framework 4. Estoy utilizando Unity como resolvedor e inyectador de dependencias. Ahora bien, como expliqué en posts pasados, utilicé las plantillas T4 para generar mis entidades y mi contexto de conexión. Estaba trabajando instanciando en cada consulta al contexto que me generaba el EF. Cuando hablo de contexto, me refiero a la clase parcial cuyo nombre lo toma en base al nombre que ingresamos cuando hicimos click en [Add Code Generation Item...].
Básicamente borré esa clase e hice que mis repositorios heredaran directamente de ObjectContext. Un ejemplo de una clase repositorio es la siguiente:
  
namespace mtcholakian.samples.TestDDD.Data.Repositories
{
public class CustomerRepository : GenericRepository<Customer>, ICustomerRepository
{
public CustomerRepository(string connectionString)
: base(connectionString)
{
}

}
}


Si observan en el constructor de la clase, estamos esperando una cadena de conexión. Esta cadena la tomo desde el App.Config o archivo de configuración. Ahora bien, el contenedor de Unity es el que me devuelve una instancia de CustomerRepository, pero cómo hago para pasarle ese parámetro de conexión? Fácil, nuestro App.Config tendrá que lucir de la siguiente manera:

  













Aquí se le dice que el valor del connectionString es "SampleDbEntities", lo cual es el nombre del key de la sección connectionString que va a estar declarada dentro del App.Config también.
Ahora, una cosa más y lo más interesante de todo es que tenemos un atributo typeConverter. Lo que nos dice es que al pasarle al constructor el parámetro connectionString lo resuelva esa clase, que tenemos que definir más arriba de la siguiente forma por ejemplo:

  



Y ahora simplemente vemos la definición en sí de la clase:

  
namespace mtcholakian.samples.TestDDD.Utils
{
public class ConnectionStringTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return ConfigurationManager.ConnectionStrings[value.ToString()].ConnectionString;
}
}
}


Lo que hace es recibir un valor, en nuestro caso será el que definimos en el parámetro param, "SampleDBEntities" y en base a ese string devuelve la entrada de connectionStrings del App.Config que contiene la conexión a la base de datos.
Listo!
Una cosa muy importante es que la declaración de los tipos del unity aplica únicamente a Unity 2.0, no a la versión 1.4, ya que utilizaba un elemento typeConfig del tipo TypeInjectionElement que no viene más a partir de la versión 2.0.
Espero les haya sido útil.
Saludos,

Mike

9 dic 2010

Unity - Inyectar dependencias genéricas

Siguiendo con mis entradas anteriores y con la implementación de mi patrón Repositorio, se me ocurrió inyectar una dependencia en el constructor de una clase.
Unity resuelve en forma automática las dependencias en un constructor, lo cual significa que no tenemos que declarar ese parámetro explícitamente dentro de la sección <types>. En mi caso estoy declarando los mapeos en el archivo de configuración (App.Config), entonces cuando el contenedor detecte esa dependencia en el constructor, automáticamente le inyectará la implementación que hayamos declarado.
Lo interesante es cómo inyectar una dependencia y que esta además sea genérica.
Paso a detallar mi clase en cuestión:
    

public class CustomerRepository : GenericRepository<Customer>, ICustomerRepository
{
//la interfaz genérica es la que el contenedor de Unity debe resolver
public CustomerRepository(IContext<Customer> context) : base(context)
{
}

}


Nuestro archivo de configuración tendrá un aspecto similar al siguiente:


<typeAlias alias="IContext`1" type="mtcholakian.samples.TestDDD.Business.Core.IContext`1,Business.Core"/>
<typeAlias alias="Context`1" type="mtcholakian.samples.TestDDD.Data.Context.Context`1,Data"/>


Observen cómo se declara un generics y luego la clase que lo implementa.
Finalmente dentro de nuestro container declaramos el mapeo:
    
<container name="RootContainer">
<types>
<type type="IContext`1" mapTo="Context`1" name=""></type>
</types>
</container>


Luego, cuando desde la aplicación instanciemos CustomerRepository obtendremos una instancia en el constructor con la clase indicada.
Espero les haya sido útil.
Saludos,
Mike

5 dic 2010

Entity Framework 4 - Cómo obtener un objeto por su primary key

Implementando mi patrón Repository, me encontré con un problema al tratar de hacer un método del estilo GetObjectById(int id), que básicamente devolvería un objeto a partir de su clave primaria en la base de datos.
Por más que revisé toda la web, no encontré la solución, así que luego de estar un largo rato probando voy a plasmar aquí la forma de resolverlo.
Básicamente lo que hago es un método donde le paso un array de objects, ya que la clave primaria en la BD puede estar formada por varios campos, donde cada elemento es una clave.
Aquí el método en cuestión:


public T GetEntityById(params object[] keys)
{
using (SampleDBEntities context = new SampleDBEntities(SampleDBEntities.ConnectionString))
{
EntitySet en = context.CreateObjectSet<T>().EntitySet;
EntityKeyMember[] entityKeys = new EntityKeyMember[en.ElementType.KeyMembers.Count];

for(int i=0;i < en.ElementType.KeyMembers.Count;i++)
{
entityKeys[i] = new EntityKeyMember();
entityKeys[i].Key = en.ElementType.KeyMembers[i].Name;
entityKeys[i].Value = keys[i];
}

EntityKey entityKey = new System.Data.EntityKey { EntityContainerName = en.EntityContainer.Name, EntityKeyValues = entityKeys, EntitySetName = en.Name };
object outObject = null;

context.TryGetObjectByKey(entityKey, out outObject);

return (T)outObject;
}
}



Luego desde un main podrían usarlo de la siguiente forma:


static void Main(string[] args)
{
//aquí uso Unity para resolver implementaciones
ICustomerRepository repo = ProxyGenerator.GetImplementation<ICustomerRepository>("CustomerMappging");

//obtengo el cliente por su primary key

Customer customer = repo.GetCustomerById(1);
}


Espero que haya sido útil.
Saludos,
Mike