AutoMapper奇怪的IQueryable投影异常

时间:2015-09-30 18:53:09

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

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

我使用EF和AutoMapper IQueryableExtensions。

我的两个模型如下:

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上有以下ctors:

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引起奇怪行为的想法。

3 个答案:

答案 0 :(得分:1)

我可以在第二次编辑中重现该问题。不知何故(我不太了解AutoMapper以便了解原因),参数化构造函数总是优先于AutoMapper中的无参数构造函数。并且EF在其表达式树中不支持参数化构造函数。

无论如何,您可以使用ConstructProjectionUsing

解决此问题
Mapper.CreateMap<Semester, SemesterDto>().MaxDepth(1)
      .ConstructProjectionUsing(sem => new SemesterDto());

答案 1 :(得分:1)

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

如果您有v4.1.0或更高,那么这应该已经修复。

答案 2 :(得分:0)

Mapper.CreateMap<Article, Dto.Article>()
      .ForMember(d => d.Semester, o => o.MapFrom(s => Mapper.Map<Dto.Semester>(s));

您需要告诉它使用Semester - &gt; Dto.Semester映射。