加载虚拟属性

时间:2013-12-18 15:13:19

标签: c# .net entity-framework projection

我正在使用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();

你怎么说?有可能吗?

1 个答案:

答案 0 :(得分:2)

问题是LoadUserData()LoadInstitutionData()应该是什么样子。假设此方法链的最终产品应为IEnumerable<UserAccountDTO>。流畅语法中的最后一个方法总是确定输出。因此LoadInstitutionData()应返回所需的IEnumerable。清除。

但输入呢?我看到两种选择:

  • 输入为IEnumerable<User>(或IQueryable<User>)。好的,这意味着LoadInstitutionData()除了你已经拥有的代码之外别无他法。这不是解决方案,因为你不喜欢这些代码。此外,该方法要求user.InstitutionCountry必须加载或延迟加载,这在任何形式的代码合同中都难以表达。

  • 输入为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语句。那是我喜欢的代码。