2010-02-06 73 views
20

我对Linq to SQL和Lazy加载特性非常感兴趣。在我的项目中,我使用AutoMapper将数据库模型映射到域模型(从DB_RoleInfoDO_RoleInfo)。在我的仓库代码如下:AutoMapper是否支持Linq?

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,我得到的错误:

Method “RealMVC.Data.DO_RoleInfo MapDB_RoleInfo,DO_RoleInfo” could not translate to 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 
    }; 
} 

此方法我想它的方式。

+0

只是除了您的更新我的笔记回答:第二个版本的工作原理是因为Linq到SQL已经*知道*你已经做了一个不可逆转的预测; “SelectByKey”实际上只是将Linq用于对象。如果您检查正在生成的实际查询,我认为您会发现它仍然从数据库中选择所有实体,这相当于使用“ToList()”,然后过滤结果列表。 – Aaronaught 2010-02-06 15:33:57

+2

这不是说AutoMapper支持LINQ。这是LINQ to SQL不支持AutoMapper。 LINQ to SQL查询提供程序查看表达式树以确定如何生成SQL查询。当它到达Mapper.Map部分时,它不知道如何生成SQL。 – 2010-02-07 18:11:15

回答

23

改变你的第二个功能是:

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到SQL,但它不能为递延查询的一部分来执行。在您的Linq查询的末尾添加ToList()会导致它立即评估结果,而不是尝试将AutoMapper段作为查询的一部分进行翻译。


澄清

延迟执行“懒加载”)的概念没有任何意义,一旦你已经改变了结果类型的东西,这不是一个数据实体。考虑这两个类:

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如何能够可能能够成功地将您的where成为一个实际的数据库查询?

Linq对DO_RoleInfo类一无所知。它不知道如何做映射落后 - 在某些情况下,这可能甚至不可能。当然,你可以看看这个代码,然后去“哦,这很简单,只需在Name列”“中搜索'管理员'或'执行',但只有你是唯一知道这一点的人。就Linq to SQL而言,查询是纯粹的废话。

想象一下,有人给你这些指令:

Go to the supermarket and bring back the ingredients for making Morton Thompson Turkey.

除非你之前已经做到了,而且大部分人都没有,你到指令响应是最有可能将是:

  • 到底是什么?

你可以去市场,你可以通过名字来取得具体成分,但你不能评价我已经给你而你那边的条件。我必须“取消”标准第一个。我必须告诉你,这里是我们需要这个食谱的成分 - 现在去拿他们。


总之,这是不是LINQ的之间的一些简单不相容到SQL和AutoMapper。这两个图书馆都不是唯一的。不要紧实际操作中做映射到非实体类型 - 你可以很容易地做手工的映射,你仍然得到同样的错误,因为你现在给LINQ to SQL的一组不能再理解的指令,处理不具有任何特定实体类型的内在映射的神秘类。

这个问题到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)); 
} 

这是处理映射为您和接受过滤器的实用方法在实体,而不是映射的实体。如果您有许多不同类型的过滤器,但始终需要执行相同的映射,则此功能可能会很有用。个人而言,我认为只要正确写出查询,首先确定需要从数据库中检索,然后进行任何预测/映射,然后最后如果需要做进一步过滤(你不应该),然后物化结果与ToList()ToArray()并写入更多的条件对本地列表。

不要试图使用AutoMapper或任何其他工具来将Linq公开的实体隐藏到SQL中。领域模型是您的公共接口。您编写的查询是您的私有执行的一个方面。理解差异并保持良好的关注点很重要。

+0

感谢您的回答! 但是,按照您提供的方式,SelectAll将从数据库中检索所有记录。我想享受延迟加载的好处,它意味着应用程序将从SelectByKey中的数据库中选择一条记录,但不是SelectAll中的所有记录。 – 2010-02-06 07:42:07

+2

格特阿诺德的答案在下面是解决方案。作为参考,这里是相关的API https://github.com/AutoMapper/AutoMapper/blob/master/src/AutoMapper/QueryableExtensions.cs – 2013-02-08 01:06:33

54

@Aaronaught的回答在撰写本文时是正确的,因为世界经常发生变化,AutoMapper也随之改变。与此同时,QueryableExtensions被添加到代码库中,该代码库增加了对翻译成表达式的预测的支持,最后还支持SQL。

核心扩展方法是ProjectTo 。这是你的代码可能看起来像:

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,无论)。所以它仍然是组合的,这将转化为WHERE子句中的SQL:

SelectAll().Where(x => x.Id == Key).SingleOrDefault(); 
v之前

Project().To<T>() 4.1.0

+2

这个答案应该成为新的答案。 – 2013-02-08 01:07:35

+3

只需使用AutoMapper.QueryableExtensions添加; – Colin 2013-03-22 14:40:47

+0

我收到以下错误“The method'Where'不能按照方法'选择'或不支持'。请参阅此处的问题(http://stackoverflow.com/q/16174351/1133338)。 – 2013-04-23 17:01:13