C# 将实体映射到DTO而不使用重复代码
我正试图解决这个问题,在N层应用程序中使用实体框架(6)。由于来自存储库(包含与数据库的所有通信)的数据应该在更高的层(UI、服务等)中使用,因此我需要将其映射到DTO 在数据库中,存在相当多的多对多关系,因此数据结构可能/将在应用程序生命周期的某个阶段变得复杂。我偶然发现,在编写存储库方法时,我正在重复完全相同的代码。我的C# 将实体映射到DTO而不使用重复代码,c#,entity-framework,entity-framework-6,dto,C#,Entity Framework,Entity Framework 6,Dto,我正试图解决这个问题,在N层应用程序中使用实体框架(6)。由于来自存储库(包含与数据库的所有通信)的数据应该在更高的层(UI、服务等)中使用,因此我需要将其映射到DTO 在数据库中,存在相当多的多对多关系,因此数据结构可能/将在应用程序生命周期的某个阶段变得复杂。我偶然发现,在编写存储库方法时,我正在重复完全相同的代码。我的FirmRepository就是一个例子,它包含一个GetAll()方法和GetById(int-firmId)方法 在GetById(int-firmId)方法中,我有以下
FirmRepository
就是一个例子,它包含一个GetAll()
方法和GetById(int-firmId)
方法
在GetById(int-firmId)
方法中,我有以下代码(不完整,因为有很多关系需要映射到DTO):
public DTO.Firm GetById(int-id)
{
//返回结果
var结果=新的DTO.Firm();
尝试
{
//数据库连接
使用(var ctx=new MyEntities())
{
//从数据库中获取公司信息
var公司=(来自ctx公司中的f
其中f.ID==ID
选择f).FirstOrDefault();
//如果找到公司,则开始映射到DTO对象
如果(坚定!=null)
{
结果.地址=公司.地址;
result.Address2=firm.Address2;
结果.增值税=公司.增值税;
result.Email=firm.Email;
//地图Zipcode和城市
result.City=new DTO.City()
{
CityName=firm.City.City1,
ZipCode=firm.City.ZipCode
};
//地图ISO代码和国家/地区
result.Country=新的DTO.Country()
{
CountryName=firm.Country.Country1,
ISO=firm.Country.ISO
};
//检查该公司是否有任何专有参数
if(firm.ExclusiveParameterType\u Product\u firm.Any())
{
var exclusiveParamsList=新列表();
//映射独占参数类型
foreach(公司内var参数。独家参数类型产品公司)
{
//继续之前,请检查独占参数类型是否为null
if(param.ExclusiveParameterType!=null)
{
//创建新的独占参数类型DTO
var exclusiveParameter=新的DTO.ExclusiveParameterType()
{
ID=param.ExclusiveParameterType.ID,
Description=param.ExclusiveParameterType.Description,
Name=param.ExclusiveParameterType.Name
};
//将新的DTO添加到列表中
排他性参数列表。添加(排他性参数);
}
}
//要映射的对象多得多。。。。
//在结果对象上设置列表
result.ExclusiveParameterTypes=exclusiveParamsList;
}
}
}
//返回DTO
返回结果;
}
捕获(例外e)
{
//日志异常
Logging.Instance.Error(e);
//只需返回null
返回null;
}
}
这只是一种方法。然后,GetAll()
方法将具有完全相同的映射逻辑,从而产生重复的代码。此外,当添加更多方法时,即Find
或Search
方法,需要再次复制相同的映射。当然,这并不理想
我已经读了很多关于著名的AutoMapper框架的书,该框架可以将实体映射到DTO或从DTO映射到实体,但是由于我有这些多对多关系,它很快就会因为AutoMapper配置代码而感到臃肿。我也读过这篇文章,在我看来很有意义:
有没有其他方法可以在不反复复制/粘贴相同代码的情况下执行此操作
提前谢谢 另一种策略是使用类构造函数和
显式
和/或隐式
转换运算符的组合。它允许您将一个用户定义的实体强制转换为另一个实体。该特性还有一个额外的好处,就是将流程抽象出来,这样您就不会重复自己
在您的DTO.Firm
类中,定义显式或隐式运算符(注意:我正在对您的类的名称进行假设):
然后,您可以在存储库代码中使用它,如下所示:
public DTO.Firm GetById(int id) {
using (var ctx = new MyEntities()) {
var firm = (from f in ctx.Firms
where f.ID == id
select f).FirstOrDefault();
return (DTO.Firm)firm;
}
}
public List<DTO.Firm> GetAll() {
using (var ctx = new MyEntities()) {
return ctx.Firms.Cast<DTO.Firm>().ToList();
}
}
public DTO.Firm GetById(int-id){
使用(var ctx=new MyEntities()){
var公司=(来自ctx公司中的f
其中f.ID==ID
选择f).FirstOrDefault();
返回(DTO.公司)公司;
}
}
公共列表GetAll(){
使用(var ctx=new MyEntities()){
返回ctx.Firms.Cast().ToList();
}
}
这是MSDN中的扩展方法。您可以对实体公司(DB.firm)进行如下扩展
public static class Extensions
{
public static DTO.Firm ToDto(this DB.Firm firm)
{
var result = new DTO.Firm();
result.Address = firm.Address;
result.Address2 = firm.Address2;
//...
return result;
}
}
然后可以在代码中的任意位置转换DB.Firm对象,如
Firm.ToDto()代码>关于映射:实际上,如果您使用Automapper或在某些方法中完全手动准备映射(扩展名1或其他答案中提到的显式强制转换操作符),这并不重要—关键是将其放在一个地方以实现可重用性
只是
public DTO.Firm GetById(int id) {
using (var ctx = new MyEntities()) {
var firm = (from f in ctx.Firms
where f.ID == id
select f).FirstOrDefault();
return (DTO.Firm)firm;
}
}
public List<DTO.Firm> GetAll() {
using (var ctx = new MyEntities()) {
return ctx.Firms.Cast<DTO.Firm>().ToList();
}
}
public static class Extensions
{
public static DTO.Firm ToDto(this DB.Firm firm)
{
var result = new DTO.Firm();
result.Address = firm.Address;
result.Address2 = firm.Address2;
//...
return result;
}
}