Возникла проблема с AutoMapper (v9.0), использующим неправильное сопоставление для унаследованного класса при сопоставлении с прокси-классом Entity Framework (v6.4). Похоже, что это связано с порядком, в котором выполняется отображение, и, похоже, связано с некоторым типом кэширования используемых карт. Вот конфигурация Entity Framwork:
public class MyDbContext : DbContext
{
public MyDbContext()
{
base.Configuration.ProxyCreationEnabled = true;
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public class Blog
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
}
public class Post
{
[Key]
public int Id { get; set; }
public DateTime PostDate { get; set; }
public string Content { get; set; }
public string Keywords { get; set; }
public virtual Blog Blog { get; set; }
}
И мои классы DTO:
public class PostDTO
{
public DateTime PostDate { get; set; }
public string Content { get; set; }
}
public class PostWithKeywordsDTO : PostDTO
{
public string Keywords { get; set; }
}
Профиль отображения:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<PostDTO, Post>()
.ForMember(dest => dest.Keywords, opt => opt.MapFrom(src => "No Keywords Specified"));
CreateMap<PostWithKeywordsDTO, Post>()
.ForMember(dest => dest.Keywords, opt => opt.MapFrom(src => src.Keywords));
}
}
Я пытаюсь сопоставить эти DTO Объект на прокси класса 'Post', который генерируется либо путем извлечения существующей записи Post из базы данных, либо путем создания нового прокси класса Post с использованием (обратите внимание, мне нужно разрешить создание прокси-класса по соображениям производительности в моем app):
_myDbContext.Posts.Create();
Теперь, когда я пытаюсь выполнить сопоставление следующих объектов postDTO и postWithKeywordsDTO с прокси-классом:
var postDTO = new PostDTO
{
PostDate = DateTime.Parse("1/1/2000"),
Content = "Post #1"
};
var postWithKeywordsDTO = new PostWithKeywordsDTO
{
PostDate = DateTime.Parse("6/30/2005"),
Content = "Post #2",
Keywords = "C#, Automapper, Proxy"
};
var postProxy = mapper.Map(postDTO, _myDbContext.Posts.Create());
var postWithKeywordsProxy = mapper.Map(postWithKeywordsDTO, dbContext.Posts.Create());
получаются прокси-объекты (псевдо- json):
postProxy: {
PostDate: '1/1/2000',
Content: 'Post #1',
Keywords: 'No Keywords Specified'
}
postWithKeywordsProxy: {
PostDate: '6/30/2005',
Content: 'Post #2',
Keywords: 'No Keywords Specified'
}
Кроме того, если я использую что-то вроде встроенного ValueResolver в отображении и ставлю точку останова в строках 'return', я вижу, что сопоставление PostDTO -> Post выполняется используется в обоих случаях, и PostWithKeywords-> Post Mapping не затрагивается вообще.
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<PostDTO, Post>()
.ForMember(dest => dest.Keywords, opt => opt.MapFrom(src => "No Keywords Specified"))
.ForMember(dest => dest.Content, opt => opt.MapFrom((src, dest) =>
{
return src.Content; <-- Hit for both PostDTO and PostWithKeywordsDTO maps to Post
}))
;
CreateMap<PostWithKeywordsDTO, Post>()
.ForMember(dest => dest.Keywords, opt => opt.MapFrom(src => src.Keywords))
.ForMember(dest => dest.Content, opt => opt.MapFrom((src, dest) =>
{
return src.Content;
}))
;
}
}
Что я понимаю из этого, так это то, что кажется, что есть какая-то проблема в определить, какую карту типов использовать при работе с прокси-объектом. Кажется, как будто в первом сценарии он обнаруживает попытку сопоставления между PostDTO -> Post_B24121DF0B3091C6258AA7C620C6D74F4114A74351B7D90111C87EAAF182C939 (прокси-класс) и правильно определяет, что используемой картой является PostDTO -> Post mapping. Затем он сталкивается с попыткой сопоставления между PostWithKeywordsDTO -> Post_B24121DF0B3091C6258AA7C620C6D74F4114A74351B7D90111C87EAAF182C939 и не понимает, что PostWithKeywordsDTO на самом деле является дочерним элементом PostDTO, но ошибочно воспринимает * Post * 10, что ошибочно, но ошибочно воспринимает 10 *, что является ошибочным, но по-разному означает *, что означает *, что является ошибкой *, то есть, то есть, то есть, то есть *, то есть, что является ошибкой *, то есть, то есть, то есть *, то есть, то есть ошибочно, но по-разному думает о том, что это не так, как если бы это было *, то есть 10 *, то есть ошибочно, но по-разному думает о том, что делает Post_TO, и ошибочно воспринимает это как 10 *. что произойдет, если я переверну порядок выполнения карт:
var postWithKeywordsProxy = mapper.Map(postWithKeywordsDTO, dbContext.Posts.Create());
var postProxy = mapper.Map(postDTO, _myDbContext.Posts.Create());
Полученные прокси-объекты верны:
postWithKeywordsProxy: {
PostDate: '6/30/2005',
Content: 'Post #2',
Keywords: 'C#, Automapper, Proxy'
}
postProxy: {
PostDate: '1/1/2000',
Content: 'Post #1',
Keywords: 'No Keywords Specified'
}
Это заставляет меня думать, что это связано с каким-то видом механизма кэширования, который, возможно, ищет первую найденную карту, которая удовлетворяет запрошенной карте прокси, даже если она не является точным соответствием. В этом случае PostWithKeywordsDTO -> отображение Post_B24121DF0B3091C6258AA7C620C6D74F4114A74351B7D90111C87EAAF182C939 происходит первое, так что при последующем PostDTO. -> Post_B24121DF0B3091C6258AA7C620C6D74F4114A74351B7D90111C87EAAF182C939 карта бывает, не в состоянии найти кэшированный тип карту, которая удовлетворяет параметры и она продолжает генерировать правильную карту в кэше
Я пытался использовать версию метода Map, которая принимает явные типы отображаемых элементов, однако это дало тот же результат:
var postProxy = mapper.Map(postDTO, _myDbContext.Posts.Create(), typeof(PostDTO), typeof(Post));
var postWithKeywordsProxy = mapper.Map(postWithKeywordsDTO, dbContext.Posts.Create(), typeof(PostWithKeywordsDTO), typeof(Post));
Также обратите внимание, что если я не надену Не используйте прокси-версии класса Post, все работает, как и ожидалось, поэтому это не проблема с конфигурацией сопоставления.
Что касается возможных обходных путей, наиболее близким является то, что я нашел в этой теме ( Automapper: проблема с отображением с наследованием и абстрактным базовым классом в коллекциях с Entity Framework 4 Proxy Pocos ), что, похоже, является аналогичной проблемой, однако В этом случае karound должен был использовать функцию DynamicMap, которая с тех пор устарела в AutoMapper. Кто-нибудь еще сталкивался с подобной проблемой с отображением классов прокси и знает другое решение?