【问题标题】:AutoMapper strange IQueryable projection exceptionAutoMapper 奇怪的 IQueryable 投影异常
【发布时间】:2015-10-15 18:02:42
【问题描述】:

要跳过痛苦,请转到下面的编辑


我正在使用带有 AutoMapper IQueryableExtensions 的 EF。

我的两个模型如下:

public class Article
{
    public int Key { get; set; }

    public DateTimeOffset Created { get; set; }

    public int? SemesterKey { get; set; }
    public virtual Semester Semester { get; set; }
}

public class Semester
{
    public int Key { get; set; }

    public string Name { get; set; }

    public virtual ICollection<Article> Articles { get; set; }

    // Other relationships
    public virtual ICollection<Subject> Subjects { get; set; }

    public virtual ICollection<AppUser> Users { get; set; }
}

我有以下 DTO:

public class ArticleDto
{
    public int Key { get; set; }

    public DateTimeOffset Created { get; set; }

    // If I remove this (or ignore it), everything works.
    public SemesterDto Semester { get; set; }
}

public class SemesterDto
{
    public int Key { get; set; }

    public string Name { get; set; }
}

配置:

Mapper.CreateMap<Semester, SemesterDto>().MaxDepth(1);
Mapper.CreateMap<Article, ArticleDto>().MaxDepth(1);

查询:

context.Articles.Include(a => a.Semester)
                .OrderByDescending(a => a.Created)
                .Take(5)
                .ProjectTo<ArticleDto>()
                .ToList();

执行查询会产生以下奇怪的异常:

System.ArgumentException: Property 'NS.Models.Semester Semester' is not defined for type 'NS.Models.Semester'

这是一些堆栈跟踪:

System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member)
....
AutoMapper.MappingEngine.CreateMapExpression(ExpressionRequest request, Expression instanceParameter, IDictionary`2 typePairCount)

似乎 AutoMapper 生成了一个表达式,该表达式正在寻找 Semester 属性(似乎是对 ArticleDto.Semester 的响应),该 NS.Models.Semester 类型当然不存在。 如果我执行以下操作,我可以让它工作:

Mapper.CreateMap<Article, ArticleDto>().MaxDepth(1)
      .ForMember(a => a.Semester, c => c.MapFrom(a => a.Semester == null ? null : new SemesterDto() {
                        Key = a.Semester.Key,
                        Year = a.Semester.Year }));

但这正是我试图避免使用 AutoMapper 编写的东西!

这可能是我这边出了点问题,但我找不到任何可疑之处。

编辑:

我在 SemesterDto 上有以下演员:

public SemesterDto()
{
}

public SemesterDto(int key, string name)
{
    Key = key;
    Name = name;
}

当我删除第二个时,一切正常。似乎就是这样,但这确实是一种奇怪的行为。我从没想过ctor会出现问题,所以为了清楚起见我没有包括它,一切皆有可能不是吗。对此感到抱歉。

那么,这是 AutoMapper 的错误还是我误解了其他什么?

编辑 2:

剥离更多,我尝试将Semester 直接映射到SemesterDto,结果如下:

context.Semesters.ProjectTo<SemesterDto>().FirstOrDefault();

NotSupportedException: Only parameterless constructors and initializers are supported in LINQ to Entities.

这强化了 ctor 导致奇怪行为的想法。

【问题讨论】:

  • 你用的是什么版本的AutoMapper?
  • Project().To&lt;ArticleDto&gt;() 工作正常吗?
  • 同样的结果,同样的事情。
  • 我无法复制这个。也许你应该展示更多细节,这样我们可能会看到你自己忽略的问题。
  • 我制作了一些轻量级模型仅用于测试并且映射工作正常。现在我正试图排除导致此失败的差异。

标签: asp.net-mvc entity-framework automapper


【解决方案1】:

我可以在您的第二次编辑中重现该问题。不知何故(我对 AutoMapper 了解得不够透彻,无法理解为什么),参数化的构造函数总是优先于 AutoMapper 中的无参数构造函数。并且 EF 在其表达式树中不支持参数化构造函数。

无论如何,您可以使用ConstructProjectionUsing 解决此问题:

Mapper.CreateMap<Semester, SemesterDto>().MaxDepth(1)
      .ConstructProjectionUsing(sem => new SemesterDto());

【讨论】:

  • 所以关闭!这在直接映射到 SemesterDto 时有效,但是当 SemesterDto 像以前一样嵌套在另一个 dto 中时会失败,但有不同的例外:System.InvalidOperationException: When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type. 我认为你让我们最接近问题的根源,所以我已经投票了。
  • 嗯,对我来说,直接和嵌套投影没问题。
  • 所以我发现这个异常实际上是使用ConstructProjectionUsing引起的。当我删除所有 ctors 但继续使用 ConstructProjectionUsing 时,发生了相同的异常(VisitLambda 异常)
  • 您的代码中一定有一些东西不能很好地与 AutoMapper 配合使用。对我来说ConstructProjectionUsing 工作正常,也没有参数化的ctors。我现在有点难过。
  • 我现在真的很困惑,我尝试了一个非常简单的模型。与往常一样,直接映射有效,但映射到具有嵌套 dto 的 dto 失败。我认为现在我将在余生中避免使用自动映射器在我的 DTO 上使用 ctor。
【解决方案2】:

这已被确认为错误,请参阅this github issue。现在,我想在promised 下一个版本之前,我会避免在我的 DTO 中使用 ctor。

如果您有v4.1.0 或更高版本,那么这个问题应该已经修复了。

【讨论】:

    【解决方案3】:
    Mapper.CreateMap<Article, Dto.Article>()
          .ForMember(d => d.Semester, o => o.MapFrom(s => Mapper.Map<Dto.Semester>(s));
    

    您需要告诉它使用Semester -> Dto.Semester 映射。

    【讨论】:

    • 这反而给了我以下异常:LINQ to Entities does not recognize the method 'NS.Models.SemesterDto Map[Semester](System.Object)' method...
    • 如何调用初始映射器?
    • 你是什么意思?另一方面,我试过这个:.ForMember(a =&gt; a.Semester, c =&gt; c.MapFrom(a =&gt; Mapper.Map&lt;SemesterDto&gt;(a.Semester)));.
    • 这个解决方案很可能只适用于内存映射。我做了一些轻量级模型,也出现了同样的异常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多