Patrón de repositorio y mapeo entre modelos de dominio y Entity Framework
Mis repositorios tratan y proporcionan persistencia para un modelo de dominio enriquecido. No quiero exponer la entidad de datos Entity Framework anémica a mis capas empresariales, por lo que necesito alguna forma de mapeo entre ellas.
En la mayoría de los casos, la construcción de una instancia de modelo de dominio a partir de una entidad de datos requiere el uso de métodos y constructores parametrizados (ya que es rica). No es tan simple como una coincidencia de propiedad / campo. AutoMapper podría usarse para la situación opuesta (mapeo a entidades de datos) pero no al crear modelos de dominio.
A continuación se muestra el núcleo de mi patrón de repositorio.
losEntityFrameworkRepository
La clase trabaja con dos tipos genéricos:
TDomainModel
: El modelo de dominio ricoTEntityModel
: La entidad de datos Entity FrameworkSe definen dos métodos abstractos:
ToDataEntity(TDomainModel)
: Convertir a entidades de datos (paraAdd()
yUpdate()
métodos)ToDomainModel(TEntityModel)
: Para construir modelos de dominio (para laFind()
método).Las implementaciones concretas de estos métodos definirían el mapeo requerido para el repositorio en cuestión.
public interface IRepository<T> where T : DomainModel
{
T Find(int id);
void Add(T item);
void Update(T item);
}
public abstract class EntityFrameworkRepository<TDomainModel, TEntityModel> : IRepository<TDomainModel>
where TDomainModel : DomainModel
where TEntityModel : EntityModel
{
public EntityFrameworkRepository(IUnitOfWork unitOfWork)
{
// ...
}
public virtual TDomainModel Find(int id)
{
var entity = context.Set<TEntityModel>().Find(id);
return ToDomainModel(entity);
}
public virtual void Add(TDomainModel item)
{
context.Set<TEntityModel>().Add(ToDataEntity(item));
}
public virtual void Update(TDomainModel item)
{
var entity = ToDataEntity(item);
DbEntityEntry dbEntityEntry = context.Entry<TEntityModel>(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
context.Set<TEntityModel>().Attach(entity);
dbEntityEntry.State = EntityState.Modified;
}
}
protected abstract TEntityModel ToDataEntity(TDomainModel domainModel);
protected abstract TDomainModel ToDomainModel(TEntityModel dataEntity);
}
Aquí hay un ejemplo básico de una implementación de repositorio:
public interface ICompanyRepository : IRepository<Company>
{
// Any specific methods could be included here
}
public class CompanyRepository : EntityFrameworkRepository<Company, CompanyTableEntity>, ICompanyRepository
{
protected CompanyTableEntity ToDataEntity(Company domainModel)
{
return new CompanyTable()
{
Name = domainModel.Name,
City = domainModel.City
IsActive = domainModel.IsActive
};
}
protected Company ToDomainModel(CompanyTableEntity dataEntity)
{
return new Company(dataEntity.Name, dataEntity.IsActive)
{
City = dataEntity.City
}
}
}
Problema:
A Company
podría estar compuesto de muchosDepartments
. Si quiero cargar con entusiasmo estos desde elCompanyRepository
cuando va a buscar unCompany
entonces, ¿dónde definiría la asignación entre unaDepartment
y unDepartmentDataEntity
?
Podría proporcionar más métodos de mapeo en elCompanyRepository
, pero esto pronto se ensuciará. Pronto habría duplicados métodos de mapeo en todo el sistema.
¿Cuál es un mejor enfoque para el problema anterior?