C#

[C#] Microsoft Unity II – Iniciando con Unity

Posted on Actualizado enn

Post de la serie:

Hola, en el anterior post (dale una mirada acá) se habló sobre las ventajas y características de utilizar Inyección de Dependencias, sin embargo no vimos nada de código, por lo que hoy si vamos a tirar algunas lineas para ir mirando su funcionamiento.

En el post anterior comentaba que uno de los principios básico de la DI es trabajar contra abstracciones y no contra implementaciones, y en resumen podemos decir que vamos a trabajar contra interfaces y no contra clases, no voy a entrar en detalle sobre el cómo funcionan las interfaces (lo podemos tratar en otro post), así que solo vamos a ver un pequeño ejemplo en donde tenemos una clase que se encarga de guardar en un log (en los ejemplos voy a estar utilizando ASP.NET MVC, pero aplican a cualquier tipo de proyecto):

public class LogFile
{
	public void Log(string msg)
	{ 
		//TODO: Log the msg
	}
}
public class HomeController : Controller
{
	private readonly LogFile log;

	public HomeController()
	{
		log = new LogFile();
	}

	public ActionResult Index()
	{
		log.Log("Action: Index");
		return View();
	}
}

Revisando el código anterior, es claro que nuestro HomeController que no es más que una clase depende totalmente de la clase LogFile, en este caso se dice que tenemos un fuerte acoplamiento, ya que la clase HomeController conoce totalmente a LogFile, bueno pero vamos ahora a reescribir un poco el código para trabajar contra interfaces y tener un poco menos de acoplamiento; lo primero es crear una interfaz ILog para que sea implementada por la clase LogFile:

public interface ILog
{
	void Log(string msg);
}
public class LogFile : ILog
{
	public void Log(string msg)
	{ 
		//TODO: Log the msg
	}
}

Bueno, hasta el momento solo ha sido un pequeño cambio, ahora es momento de cambiar HomeController para trabajar contra la interfaz y no con la clase específica:

public class HomeController : Controller
{
	private readonly ILog log;

	public HomeController(ILog log)
	{
		this.log = log;
	}

	public ActionResult Index()
	{
		log.Log("Action: Index");
		return View();
	}
}

Si ahora revisamos HomeController, por ningún lado aparece la clase LogFile, y HomeController solo conoce la interfaz ILog, es decir solo tiene conocimiento de lo que hace más no de cómo lo hace, y otro punto importante, en el constructor de la clase le inyectamos la dependencia. Si en este punto probamos la aplicación vamos a obtener un error, y por el momento les dejo dos posibles soluciones Creando una factoría de controladores personalizada e Inyectando dependencias con Microsoft Unity.

Bueno pero hasta el momento nada de Unity…y es acá donde iniciamos a hablar de él. Unity es una herramienta que se conoce como un DI Container o un contenerdo de inyección de dependencias (existen muchos otros bastante buenos), y la idea básicamente es tener un sitio componente especializado en donde registremos todas las dependencias y como se van a resolver, además se va a encargar de manejar el tiempo de vida de cada componente registrado (lo trataremos en otro post de la serie) entre otros.

Ahora manos a la obra, lo primero es añadir la referencia a Unity mediante Nuget, en este caso buscamos por Unity y escogemos Unity bootstrapper for ASP.NET MVC:

unity II - img I

El paquete anterior añade un par de clases en el folder App_Start:

  • UnityConfig: Acá se van a registrar las dependencias.
  • UnityMvcActivator: Es llamada cuando se inicia la aplicación (ya que es del tipo WebActivator) y se encarga de crear el contenedor de Unity.

Luego de tener ya listo nuestro contenedor, el siguiente paso es comenzar a registrar nuestras dependencias, pero antes de hacerlo revisemos tres métodos (que hacen parte del ciclo de ejecución) claves en Unity:

  • RegisterType: Es utilizado para relacionar o definir el mapping entre la interfaz y el tipo concreto que se va a utilizar, la sintaxis básica es container.RegisterType<Interfaz,TipoConcreto>(); en otro post de la serie revisaremos las otras formas de registrar y crear los mappings.
  • Resolve: Es utilizado para obtener el tipo concreto de una interfaz, la sintaxis es: container.Resolve<Interfaz>();
  • Dispose: Se encarga de liberar recursos.

Ahora para registrar las dependencias, vamos a la clase UnityConfig, más exactamente en el método RegisterTypes y allí realizamos el registro de la dependencia, para el ejemplo solo será para la interfaz ILog, por el momento la clase debe verse algo así:

public class UnityConfig
{
	#region Unity Container
	private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
	{
		var container = new UnityContainer();
		RegisterTypes(container);
		return container;
	});

	/// <summary>
	/// Gets the configured Unity container.
	/// </summary>
	public static IUnityContainer GetConfiguredContainer()
	{
		return container.Value;
	}
	#endregion

	/// <summary>Registers the type mappings with the Unity container.</summary>
	/// <param name="container">The unity container to configure.</param>
	/// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
	/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
	public static void RegisterTypes(IUnityContainer container)
	{
		// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
		// container.LoadConfiguration();

		// TODO: Register your types here
		container.RegisterType<ILog, LogFile>();
	}
}

Ahora si podemos ejecutar la aplicación, y en este caso podemos ver que la dependencia se resuelve correctamente:

unity II - img II

Espero les haya gustado el post, en el siguente post ya vamos a entrar más en detalle en otras características de Unity!

Y no te olvides compartir el post!

Anuncios

[C#] Microsoft Unity I – Introducción a la inyección de dependencias

Posted on Actualizado enn

Post de la serie:

Hola, inicio una serie de post sobre la inyección de dependencias, la inyección de dependencias conocida por las siglas DI por su nombre en inglés Dependency Inyection, es un patrón de diseño altamente utilizado en aplicaciones medianas/grandes que requieren un ciclo de vida especial durante todo su proceso de desarrollo y mantenimiento, en este primer post no vamos a ver código (al menos no mucho), ya que quiero hablar del por qué DI es importante y que beneficios obtenemos al utilizarla.

Durante el proceso de desarrollo de software y espero que no sea tu caso (a mi si que me ha pasado) es posible que algunos requerimientos cambien o bien que se añadan nuevos, esto puede ser dado no solo por el cliente, dichos cambios pueden venir de negocio, o de la misma área de tecnología… sin importar el origen del cambio, lo importante es que nuestro sistema este preparado para asumir esos cambios y no se genere un impacto muy fuerte en el proyecto, claro que también depende del tipo del requerimiento, pero en general esa es la idea.

Para que nuestro sistema este preparado para asumir cambios (entre otras cosas) es importante tener una arquitectura extensible y modular que permita ser ágiles en el momento de responder a nuevas necesidades/requerimientos que vayan llegando, y es allí donde la inyección de dependencias juega un papel importante.

La inyección de dependencias ofrece algunos beneficios como:

  • Mantenibilidad: Cuando el sistema comienza a crecer tanto en tamaño como en funcionalidad, se va complicando el modificar/arreglar alguna funcionalidad, ya que cualquier cambio puede afectar múltiples partes del sistema o bien ser necesario realizar el mismo cambio en diferentes partes (si, creo que te has dado que has estado repitiendo código) con lo cual es engorroso realizar estos cambios y el sistema es muy sensible a introducir nuevos bugs; con la inyección de dependencias y un diseño adecuado del sistema las tareas de mantenimiento se vuelven más sencillas y es menos probable que se inyecten nuevos bugs o que haga falta replicar el cambio en alguna parte del código, ya que se tiene claro que componente hace que cosa, y aquí encontramos un principio clave de la DI, trabaja contra abstracciones y no contra implementaciones, y que quiere decir esto? Básicamente que debes trabajar contra interfaces (la abstracción) y no con la clase concreta (la implementación) así tu sistema va a funcionar con cualquier clase que implemente la interfaz que requieres.
  • Testeabilidad: Uno de los puntos en mi opinión más importante del usar DI, es su facilidad para testear componentes del sistema, y esto va muy de la mano con TDD (Test Driven Development), en donde la idea es poder tener un set de pruebas unitarias para probar/validar la funcionalidad de nuestro sistema, así al hacer algún cambio en el desarrollo, es posible identificar tempranamente si se inyecto algún bug en el sistema gracias al todo el conjunto de pruebas unitarias que al menos deberían validar el core y/o partes principales del sistema. Este tema de pruebas y sobre todo de TDD es bastante amplio e interesante, así que te recomiendo le des una buena revisada.
  • Extensibilidad: Siempre va a ser necesario seguir añadiendo funcionalidad a una aplicación, y gracias a la abstración sobre los componentes específicos sería posible por ejemplo cambiar de un caché local a implementar un cache de segundo nivel con AppFabric o un Memcached.
  • Poco acoplamiento: Otro punto bastante interesante, la idea de tener poco acoplamiento entre componentes permita facilmente cambiar la implementación de uno de ellos o reemplazarlo por otro, adicionalmente ayuda en la testeabilidad de los mismos, ya que al ser poco dependientes de otros sistemas/elementos podemos realizar pruebas sobre ellos de una manera relativamente sencilla.
  • Desarrollo en equipo: Cuando el equipo de desarrollo comienza a crecer, es cada vez más importante distribuir requerimientos a cada miembro del equipo de desarrollo, y gracias a que se tienen componentes desacoplados, es sencillo dividir al equipo para que cada miembro se enfoque en una parte específica del sistema, además, si recordamos la idea de trabaja contra abstracciones y no contra implementaciones, cada componente del sistema va a conocer que hace el otro (por medio de un contrato, es decir la interfaz) pero no le interesa como lo haga.

Luego de conocer algunos de los beneficios de implementar inyección de dependencias, es momento de hablar el cómo podemos usarla. Existen tres formas en las cuales podemos inyectar una dependencia:

  • Inyección por método
  • Inyección por constructor
  • Inyección por propiedades

Espero esta pequeña introducción les sea tanto útil como interesante, nos vemos en el siguiente post donde ya vamos a comenzar a hablar y utilizar Microsof Unity.

Y no te olvides compartir el post!

Saludos.

[C#] Custom value resolver en AutoMapper

Posted on Actualizado enn

Hola, anteriormente hicimos una introducción a AutoMapper, vimos algunos conceptos básicos para rápidamente ponernos a tono con dicha herramienta y finalmente comente algunas características importantes y muy comúnmente utilizadas, y como podrán adivinar quedaron algunos temas por fuera, así que en esta oportunidad vamos a revisar una de ellas, los Value Resolver.

En esencia podemos crear un Value Resolver personalizado para especificar un mapeo diferente al por defecto (propiedad origen a propiedad destino) para cumplir con necesidades específicas, así que en esta oportunidad vamos a personalizar la asignación a la propiedad Name de la clase ClientViewModel. Así que vamos a cambiar un poco la clase ClientViewModel, donde eliminamos la propiedad LastName, así nuestra clase ahora solo tiene dos propiedades, Name y Email:

public class ClientViewModel
{
	public string Name { get; set; }

	public string Email { get; set; }
}

La concatenar las propiedades Name y LastName de la clase Cliente y ese valor será el asignado para el Name de ClientViewModel, así que vamos a crear un Value Resolver que nos ayude con esa tarea, crear un Value Resolver personalizado es tan simple como tener una clase que herede de ValueResolver y sobre-escribir el método ResolveCore. Así que creamos una clase llamada FullNameResolver que hereda de ValueResolver y definimos el source (la clase cliente original) y el tipo de la salida (un string en este caso), y en el override del método ResolveCore simplemente concatenamos las propiedades y retornamos el valor:

public class FullNameResolver : ValueResolver<Client, string>
{
	protected override string ResolveCore(Client source)
	{
		return string.Format("{0} {1}",source.Name, source.LastName);
	}
}

Ya que se ha creado el resolver, solo resta usarlo, así que vamos a nuestra clase CustomDto y allí en el método Configure en la definición del mapeo de la propiedad Name llamamos el resolver creado con la función ResolveUsing y le pasamos el FullNameResolver creado anteriormente:

protected override void Configure()
{
	Mapper.CreateMap<Client, ClientViewModel>()
		.ForMember(d => d.Name, o => o.ResolveUsing<FullNameResolver>())
		.ForMember(d => d.Email, o => o.MapFrom(c => c.Email));
}

Por último una prueba:

ValueResolver

Hasta la próxima, saludos!

[C#] AutoMapper desde ceros

Posted on Actualizado enn

Hola a todos, hoy vamos a ver una pequeña guía sobre AutoMapper, iniciando desde cómo instalarlo  y mirando algunas de sus características.

AutoMapper básicamente es una herramienta que permite realizar un mapeo de un objeto a otro ahorrandonos gran cantidad de código y que provee algunas utilidades para personalizar cómo se realiza dicho mapeo entre objetos.

Realizar mapeo entre objetos en una funcionalidad común en muchos de nuestros desarrollos, ya que permiten personalizar la salida de los datos evitando revelar información confidencial y dando al cliente solo la información que él realmente necesita, basandonos en dicha afirmación, pensemos en una clase (modelo) Cliente que tiene campos como Id, nombre, apellido, edad, email, fecha de creación y clave entre otros, y ahora tenemos alguna funcionalidad que expone la información del cliente, en este caso estamos revelando información que no deberiamos entregar como la fecha de creación y la clave, en ese caso lo que podemos hacer es crear una nueva clase (conocída como ViewModel) con solo los campos que vamos a exponer; y aquí es donde AutoMapper juega un papel importante ya que nos va a ayudar a pasar de nuestro modelo/clase Cliente a nuestro ViewModel ClienteViewModel de una forma bastante sencilla y robusta.

Para ver como funciona AutoMapper vamos a partir de una aplicación Web con ASP.NET MVC, sin embargo es posible usarlo en cualquier otro tipo de aplicación.

Lo primero es la instalación, y como podrán adivinar tan solo debemos añadir AutoMapper haciendo uso de Nuget:

AutoMapper

Ahora vamos a crear el modelo, en este caso la clase Client:

public class Client
{
	public int ClientId { get; set; }

	public string Name { get; set; }

	public string LastName { get; set; }

	public string Email { get; set; }

	public string CreationDate { get; set; }

	public string Password { get; set; }
}

Como ya mencionamos, necesitamos un ViewModel, que no es más que otra clase con el nombre ClientViewModel:

public class ClientViewModel
{
	public string Name { get; set; }

	public string LastName { get; set; }

	public string Email { get; set; }
}

Ahora, vamos a crear una sencilla clase que va a actuar como repositorio y va a retornar un cliente (realmente un ClientViewModel) dado su id:

public class ClientRepository
{
	private readonly List<Client> clients = new List<Client>() 
	{ 
		new Client(){ 
			ClientId = 1, 
			Name = "Julio", 
			LastName = "Avellaneda", 
			Email="julito_gtu@hotmail.com", 
			CreationDate = DateTime.Now, 
			Password = "123456"}
	};

	public ClientViewModel GetById(int id)
	{
		//Get the client
		var client = clients[0];
		//Define the mapping
		AutoMapper.Mapper.CreateMap<Client, ClientViewModel>();
		//Execute the mapping
		var clientViewModel = AutoMapper.Mapper.Map<Client, ClientViewModel>(client);
		//Return a viewmodel
		return clientViewModel;
	}
}

En el método anterior hacemos uso del método CreateMap de AutoMapper.Mapper para definir el mapeo de objetos, el primer parámetro es el objeto original (para el ejemplo Client) y el segundo parámetro es el objeto al cual se va a mapear (en este caso ClientViewModel), luego para que la magia funcione usamos el método Map para que se conviertan los objetos.

En el ejemplo anterior, AutoMapper realiza el mapeo entre las propiedades que tengan el mismo nombre y tipo de dato entre el modelo original y el ViewModel, razón por la cual poco hemos realizado en el método, sin embargo dicha premisa no se cumple siempre, ya que no en todos los casos las propiedades tienen el mismo nombre y puede existir alguna lógica adicional que se requiera implementar.

La siguiente imagen lo muestra claramente:

mappervm

Para casos más complejos que no cumplen la premisa básica del caso anterior, AutoMapper tiene una característica conocida como perfiles, y allí podemos definir un comportamiento más personalizado para los mapeos, para definir un perfil tenemos tres sencillos pasos:

  1. Crear una clase que herede de Profile (AutoMapper.Profile)
  2. Sobreescribir la propiedad ProfileName (Nombre del perfil)
  3. Sobreescribir el método Configure (Lógica del mapeo)

Entonces vamos a crear la clase CustomDto y realizamos los tres pasos anteriores, como nombre del perfil (propiedad ProfileName) definimos CustomDto, y por el momento vamos a dejar sin lógica el método Configure:

public class CustomDto : Profile
{
	public override string ProfileName
	{
		get
		{
			return "CustomDto";
		}
	}

	protected override void Configure()
	{
		
	}
}

Ahora vamos a centrarnos en el método Configure, de nuevo hacemos uso de AutoMapper.Mapper.CreateMap con objeto origen y objeto destino, y ahora para la personalización de como será llevado a cabo el mapeo entre los objetos, para ello hacemos uso del método ForMember, entonces para el ejemplo que veníamos trabajando el método Configure sería (por el momento no vamos a personalizar nada):

protected override void Configure()
{
	Mapper.CreateMap<Client, ClientViewModel>()
		.ForMember(d => d.Name, o => o.MapFrom(c => c.Name))
		.ForMember(d => d.LastName, o => o.MapFrom(c => c.LastName))
		.ForMember(d => d.Email, o => o.MapFrom(c => c.Email));
}

Ya que tenemos el perfil listo, ahora debemos llamarlo, y un buen lugar para hacerlo es en el evento Application_Start del Global.asax:

protected void Application_Start()
{
	AreaRegistration.RegisterAllAreas();
	FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
	RouteConfig.RegisterRoutes(RouteTable.Routes);
	BundleConfig.RegisterBundles(BundleTable.Bundles);

	AutoMapper.Mapper.Initialize(c => c.AddProfile(new CustomDto()));  
}

Ahora vamos a crear un nuevo método en la clase repositorio con el nombre GetByIdWithProfile para ver que para usar el perfil simplemente eliminamos la definición del mapeo:

public ClientViewModel GetByIdWithProfile(int id)
{
	//Get the client
	var client = clients[0];
	//Execute the mapping
	var clientViewModel = AutoMapper.Mapper.Map<Client, ClientViewModel>(client);
	//Return a viewmodel
	return clientViewModel;
}

Y si volvemos a probar efectivamente funciona sin problemas:

mappervm2

Hasta el momento se ha usado la funcionalidad por defecto de AutoMapper, así que vamos con el primer caso, en donde vamos a controlar cuando el valor de la propiedad origen es null, primero que pasa si por ejemplo la propiedad LastName no tiene un valor:

mappervmnull

Como podemos ver, la propiedad del ViewModel también tendrá el valor, así que por alguna regla de negocio, cuando el cliente no tiene un valor en LastName se debe mostrar el texto “-“, dicha regla de negocio la podriamos implementar en nuestra capa de UI o validando el valor de la propiedad antes de retornar el objeto… o aún mejor, en la definición del mapeo de AutoMapper, así que volvemos al método Configure y hacemos uso de NullSubstitute:

protected override void Configure()
{
	Mapper.CreateMap<Client, ClientViewModel>()
		.ForMember(d => d.Name, o => o.MapFrom(c => c.Name))
		.ForMember(d => d.LastName, o => o.NullSubstitute("-"))
		.ForMember(d => d.Email, o => o.MapFrom(c => c.Email));
}

Y probando de nuevo:

mappervmnullsub

Otro caso típico, se da cuando necesitamos trabajar con colecciones de datos en lugar de 1 solo objeto, en este caso prácticamente es transparente el cambio, basta solo con especificar en el método Map que por ejemplo usaremos un IEnumerable, para mostrarlo de nuevo creamos un nuevo método en nuestra clase repositorio:

public IEnumerable<ClientViewModel> GetAll()
{
	//Execute the mapping
	var clientViewModel = AutoMapper.Mapper
						.Map<IEnumerable<Client>, IEnumerable<ClientViewModel>>(clients);
	//Return a viewmodel
	return clientViewModel;
}

Y de nuevo probando:

mappervmlist

Y por el momento vamos a dejar aquí, la idea del post era dar una rápida introducción a AutoMapper y creo que ha sido así, en próximos post veremos otras características de esta poderosa herramienta.

Saludos.

[ASP.NET MVC] Creando una factoria de controladores personalizada

Posted on Actualizado enn

Hola a todos, como ya sabemos, un controlador en ASP.NET MVC no es más que una clase, la cual hereda de la clase Controller, sin embargo dicha clase no tiene un constructor definido, o bueno no al menos uno que podamos ver, dicho esto cuando una clase no tiene un constructor definido (o varios) por defecto si se tiene un constructor para esa clase que no recibe parámetros, ahora bien, en MVC tenemos algo que se conoce como factoría de controladores, cuya principal función es inicializar los controladores usando ese constructor vacío que todos tienen…y de que va todo esto? simple, que pasa cuando tenemos un controlador pero este requiere un constructor personalizado? Por ejemplo, si tenemos el siguiente controlador con un constructor personalizado:

public class HomeController : Controller
{
	private string username = string.Empty;
	public HomeController(string username)
	{
		this.username = username;
	}
}

Ahora si intentamos ejecutar, vamos a obtener el siguiente error:

controllererror

El error anterior se da porque el controlador no tiene un constructor sin parámetros por lo tanto no es posible inicializarlo. Pero y el constructor vacío que tienen todas las clases? Bueno, pues resulta que eso solo aplica cuando no se ha definido ningún constructor en la clase, y como nuestro controlador le definimos uno el otro ahora si desaparece.

Debido al problema anterior, necesitamos definir una nueva forma de inicializar el controlador HomeController y pasarle el parámetro que requiere, y para ello podemos crear una factoría personalizada, básicamente debemos crear una nueva clase que implemente la interfaz IControllerFactory, y en la función CreateController definimos como inicializar el controlador anterior.

Por organización vamos a crear una nueva carpeta llamada Factory, y allí dentro una nueva clase con el nombre CustomControllerFactory que implementa IControllerFactory:

public class CustomControllerFactory : IControllerFactory
{
	public IController CreateController(RequestContext requestContext, string controllerName)
	{
		// TODO: Implement this method
		throw new NotImplementedException();
	}

	public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
	{
		// TODO: Implement this method
		throw new NotImplementedException();
	}

	public void ReleaseController(IController controller)
	{
		// TODO: Implement this method
		throw new NotImplementedException();
	}
}

Ya con la clase creada, vamos a iniciar a definir lógica para cada método, iniciemos con ReleaseController, cuya función es liberar recursos:

public void ReleaseController(IController controller)
{
	var disposable = controller as IDisposable;
	if (disposable != null)
		disposable.Dispose();
}

Luego vamos con GetControllerSessionBehavior, por el momento vamos a dejar el comportamiento por defecto:

public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
	return SessionStateBehavior.Default;
}

Y finalmente CreateController, donde vamos a especificar la nueva forma de instanciar el controlador que hemos modificado anteriormente:

public IController CreateController(RequestContext requestContext, string controllerName)
{
	if (controllerName.ToLower() == "home")
	{
		var controller = new HomeController("julitogtu");
		return controller;
	}

	var defaultFactory = new DefaultControllerFactory();
	return defaultFactory.CreateController(requestContext, controllerName);
}

Lo que hace el código anterior es validar si el controlador que se esta requiriendo en Home, en caso afirmativo lo instanciamos usando el constructor que hemos creado, en caso que no sea el controlador Home, entonces dejamos que la factoría por defecto haga su trabajo. Ahora el código completo de nuestra factoría:

public class CustomControllerFactory : IControllerFactory
{
	public IController CreateController(RequestContext requestContext, string controllerName)
	{
		if (controllerName.ToLower() == "home")
		{
			var controller = new HomeController("julitogtu");
			return controller;
		}

		var defaultFactory = new DefaultControllerFactory();
		return defaultFactory.CreateController(requestContext, controllerName);
	}

	public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
	{
		return SessionStateBehavior.Default;
	}

	public void ReleaseController(IController controller)
	{
		var disposable = controller as IDisposable;
		if (disposable != null)
			disposable.Dispose();
	}
}

Finalmente, para que la factoría funcione, la llamamos en el evento Application_Start de el Global.asax:

ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

Espero el ejemplo les sea interesante, saludos!

Usando caché con ASP.NET

Posted on Actualizado enn

Hola, hoy quiero mostrar como podemos usar la cache para tener nuestros datos en memoria y evitar hacer idas y vueltas a nuestra base de datos continuamente.
El proceso para utilizar la cache consta de 3 pasos:

  1. Buscar si existe un determinado elemento en la cache.
  2. Si el elemento existe entonces utilizarlo.
  3. Si el elemento no existe ir a la base de datos y cachear el elemento.

Vamos a revisar tres escenarios:

  1. Cache simple: Podemos utilizar la cache de manera similar a si usáramos una variable de session, simplemente verificamos si existe el elemento, en caso de existir lo usamos de lo contrario vamos a la base de datos y luego cacheamos el elemento. La desventaja de este método es que una vez cacheado el elemento, no es posible determinar si nuetsro origen de datos ha cambiado, lo cual podria generar datos inconsistente.El código siguiente muestra la implementación de este caso:
        
           If Cache("cache1") Is Nothing Then
                dt = oConexion.GetUsers()
                Cache("cache1") = dt
                grvUsuarios.DataSource = dt
                grvUsuarios.DataBind()
                Response.Write("Cache created at: " & Now)
            Else
                dt = CType(Cache("cache1"), DataTable)
                grvUsuarios.DataSource = dt
                grvUsuarios.DataBind()
                Response.Write("Cache used at: " & Now)
            End If
    
    Lo primero es verificar si el objeto existe en la cache, si no existe entonces obtenemos los datos de la base de datos con la función GetUsers() y luego cacheamos el elemento, así en una segunda oportunidad como ya se encuentra el objeto en la cache no se realiza de nuevo la consulta a la base de datos.
  2. Cache con tiempo de expiración: Al igual que con el caso anterior, para acceder a la cache lo hacemos de manera similar a como se accede a una variable de session, la diferencia radica en que cuando creamos el objeto en la cache le definimos un tiempo de vida, así cuando dicho tiempo se cumpla el objeto cacheado “muere” y tendriamos que volver a cachearlo; de esta manera estariamos refrescando regularmente nuestros objetos cacheados para tener siempre los datos reales de la base de datos, sin embargo se puede dar el caso q los datos puedan ser cambiados y que le tiempo de cache no se haya cumplido, pero ya veremos en el siguiente caso como podemos solucionar este caso.
        
            Dim Expires As DateTime
            Expires = DateAdd(DateInterval.Second, 10, Now)
    
            If Cache("cache2") Is Nothing Then
                dt = oConexion.GetUsers()
                Cache.Insert("cache2",
                             dt,
                             Nothing,
                             Expires,
                             System.Web.Caching.Cache.NoSlidingExpiration,
                             System.Web.Caching.CacheItemPriority.Default,
                             Nothing)
                grvUsuarios.DataSource = dt
                grvUsuarios.DataBind()
                Response.Write("Cache created at: " & Now)
            Else
                dt = CType(Cache("cache2"), DataTable)
                grvUsuarios.DataSource = dt
                grvUsuarios.DataBind()
                Response.Write("Cache used at: " & Now)
            End If

    Lo primero es verificar si el objeto existe en la cache, si no existe entonces obtenemos los datos de la base de datos con la función GetUsers() y luego cacheamos el elemento, pero notese aca que el primer caso usamos Cache(“nombreitem”) y en este caso utilizamos Cache.Insert(…), lo que pasa es que cuando se utliza el método Insert(…) tenemos un mayor control y mayores funcionalidades al crear el objeto, con el método simple solo creamos el objeto en cache sin mayores características. En el ejemplo especificamos que el tiempo de vida del objeto en cache sea de 10 segundos, ya que en la línea Expires = DateAdd(DateInterval.Second, 10, Now) a la fecha actual le estamos diciendo que nos sume 10 segundos a la fechha y hora actuales.

  3. Cache con dependencia a SQL Server: Cuando utilizamos caché con dependencia a SQL Sever, lo que se logra es que nuetsra aplicación detecte automáticamente cuando se realiza algún cambio en la tabla origen, así podemos tener siempre en caché los datos actuales de nuestra base de datos. Para realizar este proceso primero debemos decirle a la base de datos que se va a utilizar para cachear con dependecia y luego le especificamos que tabla vamos a utilizar, con el primer proceso se crea una tabla llamada AspNet_SqlCacheTablesForChangeNotification para ir monitoreando los cambios realizados ademas de algunos procedimientos almacenado, al realizar el segundo proceso se crean algunos triggers sobre la tabla que vamos a cachear, la manera de realizar dicho proceso es por medio de la consola de comando ejecutar:
        
    aspnet_regsql -S NOMBRESERVIDOR\INSTANCIA -E -ed -d NOMBREBASE
    

    Allí lo único que se debe especificar es el nombreservidor por el servidor de base de datos y específicamos el nombre de la base de datos a utilizar.

        
    aspnet_regsql -S NOMBRESERVIDOR\INSTANCIA -E -d NOMBREBASE -et -t NOMBRETABLA
    

    En este segundo caso al final debemos especificar el nombre de la tabla que deseamos utilizar.
    Luego de tener nuestra base de datos lista, vamos al webconfig y especificamos:

        
        <caching>
          <sqlCacheDependency enabled="true">
            <databases>
              <add connectionStringName="MiCadena" name="AspCache" pollTime="500"/>
            </databases>
          </sqlCacheDependency>
        </caching>

    Ahora en nuestro código:

        
          If Cache("cache3") Is Nothing Then
                dt = oConexion.GetUsers()
                Dim scd As New SqlCacheDependency(databaseEntryName:="AspCache",
                                                                tableName:="Usuarios")
                Cache.Insert(key:="cache3",
                             value:=dt,
                             dependencies:=scd)
                grvUsuarios.DataSource = dt
                grvUsuarios.DataBind()
                Response.Write("Cache created at: " & Now)
            Else
                dt = CType(Cache("cache3"), DataTable)
                grvUsuarios.DataSource = dt
                grvUsuarios.DataBind()
                Response.Write("Cache used at: " & Now)
            End If
    

    Aca primero verificamos si existe el objeto, en caso negativo entonces llenamos un datatable con los datos, luego creamos un objeto SqlCacheDependency donde en el constructor especificamos el nombre de la base de datos y la tabla a utilizar, luego al crear el objeto en la cache le pasamos como parámetro el objeto creado anteriormente.

    Finalmente le dejo el código del ejemplo, y desde ahora los podrán descargar tanto en vb como en c#:
    Ejemplo en C# !
    Ejemplo en VB !