我的存储库处理并为丰富的域模型提供持久性。我不想将贫血的Entity Framework数据实体暴露给我的业务层,所以我需要一些在它们之间进行映射的方法。
在大多数情况下,从数据实体构建域模型实例需要使用参数化构造函数和方法(因为它很丰富)。它不像属性/字段匹配那么简单。 AutoMapper可用于相反的情况(映射到数据实体),但不能用于创建域模型。
以下是我的存储库模式的核心。
EntityFrameworkRepository
类使用两种泛型类型:
TDomainModel
:丰富的域名模型TEntityModel
:实体框架数据实体定义了两种抽象方法:
ToDataEntity(TDomainModel)
:转换为数据实体(用于Add()
和Update()
方法)ToDomainModel(TEntityModel)
:构建域模型(用于Find()
方法)。这些方法的具体实现将定义所讨论的存储库所需的映射。
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);
}
以下是存储库实现的基本示例:
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
}
}
}
问题:
Company
可能由许多Departments
组成。如果我想在获取CompanyRepository
时急切地从Company
加载这些,那么我将在哪里定义Department
和DepartmentDataEntity
之间的映射?
我可以在CompanyRepository
中提供更多的映射方法,但这很快就会变得混乱。整个系统很快就会出现重复的映射方法。
对上述问题有什么更好的解决方法?
我的存储库处理并为丰富的域模型提供持久性。我不想将贫血的Entity Framework数据实体暴露给我的业务层,所以我需要一些在它们之间进行映射的方法。
如果您使用实体框架,它可以映射Rich Domain Model本身。
我最近回答了类似的问题"Advice on mapping of entities to domain objects"。
我一直在使用NHibernate,并且知道在Entity Framework中你也可以指定从DB表到POCO对象的映射规则。在Entity Framework实体上开发另一个抽象层是一项额外的工作。让ORM负责所有mappings,状态跟踪,unit of work和identity map实现等。现代ORM知道如何处理所有这些问题。
AutoMapper可用于相反的情况(映射到数据实体),但不能用于创建域模型。
你完全正确。
当一个实体可以映射到另一个实体而没有其他依赖关系时(例如,存储库,服务,...),Automapper非常有用。
...我在哪里定义
Department
和DepartmentDataEntity
之间的映射?
我会把它放入DepartmentRepository
并添加方法IList<Department> FindByCompany(int companyId)
以便检索公司的部门。
我可以在
CompanyRepository
中提供更多的映射方法,但这很快就会变得混乱。整个系统很快就会出现重复的映射方法。对上述问题有什么更好的解决方法?
如果需要获取另一个实体的Department
s列表,则应该在DepartmentRepository
中添加一个新方法,并在需要的地方使用。
假设您有以下数据访问对象...
public class AssetDA
{
public HistoryLogEntry GetHistoryRecord(int id)
{
HistoryLogEntry record = new HistoryLogEntry();
using (IUnitOfWork uow = new NHUnitOfWork())
{
IReadOnlyRepository<HistoryLogEntry> repository = new NHRepository<HistoryLogEntry>(uow);
record = repository.Get(id);
}
return record;
}
}
返回历史日志条目数据实体。该数据实体定义如下......
public class HistoryLogEntry : IEntity
{
public virtual int Id
{ get; set; }
public virtual int AssetID
{ get; set; }
public virtual DateTime Date
{ get; set; }
public virtual string Text
{ get; set; }
public virtual Guid UserID
{ get; set; }
public virtual IList<AssetHistoryDetail> Details { get; set; }
}
你可以看到属性Details
引用了另一个数据实体AssetHistoryDetail
。现在,在我的项目中,我需要将这些数据实体映射到我的业务逻辑中使用的域模型对象。为了进行映射,我已经定义了扩展方法......我知道它是一种反模式,因为它是特定于语言的,但好的是它隔离并打破了彼此之间的依赖关系......是的,这就是它的美妙之处。因此,映射器定义如下......
internal static class AssetPOMapper
{
internal static HistoryEntryPO FromDataObject(this HistoryLogEntry t)
{
return t == null ? null :
new HistoryEntryPO()
{
Id = t.Id,
AssetID = t.AssetID,
Date = t.Date,
Text = t.Text,
UserID = t.UserID,
Details = t.Details.Select(x=>x.FromDataObject()).ToList()
};
}
internal static AssetHistoryDetailPO FromDataObject(this AssetHistoryDetail t)
{
return t == null ? null :
new AssetHistoryDetailPO()
{
Id = t.Id,
ChangedDetail = t.ChangedDetail,
OldValue = t.OldValue,
NewValue = t.NewValue
};
}
}
这就是它。所有依赖项都在一个地方。然后,当从业务逻辑层调用数据对象时,我会让LINQ
做其余的事情......
var da = new AssetDA();
var entry = da.GetHistoryRecord(1234);
var domainModelEntry = entry.FromDataObject();
请注意,您可以定义相同的内容以将“域模型”对象映射到“数据实体”。
使用实体框架,跨越所有层IMHO,从实体模型转换为其他形式的模型(域模型,值对象,视图模型等)通常是一个坏主意,反之亦然,除非仅在应用程序层中,因为您将丢失了许多只能通过实体对象实现的EF功能,例如丢失更改跟踪和丢失LINQ可查询。
最好在存储库层和应用程序层之间进行映射。将实体模型保留在存储库层中。
我喜欢使用自定义的Extension方法来实现Entity和Domain对象之间的映射。
一个简单的例子:
public static class LevelTypeItemMapping
{
public static LevelTypeModel ToModel(this LevelTypeItem entity)
{
if (entity == null) return new LevelTypeModel();
return new LevelTypeModel
{
Id = entity.Id;
IsDataCaptureRequired = entity.IsDataCaptureRequired;
IsSetupRequired = entity.IsSetupRequired;
IsApprover = entity.IsApprover;
Name = entity.Name;
}
}
public static IEnumerable<LevelTypeModel> ToModel(this IEnumerable<LevelTypeItem> entities)
{
if (entities== null) return new List<LevelTypeModel>();
return (from e in entities
select e.ToModel());
}
}
......你像这样使用它们.....
using (IUnitOfWork uow = new NHUnitOfWork())
{
IReadOnlyRepository<LevelTypeItem> repository = new NHRepository<LevelTypeItem>(uow);
record = repository.Get(id);
return record.ToModel();
records = repository.GetAll(); // Return a collection from the DB
return records.ToModel(); // Convert a collection of entities to a collection of models
}
不完美,但很容易遵循,很容易重用。
像之前的帖子一样说。最好等到存储库之后直到做实际映射。但是我喜欢使用自动映射器。它提供了一种将对象映射到其他对象的简单方法。对于某些关注点分离,您还可以在单独的项目中定义映射。这些映射也是通用的/基于类型的:
如果我正确理解你的问题,这可以解决问题。
我不喜欢手动映射所以我正在使用http://valueinjecter.codeplex.com/进行映射