AutoMapper是否支持Linq?

时间:2010-02-06 04:56:56

标签: linq-to-sql automapper

我对使用Lazy加载功能的Linq to SQL非常感兴趣。在我的项目中,我使用AutoMapper将数据库模型映射到域模型(从DB_RoleInfoDO_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
    };
}

这种方法按照我想要的方式工作。

2 个答案:

答案 0 :(得分:59)

虽然@ Aaronaught的答案在撰写本文时是正确的,但世界经常发生变化,AutoMapper也随之而来。与此同时,QueryableExtensions被添加到代码库中,增加了对转换为表达式的投影的支持,最后是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();

在v.4.1.0之前

1 Project().To<T>()

答案 1 :(得分:24)

将您的第二个功能更改为:

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段转换为查询的一部分。


澄清

一旦您将结果类型更改为非数据实体的内容,延迟执行“延迟加载”)的概念就没有任何意义。考虑这两个类:

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

Linq对DO_RoleInfo课程一无所知。它不知道如何进行映射向后 - 在某些情况下,甚至可能不可能。当然,您可以查看此代码并转到“哦,这很简单,只需在Name列中搜索'管理员'或'执行',但你是唯一一个知道这一点的人。就Linq to SQL而言,这个查询纯属无稽之谈。

想象一下有人给了你这些指示:

  

前往超市并带回制作Morton Thompson Turkey的配料。

除非你以前做过,而大多数人都没有,否则你对该指令的回应很可能是:

  • 到底是什么意思?

你可以去市场,你可以通过名字获得特定的成分,但是当你在那里时,你无法评估我给你的条件。我必须“取消映射”标准第一个。我必须告诉你,这里是我们需要的配方 - 现在去拿它们。


总而言之,这不是Linq to SQL和AutoMapper之间的一些简单的不兼容。它不是这两个库中任何一个都是唯一的。 如何实际上映射到非实体类型并不重要 - 您可以轻松地手动执行映射,并且您仍然会得到相同的错误,因为您现在正在给予Linq to SQL一组不再易于理解的指令,处理没有任何特定实体类型的内在映射的神秘类。

此问题是 O / R Mapping和延迟查询执行概念的基础。 投影单向操作。一旦你投射,你就不能再回到查询引擎并且顺便说出哦,这里有更多的条件。太晚了。你能做的最好的事情就是把它给你的东西,并自己评估额外的条件。


最后但并非最不重要的是,我会给你一个解决方法。如果您希望能够从映射中执行的事件过滤行,则可以写下:

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 to SQL公开的真实实体。域模型是您的公共接口。您撰写的查询是私人实施的一个方面。了解差异并保持良好的关注点分离非常重要。