Construcción de una arquitectura MVC en tres capas con ASP.NET

1: Interfaz IBaseDAL

La interfaz base para el acceso a datos:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ArquitecturaTresCapas.IDAL
{
    public interface IRepositorioBase<t> where T : class
    {
        int GuardarCambios();
        void Agregar(T entidad);
        void Eliminar(T entidad);
        void EliminarPor(Expression<func bool="">> condicionEliminacion);
        void Modificar(T entidad, params string[] nombresPropiedades);
        IQueryable<t> Donde(Expression<func bool="">> expresionLambda);
        IQueryable<t> DondeOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true);
        IQueryable<t> DondeIncluir(Expression<func bool="">> expresionLambda, params string[] nombresPropiedadesIncluidas);
        IQueryable<t> DondeIncluirOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas);
        IEnumerable<t> DondePaginar<tkey>(int indicePagina, int tamanoPagina, out int totalRegistros, Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas);
        IQueryable<t> ConsultarPorSql(string consultaSql, params System.Data.SqlClient.SqlParameter[] parametros);
    }
}
</t></func></func></tkey></t></func></func></tkey></t></func></t></func></func></tkey></t></func></t></func></t>

Plantilla T4 generadora de interfaces:


<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs"#>
<#
CodeGenerationTools codigo = new CodeGenerationTools(this);
MetadataLoader cargador = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this); 
string archivoEntrada1 = @"R:\Proyectos\ArquitecturaTresCapas\Modelo\Modelo.edmx";  
EdmItemCollection ColeccionElementos1 = cargador.CreateEdmItemCollection(archivoEntrada1); 
string espacioNombres = codigo.VsNamespaceSuggestion(); 
EntityFrameworkTemplateFileManager gestorArchivos = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.Modelo;

namespace ArquitecturaTresCapas.IDAL
{ 
<# 
foreach (EntityType entidad in ColeccionElementos1.GetItems<entitytype>().OrderBy(e => e.Nombre))
{
#>
    public partial interface I<#=entidad.Nombre#>Repositorio : IRepositorioBase<<#=entidad.Nombre#>>{ }        
<#}#> 
}
</entitytype>

Código generado:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.Modelo;

namespace ArquitecturaTresCapas.IDAL
{ 
    public partial interface ICompraRepositorio : IRepositorioBase<compra>{ }        
    public partial interface IMenuRepositorio : IRepositorioBase<menu>{ }        
    public partial interface IProductoRepositorio : IRepositorioBase<producto>{ }        
    public partial interface IRolRepositorio : IRepositorioBase<rol>{ }        
    public partial interface IRolMenuRepositorio : IRepositorioBase<rolmenu>{ }        
    public partial interface IUsuarioRolRepositorio : IRepositorioBase<usuariorol>{ }        
    public partial interface IUsuarioRepositorio : IRepositorioBase<usuario>{ }        
 
}
</usuario></usuariorol></rolmenu></rol></producto></menu></compra>

2: Capa de Acceso a Datos (DAL)

Implementación base del repositorio:


using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;

namespace ArquitecturaTresCapas.DAL
{
    using ArquitecturaTresCapas.IDAL;
    using InyeccionDependencias;
    using System.Data.Entity;

    public class RepositorioBase<t> : IRepositorioBase<t> where T : class
    {
        private DbContext contexto;
        
        public RepositorioBase()
        {
            contexto = FactoriaContextoDbContext.ObtenerDbContext();
        }

        #region Guardar cambios en la base de datos
        public int GuardarCambios()
        {
            return contexto.SaveChanges();
        }
        #endregion

        #region Agregar entidad
        public void Agregar(T entidad)
        {
            contexto.Set<t>().Add(entidad);
        }
        #endregion

        #region Eliminar entidad
        public void Eliminar(T entidad)
        {
            DbEntityEntry entrada = contexto.Entry<t>(entidad);
            entrada.State = System.Data.Entity.EntityState.Deleted;
        }
        #endregion

        #region Eliminar por condición
        public void EliminarPor(Expression<func bool="">> condicionEliminacion)
        {
            var listaEliminacion = contexto.Set<t>().Where(condicionEliminacion);
            foreach (T entidad in listaEliminacion)
            {
                Eliminar(entidad);
            }
        }
        #endregion

        #region Modificar entidad
        public void Modificar(T entidad, params string[] nombresPropiedades)
        {
            DbEntityEntry entrada = contexto.Entry<t>(entidad);
            entrada.State = System.Data.Entity.EntityState.Unchanged;
            foreach (string nombrePropiedad in nombresPropiedades)
            {
                entrada.Property(nombrePropiedad).IsModified = true;
            }
        }
        #endregion

        #region Consultar entidades
        public IQueryable<t> Donde(Expression<func bool="">> expresionLambda)
        {
            return contexto.Set<t>().Where(expresionLambda);
        }
        #endregion

        #region Consultar con ordenamiento
        public IQueryable<t> DondeOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true)
        {
            if (ascendente)
                return contexto.Set<t>().Where(expresionLambda).OrderBy(selectorClave);
            else
                return contexto.Set<t>().Where(expresionLambda).OrderByDescending(selectorClave);
        }
        #endregion

        #region Consultar con include
        public IQueryable<t> DondeIncluir(Expression<func bool="">> expresionLambda, params string[] nombresPropiedadesIncluidas)
        {
            DbQuery<t> consultaDb = contexto.Set<t>();
            foreach (string nombreIncluir in nombresPropiedadesIncluidas)
            {
                consultaDb = consultaDb.Include(nombreIncluir);
            }
            return consultaDb.Where(expresionLambda);
        }
        #endregion

        #region Consultar con include y ordenamiento
        public IQueryable<t> DondeIncluirOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas)
        {
            DbQuery<t> consultaDb = contexto.Set<t>();
            if (nombresPropiedadesIncluidas != null && nombresPropiedadesIncluidas.Length > 0)
            {
                foreach (string nombreIncluir in nombresPropiedadesIncluidas)
                {
                    consultaDb = consultaDb.Include(nombreIncluir);
                }
            }
            IQueryable<t> consulta = consultaDb.Where(expresionLambda);
            if (ascendente)
                return consulta.OrderBy(selectorClave);
            else
                return consulta.OrderByDescending(selectorClave);
        }
        #endregion

        #region Consulta paginada con include y ordenamiento
        public IEnumerable<t> DondePaginar<tkey>(int indicePagina, int tamanoPagina, out int totalRegistros, Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas)
        {
            DbQuery<t> consultaDb = contexto.Set<t>();
            if (nombresPropiedadesIncluidas != null && nombresPropiedadesIncluidas.Length > 0)
            {
                foreach (string nombreIncluir in nombresPropiedadesIncluidas)
                {
                    consultaDb = consultaDb.Include(nombreIncluir);
                }
            }

            IOrderedQueryable<t> consultaOrdenada = null;
            if (ascendente) 
            { 
                consultaOrdenada = consultaDb.OrderBy(selectorClave); 
            }
            else 
            { 
                consultaOrdenada = consultaDb.OrderByDescending(selectorClave); 
            }
            
            var lista = consultaOrdenada.Where(expresionLambda).Skip((indicePagina - 1) * tamanoPagina).Take(tamanoPagina).ToList();
            totalRegistros = consultaOrdenada.Where(expresionLambda).Count();
            return lista;
        }
        #endregion

        #region Consulta por SQL
        public IQueryable<t> ConsultarPorSql(string consultaSql, params System.Data.SqlClient.SqlParameter[] parametros)
        {
            return contexto.Database.SqlQuery<t>(consultaSql, parametros).AsQueryable();
        }
        #endregion
    }
}
</t></t></t></t></t></func></func></tkey></t></t></t></t></func></func></tkey></t></t></t></func></t></t></t></func></func></tkey></t></t></func></t></t></t></func></t></t></t></t>

Plantilla T4 generadora de clases de reposiotrio:


<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs"#>
<#
CodeGenerationTools codigo = new CodeGenerationTools(this);
MetadataLoader cargador = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this); 
string archivoEntrada1 = @"R:\Proyectos\ArquitecturaTresCapas\Modelo\Modelo.edmx";  
EdmItemCollection ColeccionElementos1 = cargador.CreateEdmItemCollection(archivoEntrada1); 
string espacioNombres = codigo.VsNamespaceSuggestion(); 
EntityFrameworkTemplateFileManager gestorArchivos = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.IDAL;
using ArquitecturaTresCapas.Modelo;

namespace ArquitecturaTresCapas.DAL
{ 
<# 
foreach (EntityType entidad in ColeccionElementos1.GetItems<entitytype>().OrderBy(e => e.Nombre))
{
#>
    public partial class <#=entidad.Nombre#>Repositorio : RepositorioBase<<#=entidad.Nombre#>>,I<#=entidad.Nombre#>Repositorio{ }        
<#}#> 
}
</entitytype>

Código generado:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.IDAL;
using ArquitecturaTresCapas.Modelo;

namespace ArquitecturaTresCapas.DAL
{ 
    public partial class CompraRepositorio : RepositorioBase<compra>,ICompraRepositorio{ }        
    public partial class MenuRepositorio : RepositorioBase<menu>,IMenuRepositorio{ }        
    public partial class ProductoRepositorio : RepositorioBase<producto>,IProductoRepositorio{ }        
    public partial class RolRepositorio : RepositorioBase<rol>,IRolRepositorio{ }        
    public partial class RolMenuRepositorio : RepositorioBase<rolmenu>,IRolMenuRepositorio{ }        
    public partial class UsuarioRolRepositorio : RepositorioBase<usuariorol>,IUsuarioRolRepositorio{ }        
    public partial class UsuarioRepositorio : RepositorioBase<usuario>,IUsuarioRepositorio{ }        
 
}
</usuario></usuariorol></rolmenu></rol></producto></menu></compra>

3: Interfaz de Lógica de Negocio (IBLL)

Interfaz base para la lógica de negocio:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ArquitecturaTresCapas.IBLL
{
    public interface ILogicaNegocioBase<t> where T : class
    {
        int GuardarCambios();
        void Agregar(T entidad);
        void Eliminar(T entidad);
        void EliminarPor(Expression<func bool="">> condicionEliminacion);
        void Modificar(T entidad, params string[] nombresPropiedades);
        IQueryable<t> Donde(Expression<func bool="">> expresionLambda);
        IQueryable<t> DondeOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true);
        IQueryable<t> DondeIncluir(Expression<func bool="">> expresionLambda, params string[] nombresPropiedadesIncluidas);
        IQueryable<t> DondeIncluirOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas);
        IEnumerable<t> DondePaginar<tkey>(int indicePagina, int tamanoPagina, out int totalRegistros, Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas);
        IQueryable<t> ConsultarPorSql(string consultaSql, params System.Data.SqlClient.SqlParameter[] parametros);
    }
}
</t></func></func></tkey></t></func></func></tkey></t></func></t></func></func></tkey></t></func></t></func></t>

Plantilla T4 generadora de interfaces de lógica de negocio:


<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs"#>
<#
CodeGenerationTools codigo = new CodeGenerationTools(this);
MetadataLoader cargador = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this); 
string archivoEntrada1 = @"R:\Proyectos\ArquitecturaTresCapas\Modelo\Modelo.edmx";  
EdmItemCollection ColeccionElementos1 = cargador.CreateEdmItemCollection(archivoEntrada1); 
string espacioNombres = codigo.VsNamespaceSuggestion(); 
EntityFrameworkTemplateFileManager gestorArchivos = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.Modelo;

namespace ArquitecturaTresCapas.IBLL
{ 
<# 
foreach (EntityType entidad in ColeccionElementos1.GetItems<entitytype>().OrderBy(e => e.Nombre))
{
#>
    public partial interface I<#=entidad.Nombre#>LogicaNegocio : ILogicaNegocioBase<<#=entidad.Nombre#>>{ }        
<#}#> 
}
</entitytype>

Código generado:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.Modelo;

namespace ArquitecturaTresCapas.IBLL
{ 
    public partial interface ICompraLogicaNegocio : ILogicaNegocioBase<compra>{ }        
    public partial interface IMenuLogicaNegocio : ILogicaNegocioBase<menu>{ }        
    public partial interface IProductoLogicaNegocio : ILogicaNegocioBase<producto>{ }        
    public partial interface IRolLogicaNegocio : ILogicaNegocioBase<rol>{ }        
    public partial interface IRolMenuLogicaNegocio : ILogicaNegocioBase<rolmenu>{ }        
    public partial interface IUsuarioRolLogicaNegocio : ILogicaNegocioBase<usuariorol>{ }        
    public partial interface IUsuarioLogicaNegocio : ILogicaNegocioBase<usuario>{ }        
 
}
</usuario></usuariorol></rolmenu></rol></producto></menu></compra>

4: Lógica de Negocio (BLL)

Implementación base de la lógica de negocio:


using System;
using System.Collections.Generic;
using System.Linq;

namespace ArquitecturaTresCapas.BLL
{
    using ArquitecturaTresCapas.IBLL;
    using ArquitecturaTresCapas.IDAL;
    using System.Data.SqlClient;

    public partial class LogicaNegocioBase<t> : ILogicaNegocioBase<t> where T : class
    {
        protected IRepositorioBase<t> repositorioBase;
        
        public void Agregar(T entidad)
        {
            repositorioBase.Agregar(entidad);
        }

        public void Eliminar(T entidad)
        {
            repositorioBase.Eliminar(entidad);
        }

        public void EliminarPor(Expression<func bool="">> condicionEliminacion)
        {
            repositorioBase.EliminarPor(condicionEliminacion);
        }

        public void Modificar(T entidad, params string[] nombresPropiedades)
        {
            repositorioBase.Modificar(entidad, nombresPropiedades);
        }

        public IQueryable<t> ConsultarPorSql(string consultaSql, SqlParameter[] parametros)
        {
            return repositorioBase.ConsultarPorSql(consultaSql, parametros);
        }

        public int GuardarCambios()
        {
            return repositorioBase.GuardarCambios();
        }

        public IQueryable<t> Donde(Expression<func bool="">> expresionLambda)
        {
            return repositorioBase.Donde(expresionLambda);
        }

        public IQueryable<t> DondeIncluir(Expression<func bool="">> expresionLambda, params string[] nombresPropiedadesIncluidas)
        {
            return repositorioBase.DondeIncluir(expresionLambda, nombresPropiedadesIncluidas);
        }

        public IQueryable<t> DondeIncluirOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas)
        {
            return repositorioBase.DondeIncluirOrdenarPor<tkey>(expresionLambda, selectorClave, ascendente, nombresPropiedadesIncluidas);
        }

        public IQueryable<t> DondeOrdenarPor<tkey>(Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true)
        {
            return repositorioBase.DondeOrdenarPor<tkey>(expresionLambda, selectorClave, ascendente);
        }

        public IEnumerable<t> DondePaginar<tkey>(int indicePagina, int tamanoPagina, out int totalRegistros, Expression<func bool="">> expresionLambda, Expression<func tkey="">> selectorClave, bool ascendente = true, params string[] nombresPropiedadesIncluidas)
        {
            return repositorioBase.DondePaginar<tkey>(indicePagina, tamanoPagina, out totalRegistros, expresionLambda, selectorClave, ascendente, nombresPropiedadesIncluidas);
        }
    }
}
</tkey></func></func></tkey></t></tkey></func></func></tkey></t></tkey></func></func></tkey></t></func></t></func></t></t></func></t></t></t>

Plantilla T4 generadora de clases de lógica de negocio:


<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs"#>
<#
CodeGenerationTools codigo = new CodeGenerationTools(this);
MetadataLoader cargador = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this); 
string archivoEntrada1 = @"R:\Proyectos\ArquitecturaTresCapas\Modelo\Modelo.edmx";  
EdmItemCollection ColeccionElementos1 = cargador.CreateEdmItemCollection(archivoEntrada1); 
string espacioNombres = codigo.VsNamespaceSuggestion(); 
EntityFrameworkTemplateFileManager gestorArchivos = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArquitecturaTresCapas.Modelo;
using ArquitecturaTresCapas.IBLL;
using ArquitecturaTresCapas.IDAL;
namespace ArquitecturaTresCapas.BLL
{ 
<# 
foreach (EntityType entidad in ColeccionElementos1.GetItems<entitytype>().OrderBy(e => e.Nombre))
{
#>
    public partial class <#=entidad.Nombre#>LogicaNegocio : LogicaNegocioBase<<#=entidad.Nombre#>>, I<#=entidad.Nombre#>LogicaNegocio
    { 
        I<#=entidad.Nombre#>Repositorio repositorio;
        public <#=entidad.Nombre#>LogicaNegocio(I<#=entidad.Nombre#>Repositorio repositorio){
            this.repositorio = repositorio; 
            this.repositorioBase = repositorio;
        }
    }        
<#}#> 
}
</entitytype>

Código generado:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ArquitecturaTresCapas.BLL
{
    using ArquitecturaTresCapas.Modelo;
    using ArquitecturaTresCapas.IBLL;
    using ArquitecturaTresCapas.IDAL;
    public partial class RolLogicaNegocio : LogicaNegocioBase<rol>, IRolLogicaNegocio
    {
        IRolRepositorio repositorio;
        public RolLogicaNegocio(IRolRepositorio repositorio) {
            this.repositorio = repositorio;
            this.repositorioBase = repositorio;
        }
    }
</rol>

5: Configuración de AutoFac

Archivo de configuraicón de AutoFac:


using Autofac;
using Autofac.Integration.Mvc;
using System.Reflection;

namespace AplicacionWeb
{
    public class ConfiguracionAutoFac
    {
        public static void Registrar()
        {
            var constructor = new ContainerBuilder();
            
            Assembly ensamblajeControladores = Assembly.Load("AplicacionWeb");
            constructor.RegisterControllers(ensamblajeControladores);

            Assembly ensamblajeRepositorios = Assembly.Load("ArquitecturaTresCapas.DAL");
            constructor.RegisterTypes(ensamblajeRepositorios.GetTypes()).AsImplementedInterfaces();

            Assembly ensamblajeLogicaNegocio = Assembly.Load("ArquitecturaTresCapas.BLL");
            constructor.RegisterTypes(ensamblajeLogicaNegocio.GetTypes()).AsImplementedInterfaces();

            var contenedor = constructor.Build();
            System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(contenedor));
        }
    }
}

Registro en Global.asax:


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

6: Consumo en la capa de presentación

Ejemplo de controlador que consume la lógica de negocio:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace AplicacionWeb.Controladores
{
    using ArquitecturaTresCapas.IBLL;
    using ArquitecturaTresCapas.Modelo.VistasModelo;
    using Comunes;
    using ArquitecturaTresCapas.Modelo.Filtros;
    using System.Threading.Tasks;
    using ArquitecturaTresCapas.Modelo;

    public class InicioController : Controller
    {
        private ICompraLogicaNegocio logicaCompras;
        
        public InicioController(ICompraLogicaNegocio logicaCompras)
        {
            this.logicaCompras = logicaCompras;
        }

        // GET: Inicio
        public ActionResult Index()
        {
            ViewBag.compra = logicaCompras.Donde(c => c.Id == 6).FirstOrDefault().CantidadProductos;
            ViewBag.tiempo = "La hora actual es: " + DateTime.Now.ToString();
            return View();
        }
    }
}

Etiquetas: ASP.NET MVC Entity Framework T4 Templates AutoFac C#

Publicado el 7-6 23:39