pattern net mvc example ejemplos dropdownlist asp c# asp.net-mvc repository repository-pattern

c# - example - Mejor patrón de repositorio para ASP.NET MVC



mvc dropdownlist selected value (4)

Existe una solución lista para usar en URF - Unit of Work & Repositories Framework (extensible / genérico) . Esto le ahorrará mucho tiempo. Implementaron un repositorio genérico (también hay un repositorio asíncrono). Para extender cualquier repositorio, han usado extensiones como esta:

public static decimal GetCustomerOrderTotalByYear(this IRepository<Customer> repository, string customerId, int year) { return repository .Queryable() .Where(c => c.CustomerID == customerId) .SelectMany(c => c.Orders.Where(o => o.OrderDate != null && o.OrderDate.Value.Year == year)) .SelectMany(c => c.OrderDetails) .Select(c => c.Quantity*c.UnitPrice) .Sum(); }

Algunas clases como QueryObject pueden ser un exceso de trabajo dependiendo de su proyecto, pero en general es una buena solución para ayudarlo a comenzar a funcionar.

Recientemente aprendí ASP.NET MVC (me encanta). Estoy trabajando con una empresa que usa la inyección de dependencia para cargar una instancia de repositorio en cada solicitud, y estoy familiarizado con el uso de ese repositorio.

Pero ahora estoy escribiendo un par de aplicaciones de MVC. No entiendo completamente los cómos y los por qué del repositorio que usa mi empresa, y estoy tratando de decidir el mejor enfoque para implementar el acceso a los datos.

Estoy usando C # y Entity Framework (con todas las últimas versiones).

Veo tres enfoques generales para manejar el acceso a los datos.

  1. Contexto regular de base de datos dentro de una declaración using cada vez que accedo a datos. Esto es simple y funciona bien. Sin embargo, si dos ubicaciones necesitan leer los mismos datos dentro de una solicitud, los datos deben leerse dos veces. (Con un solo repositorio por solicitud, la misma instancia se usaría en ambos lugares y entiendo que la segunda lectura simplemente devolvería los datos de la primera lectura).

  2. Un patrón de repositorio típico. Por razones que no entiendo, este patrón típico implica la creación de una clase contenedora para cada tabla utilizada desde la base de datos. Eso me parece equivocado. De hecho, dado que se implementan también como interfaces, técnicamente crearía dos clases de contenedor para cada tabla. EF crea tablas para mí. No creo que este enfoque tenga sentido.

  3. También hay un patrón de repositorio genérico donde se crea una única clase de repositorio para servir a todos los objetos de entidad. Esto tiene mucho más sentido para mí. Pero, ¿tiene sentido para los demás? ¿Es el enlace de arriba el mejor enfoque?

Me encantaría obtener información de otros sobre este tema. ¿Estás escribiendo tu propio repositorio, usando uno de los anteriores, o haciendo algo completamente diferente? Por favor comparte.


He usado una mezcla de # 2 y # 3, pero prefiero un repositorio genérico estricto si es posible (más estricto que incluso sugerido en el enlace para # 3). # 1 no es bueno porque funciona mal con pruebas unitarias.

Si tiene un dominio más pequeño o necesita restringir qué entidades permite consultar su dominio, supongo que el # 2 o el # 3 que definen las interfaces de repositorio específicas de cada entidad que implementan un repositorio genérico tienen sentido. Sin embargo, considero que es agotador e innecesario escribir una interfaz y una implementación concreta para cada entidad que deseo consultar. ¿De qué sirve public interface IFooRepository : IRepository<Foo> (nuevamente, a menos que necesite restringir a los desarrolladores a un conjunto de raíces agregadas permitidas)?

Solo defino mi interfaz genérica de repositorio, con los métodos Add , Remove , Get , Get GetDeferred , Count y Find (Find devuelve una interfaz IQueryable que permite LINQ), crear una implementación genérica concreta y llamarla por día. Depende mucho de Find y, por lo tanto, de LINQ. Si necesito usar una consulta específica más de una vez, utilizo métodos de extensión y escribo la consulta usando LINQ.

Esto cubre el 95% de mis necesidades de persistencia. Si necesito realizar algún tipo de acción de persistencia que no se puede hacer genéricamente, uso una API de ICommand cosecha propia. Por ejemplo, supongamos que estoy trabajando con NHibernate y necesito realizar una consulta compleja como parte de mi dominio, o tal vez necesite hacer un comando masivo. La API se ve más o menos así:

// marker interface, mainly used as a generic constraint public interface ICommand { } // commands that return no result, or a non-query public interface ICommandNoResult : ICommand { void Execute(); } // commands that return a result, either a scalar value or record set public interface ICommandWithResult<TResult> : ICommand { TResult Execute(); } // a query command that executes a record set and returns the resulting entities as an enumeration. public interface IQuery<TEntity> : ICommandWithResult<IEnumerable<TEntity>> { int Count(); } // used to create commands at runtime, looking up registered commands in an IoC container or service locator public interface ICommandFactory { TCommand Create<TCommand>() where TCommand : ICommand; }

Ahora puedo crear una interfaz para representar un comando específico.

public interface IAccountsWithBalanceQuery : IQuery<AccountWithBalance> { Decimal MinimumBalance { get; set; } }

Puedo crear una implementación concreta y usar SQL sin formato, NHibernate HQL, lo que sea, y registrarlo con mi localizador de servicios.

Ahora en mi lógica comercial puedo hacer algo como esto:

var query = factory.Create<IAccountsWithBalanceQuery>(); query.MinimumBalance = 100.0; var overdueAccounts = query.Execute();

También puede usar un patrón de Especificación con IQuery para generar consultas significativas, impulsadas por entrada de usuario, en lugar de tener una interfaz con millones de propiedades confusas, pero eso supone que no encuentra el patrón de especificación confuso en sí mismo;).

Una última parte del acertijo es cuando su repositorio necesita hacer una operación específica de depósito previo y posterior. Ahora, puede crear fácilmente una implementación de su repositorio genérico para una entidad específica, luego anular los métodos relevantes y hacer lo que necesita hacer, y actualizar su IoC o registro de localizador de servicios y finalizarlo.

Sin embargo, a veces esta lógica es transversal e incómoda de implementar anulando un método de repositorio. Así que creé IRepositoryBehavior , que básicamente es un receptor de eventos. (A continuación se muestra una definición aproximada de la parte superior de mi cabeza)

public interface IRepositoryBehavior { void OnAdding(CancellableBehaviorContext context); void OnAdd(BehaviorContext context); void OnGetting(CancellableBehaviorContext context); void OnGet(BehaviorContext context); void OnRemoving(CancellableBehaviorContext context); void OnRemove(BehaviorContext context); void OnFinding(CancellableBehaviorContext context); void OnFind(BehaviorContext context); bool AppliesToEntityType(Type entityType); }

Ahora bien, estos comportamientos pueden ser cualquier cosa. Auditoría, comprobación de seguridad, borrado suave, imposición de restricciones de dominio, validación, etc. Creo un comportamiento, lo registro con el IoC o localizador de servicios, y modifico mi repositorio genérico para tomar una colección de IRepositoryBehavior registrados, y IRepositoryBehavior cada comportamiento contra el tipo de repositorio actual y ajuste la operación en los controladores pre / post para cada comportamiento aplicable.

Aquí hay un ejemplo de comportamiento de borrado suave (borrar por software) significa que cuando alguien solicita eliminar una entidad, simplemente la marcamos como eliminada para que no pueda ser devuelta nuevamente, pero en realidad nunca se elimina físicamente.

public SoftDeleteBehavior : IRepositoryBehavior { // omitted public bool AppliesToEntityType(Type entityType) { // check to see if type supports soft deleting return true; } public void OnRemoving(CancellableBehaviorContext context) { var entity = context.Entity as ISoftDeletable; entity.Deleted = true; // when the NHibernate session is flushed, the Deleted column will be updated context.Cancel = true; // set this to true to make sure the repository doesn''t physically delete the entity. } }

Sí, esto es básicamente una implementación simplificada y abstracta de los oyentes de eventos de NHibernate, pero es por eso que me gusta. A) Puedo probar un comportamiento sin poner NHibernate en la imagen B) Puedo usar estos comportamientos fuera de NHibernate (digamos que el repositorio es la implementación del cliente que envuelve las llamadas al servicio REST) ​​C) Los oyentes del evento de NH pueden ser un verdadero dolor en el culo ;)


Recomendaría el número 1, con algunas advertencias. El número 2 es lo que parece ser más común, pero según mi experiencia, el repositorio acaba siendo un sucio basurero para las consultas. Si utilizas un repositorio genérico (2), es solo una capa delgada alrededor del DBContext, un poco inútil a menos que estés planeando cambiar los ORM (mala idea).

Pero cuando accedo a DBContext directamente prefiero usar un patrón de Pipes and Filters para que pueda reutilizar la lógica común, algo así como

items = DBContext.Clients .ByPhoneNumber(''1234%'') .ByOrganisation(134);

ByPhoneNumber y By Organization son solo métodos de extensión.


Aquí vamos para el mejor patrón de repositorio en Asp.Net MVC:

El patrón Repositorio agrega una capa de separación entre los datos y las capas de dominio de una aplicación. También hace que las partes de acceso a datos de una aplicación sean mejor comprobables.

Base de datos de fábrica (IDatabaseFactory.cs):

public interface IDatabaseFactory : IDisposable { Database_DBEntities Get(); }

Implementaciones de la base de datos (DatabaseFactory.cs):

public class DatabaseFactory : Disposable, IDatabaseFactory { private Database_DBEntities dataContext; public Database_DBEntities Get() { return dataContext ?? (dataContext = new Database_DBEntities()); } protected override void DisposeCore() { if (dataContext != null) dataContext.Dispose(); } }

Interfaz base (IRepository.cs):

public interface IRepository<T> where T : class { void Add(T entity); void Update(T entity); void Detach(T entity); void Delete(T entity); T GetById(long Id); T GetById(string Id); T Get(Expression<Func<T, bool>> where); IEnumerable<T> GetAll(); IEnumerable<T> GetMany(Expression<Func<T, bool>> where); void Commit(); }

Clase abstracta (Repository.cs):

public abstract class Repository<T> : IRepository<T> where T : class { private Database_DBEntities dataContext; private readonly IDbSet<T> dbset; protected Repository(IDatabaseFactory databaseFactory) { DatabaseFactory = databaseFactory; dbset = DataContext.Set<T>(); } /// <summary> /// Property for the databasefactory instance /// </summary> protected IDatabaseFactory DatabaseFactory { get; private set; } /// <summary> /// Property for the datacontext instance /// </summary> protected Database_DBEntities DataContext { get { return dataContext ?? (dataContext = DatabaseFactory.Get()); } } /// <summary> /// For adding entity /// </summary> /// <param name="entity"></param> public virtual void Add(T entity) { try { dbset.Add(entity); // dbset.Attach(entity); dataContext.Entry(entity).State = EntityState.Added; int iresult = dataContext.SaveChanges(); } catch (UpdateException ex) { } catch (DbUpdateException ex) //DbContext { } catch (Exception ex) { throw ex; } } /// <summary> /// For updating entity /// </summary> /// <param name="entity"></param> public virtual void Update(T entity) { try { // dbset.Attach(entity); dbset.Add(entity); dataContext.Entry(entity).State = EntityState.Modified; int iresult = dataContext.SaveChanges(); } catch (UpdateException ex) { throw new ApplicationException(Database_ResourceFile.DuplicateMessage, ex); } catch (DbUpdateException ex) //DbContext { throw new ApplicationException(Database_ResourceFile.DuplicateMessage, ex); } catch (Exception ex) { throw ex; } } /// <summary> /// for deleting entity with class /// </summary> /// <param name="entity"></param> public virtual void Delete(T entity) { dbset.Remove(entity); int iresult = dataContext.SaveChanges(); } //To commit save changes public void Commit() { //still needs modification accordingly DataContext.SaveChanges(); } /// <summary> /// Fetches values as per the int64 id value /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual T GetById(long id) { return dbset.Find(id); } /// <summary> /// Fetches values as per the string id input /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual T GetById(string id) { return dbset.Find(id); } /// <summary> /// fetches all the records /// </summary> /// <returns></returns> public virtual IEnumerable<T> GetAll() { return dbset.AsNoTracking().ToList(); } /// <summary> /// Fetches records as per the predicate condition /// </summary> /// <param name="where"></param> /// <returns></returns> public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where) { return dbset.Where(where).ToList(); } /// <summary> /// /// </summary> /// <param name="entity"></param> public void Detach(T entity) { dataContext.Entry(entity).State = EntityState.Detached; } /// <summary> /// fetches single records as per the predicate condition /// </summary> /// <param name="where"></param> /// <returns></returns> public T Get(Expression<Func<T, bool>> where) { return dbset.Where(where).FirstOrDefault<T>(); } }

Cómo acceder a este patrón de repositorio en el controlador:

1. Usted tiene un Modelo de Usuario:

public partial class User { public int Id { get; set; } public string Name { get; set; } }

2. Ahora debe crear la clase de repositorio de su UserModel

public class UserRepository : Repository<User>, IUserRepository { private Database_DBEntities dataContext; protected IDatabaseFactory DatabaseFactory { get; private set; } public UserRepository(IDatabaseFactory databaseFactory) : base(databaseFactory) { DatabaseFactory = databaseFactory; } protected Database_DBEntities DataContext { get { return dataContext ?? (dataContext = DatabaseFactory.Get()); } } public interface IUserRepository : IRepository<User> { } }

3. Ahora tiene que crear UserService Interface (IUserService.cs) con todos los métodos CRUD:

public interface IUserService { #region User Details List<User> GetAllUsers(); int SaveUserDetails(User Usermodel); int UpdateUserDetails(User Usermodel); int DeleteUserDetails(int Id); #endregion }

4. Ahora debe crear UserService Interface (UserService.cs) con todos los métodos CRUD:

public class UserService : IUserService { IUserRepository _userRepository; public UserService() { } public UserService(IUserRepository userRepository) { this._userRepository = userRepository; } public List<User> GetAllUsers() { try { IEnumerable<User> liUser = _userRepository.GetAll(); return liUser.ToList(); } catch (Exception ex) { throw ex; } } /// <summary> /// Saves the User details. /// </summary> /// <param name="User">The deptmodel.</param> /// <returns></returns> public int SaveUserDetails(User Usermodel) { try { if (Usermodel != null) { _userRepository.Add(Usermodel); return 1; } else return 0; } catch { throw; } } /// <summary> /// Updates the User details. /// </summary> /// <param name="User">The deptmodel.</param> /// <returns></returns> public int UpdateUserDetails(User Usermodel) { try { if (Usermodel != null) { _userRepository.Update(Usermodel); return 1; } else return 0; } catch { throw; } } /// <summary> /// Deletes the User details. /// </summary> /// <param name="Id">The code identifier.</param> /// <returns></returns> public int DeleteUserDetails(int Id) { try { User Usermodel = _userRepository.GetById(Id); if (Usermodel != null) { _userRepository.Delete(Usermodel); return 1; } else return 0; } catch { throw; } } }

5. Ahora todo configurado para su patrón de repositorio y puede acceder a todos los datos en el controlador de usuario:

//Here is the User Controller public class UserProfileController : Controller { IUserService _userservice; public CustomerProfileController(IUserService userservice) { this._userservice = userservice; } [HttpPost] public ActionResult GetAllUsers(int id) { User objUser=new User(); objUser = _userservice.GetAllUsers().Where(x => x.Id == id).FirstOrDefault(); } }