我正在使用DTO从数据库(使用Entity Framework)检索数据:
IQueryable<User> users = repository.ListFiltered<User>(n => n.Active);
var usersDTO = from user in users
select new UserAccountDTO{
UserId = user.Id,
UserName = user.Name,
InstitutionName = user.Institution.Name,
InstitutionCountry = user.Institution.Country.Name
};
我这样做是因为用户实体和机构实体拥有大量我现在不需要的数据,所以我不这样做想要从数据库中检索它。
但问题是我不喜欢这些代码,我想拆分代码并连接选择,无论如何都要这样做吗?
我希望得到类似的东西:
users.LoadUserData().LoadInstitutionData();
你怎么说?有可能吗?
答案 0 :(得分:2)
问题是LoadUserData()
和LoadInstitutionData()
应该是什么样子。假设此方法链的最终产品应为IEnumerable<UserAccountDTO>
。流畅语法中的最后一个方法总是确定输出。因此LoadInstitutionData()
应返回所需的IEnumerable
。清除。
但输入呢?我看到两种选择:
输入为IEnumerable<User>
(或IQueryable<User>
)。好的,这意味着LoadInstitutionData()
除了你已经拥有的代码之外别无他法。这不是解决方案,因为你不喜欢这些代码。此外,该方法要求user.Institution
和Country
必须加载或延迟加载,这在任何形式的代码合同中都难以表达。
输入为IEnumerable<UserAccountDTO>
,其中LoadUserData
设置了直接user
属性,LoadInstitutionData
应补充Institution
数据。现在的问题是:LoadInstitutionData
应该如何做到这一点?无论如何,现在在一个查询中完成的操作至少需要两次查询,甚至可能是1 + n。
更复杂的替代方案可能包括传递和组合表达式,但肯定你会从煎锅跳到火中,重新发明LINQ的扩展方法,或者AutoMapper,或者......
等一下,AutoMapper。
您是否知道AutoMapper可以以对您有吸引力的方式为您执行此操作?
你需要的只是一个班级
class UserAccountDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string InstitutionName { get; set; }
public string InstitutionCountryName { get; set; } // Notice the Name part!
};
映射
AutoMapper.Mapper.CreateMap<User, UserAccountDTO>();
使用:
using AutoMapper.QueryableExtensions;
简洁的语法:
var usersDTO = users.Project().To<UserAccountDTO>();
(当然是来自NuGet的AutoMapper)。
Automapper会将InstitutionCountryName
之类的属性解析为user.Institution.Country.Name
,并且投影的结果是一个表达式,该表达式被转换为带有连接和所有连接的一个SQL语句。那是我喜欢的代码。