AutoMapper是否支持Linq?
我非常感兴趣的Linq SQL与惰性加载function。 在我的项目中,我使用AutoMapper将数据库模型映射到域模型(从DB_RoleInfo
到DO_RoleInfo
)。 在我的仓库代码如下:
public DO_RoleInfo SelectByKey(Guid Key) { return SelectAll().Where(x => x.Id == Key).SingleOrDefault(); } public IQueryable<DO_RoleInfo> SelectAll() { Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); return from role in _ctx.DB_RoleInfo select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role); }
SelectAll
方法运行良好,但是当我调用SelectByKey
,我得到错误:
方法“RealMVC.Data.DO_RoleInfo MapDB_RoleInfo,DO_RoleInfo”无法转换为SQL。
Automapper是否完全不支持Linq?
而不是Automapper,我尝试下面的手动映射代码:
public IQueryable<DO_RoleInfo> SelectAll() { return from role in _ctx.DB_RoleInfo select new DO_RoleInfo { Id = role.id, name = role.name, code = role.code }; }
这种方法按我想要的方式工作。
虽然@Aaronaught的答案在编写时是正确的,但世界经常发生变化,AutoMapper也一样。 与此同时, QueryableExtensions
被添加到代码库中,该代码库增加了对被转换成expression式的投影的支持,最后还增加了对SQL的支持。
核心扩展方法是ProjectTo
1 。 这就是你的代码的样子:
using AutoMapper.QueryableExtensions; public IQueryable<DO_RoleInfo> SelectAll() { Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); return _ctx.DB_RoleInfo.ProjectTo<DO_RoleInfo>(); }
它会像手动映射一样。 ( CreateMap
语句在这里是为了演示的目的。通常,你应该在应用程序启动时定义映射一次)。
因此,只有映射所需的列被查询,结果是IQueryable
仍然具有原始查询提供者(linq-to-sql,linq-to-entities,无论)。 所以它仍然是可组合的,这将转化为SQL中的WHERE
子句:
SelectAll().Where(x => x.Id == Key).SingleOrDefault();
1 Project().To<T>()
4.1.0之前的Project().To<T>()
改变你的第二个function:
public IEnumerable<DO_RoleInfo> SelectAll() { Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); return from role in _ctx.DB_RoleInfo.ToList() select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role); }
AutoMapper在Linq to SQL中工作得很好,但不能作为延迟查询的一部分执行。 在Linq查询的末尾添加ToList()
会导致它立即计算结果,而不是试图将AutoMapper段作为查询的一部分进行翻译。
澄清
延迟执行 ( 不是 “延迟加载”)的概念在您将结果types更改为非数据实体之后没有任何意义。 考虑这两个类:
public class DB_RoleInfo { public int ID { get; set; } public string Name { get; set; } } public class DO_RoleInfo { public Role Role { get; set; } // Enumeration type }
现在考虑下面的映射:
Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo> .ForMember(dest => dest.Role, opt => opt.MapFrom(src => (Role)Enum.Parse(typeof(Role), src.Name)));
这个映射是完全正确的(除非我犯了一个错字),但是假设你在原来的文章中写了SelectAll
方法,而不是我的修改版:
public IQueryable<DO_RoleInfo> SelectAll() { Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); return from role in _ctx.DB_RoleInfo select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role); }
这实际上是一种作品,但是通过自称是“可查询的”,它就在于此。 如果我试图写这个反对什么会发生什么:
public IEnumerable<DO_RoleInfo> SelectSome() { return from ri in SelectAll() where (ri.Role == Role.Administrator) || (ri.Role == Role.Executive) select ri; }
真的很想这个。 Linq到SQL怎么可能能够成功地把你的where
变成一个实际的数据库查询?
Linq对DO_RoleInfo
类一无所知。 它不知道如何做反向映射 – 在某些情况下,这可能甚至不可能。 当然,你可以看看这个代码,然后去“哦,这很容易,只需在Name
列中search'pipe理员'或'执行' ,但是你是唯一知道这个的人。 就Linq to SQL而言,查询是纯粹的废话。
想象一下,有人给你这些指示:
去超市,带回制作莫顿汤普森土耳其的原料。
除非你以前做过,而大多数人还没有做过,否则你对这条指令的回应很可能是:
- 这他妈到底是什么?
你可以去市场,你可以通过名字得到具体的成分,但你不能评估我在你那里给你的条件。 我必须首先“解除”标准。 我必须告诉你, 这里是我们需要的这个食谱的成分 – 现在去得到它们。
总之,这并不是Linq到SQL和AutoMapper之间的简单不兼容 。 这两个图书馆都不是唯一的。 实际上,如何映射到非实体types并不重要 – 您可以轻松地手动执行映射,并且仍然会得到相同的错误,因为您现在正在向Linq提供一组指令那些不能被理解的,处理那些没有任何特定实体types的内在映射的神秘类。
这个问题是O / R映射和延迟查询执行的基本概念。 投影是单向操作 。 一旦你投射,你不能再回到查询引擎, 顺便说一句,这里有更多的条件给你 。 太晚了。 你所能做的最好的就是把它已经给你的东西,自己评估一下额外的条件。
最后但并非最不重要的是,我会给你一个解决方法。 如果你想从你的映射中唯一能做的就是过滤行,你可以这样写:
public IEnumerable<DO_RoleInfo> SelectRoles(Func<DB_RoleInfo, bool> selector) { Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); return _ctx.DB_RoleInfo .Where(selector) .Select(dbr => Mapper.Map<DB_RoleInfo, DO_RoleInfo>(dbr)); }
这是一个实用的方法,为您处理映射,接受原始实体上的filter,而不是映射的实体。 如果您有许多不同types的filter,但总是需要执行相同的映射,则可能会很有用。
就个人而言,我认为只要正确写出查询,首先确定需要从数据库中检索什么,然后进行任何预测/映射,最后如果需要进一步过滤(您不应该), 然后使用ToList()
或ToArray()
实现结果,并在本地列表中写入更多的条件。
不要试图使用AutoMapper或任何其他工具来隐藏由Linq公开的SQL实体。 域模型是您的公共接口 。 你写的查询是你的私有实现的一个方面。 理解差异并保持好的关注点是很重要的。