2015-09-30 2 views
3

Чтобы пропустить боль, перейдите к EDIT ниже.AutoMapper странное исключение проекции IQueryable


Я использую 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; } 
} 

И у меня есть следующие DTOs:

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!

Возможно, что-то не так с моей стороны, но я не могу найти ничего подозрительного.

EDIT:

У меня есть следующий ctors на SemesterDto:

public SemesterDto() 
{ 
} 

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

Когда я удалить второе один все работает. Похоже, это правда, это действительно странное поведение. Я никогда не думал, что ctor сделает проблему, поэтому я не включил ее для ясности, возможно все, не так ли. Извини за это.

Итак, это ошибка от AutoMapper или есть что-то еще, что я неправильно понял?

EDIT 2:

Зачистка это больше, я попробовал отображение а Semester к SemesterDto непосредственно и это результат:

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

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

Это укрепляет идею, что т е р вызывает странное поведение.

+0

Какую версию вы используете AutoMapper? – silkfire

+0

Последний, '4.0.4' – mrahhal

+0

Выполняет ли' Project(). () 'правильно работает? –

ответ

1

Это было подтверждено как ошибка, см this github issue. На данный момент, я думаю, я просто избегу ctors в своих DTO до promised следующей версии.

Если у вас есть v4.1.0 или выше, то это должно быть исправлено.

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

Вы должны сказать ему, чтобы использовать Semester - отображение>Dto.Semester.

+0

Это вместо этого дает мне следующее исключение: 'LINQ to Entities не распознает метод 'NS.Models.SemesterDto Map [Semester] (System.Object)' method ...' – mrahhal

+0

Как вы вызываете начальный файл? – Swati

+0

Что это значит? В другой заметке я пробовал это: '.ForMember (a => a.Semester, c => c.MapFrom (a => Mapper.Map (a.Semester)));'. – mrahhal

1

Я могу воспроизвести проблему во второй редакции. Как-то (я не знаю AutoMapper достаточно хорошо, чтобы понять, почему), параметризованный конструктор всегда имеет приоритет над параметрируемым в AutoMapper. И EF не поддерживает параметризованные конструкторы в своем дереве выражений.

Во всяком случае, вы можете решить эту проблему с помощью ConstructProjectionUsing:

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

** So ** близко! Это работает при непосредственном сопоставлении с SemesterDto, но не выполняется, когда SemesterDto вложен внутри другого dto, как и раньше, но с другим исключением: 'System.InvalidOperationException: при вызове из« VisitLambda », переписывая узел типа« System.Linq.Expressions ». ParameterExpression 'должен возвращать ненулевое значение того же типа. В качестве альтернативы, переопределите «VisitLambda» и измените его, чтобы не посещать дочерние объекты этого типа. «Я думаю, вы ближе нас к источнику проблемы, поэтому я уже поддерживал. – mrahhal

+0

Хм, для меня это нормально с прямыми и вложенными проекциями. –

+1

Итак, я обнаружил, что это исключение фактически вызвано использованием 'ConstructProjectionUsing'. Когда я удалял все ctors, но продолжал использовать 'ConstructProjectionUsing', то происходило одно и то же исключение (исключение' VisitLambda') – mrahhal

Смежные вопросы