在映射到实体框架(v6.4)代理类时,使用继承类的错误映射遇到了AutoMapper(v9.0)的问题。它似乎与执行映射的顺序有关,并且似乎与所用映射的某种缓存有关。这是实体框架配置:
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类的新代理来生成的(注意,我需要出于性能原因在我的应用中启用代理类创建):
_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());
生成的代理对象是(pseudo-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之类的东西,并在“返回”行上放置断点,则可以看到在两种情况下都使用了PostDTO-> Post映射,并且PostWithKeywords-> Post映射根本没有被击中。
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映射。然后,它在PostWithKeywordsDTO-> Post_B24121DF0B3091C6258AA7C620C6D74F4114A74351B7D90111C87EAAF182C939之间遇到尝试的映射,并且没有意识到PostWithKeywordsDTO实际上是PostDTO的子级,并且错误地重新使用了PostDTO-> Post映射。
但是,奇怪的是,如果我颠倒了映射的执行顺序会发生什么:
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_B24121DF0B309dC6258AA7C620C6D74F4114A743S发生时,生成的Maps缓存不存在,而拥有正确缓存的状态则是>
我确实尝试使用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 : mapping issue with inheritance and abstract base class on collections with Entity Framework 4 Proxy Pocos),这似乎是一个类似的问题,但是在这种情况下,解决方法是使用'DynamicMap'函数,该函数此后在AutoMapper中已弃用。有没有其他人遇到过类似的问题/代理类映射,并且知道其他解决方案?
答案 0 :(得分:1)
这就是我最终要解决的问题。深入研究代码后,我决定强制映射类型的解决方案将导致映射继承的其他问题。
相反,我确定了一个解决方案,该解决方案根据类型映射源/目标类型来自相应请求类型的继承级别数,计算每个匹配类型映射与请求类型的“距离”,然后选择“最接近的”。通过在标准的两个坐标距离计算中将“源距离”作为x值并将“目标距离”作为y值来完成此操作:
Overall Distance = SQRT([Source Distance]^2 + [Destination Distance]^2)
例如,在我的场景中,我有以下地图:
PostDTO -> Post
and
PostWithKeywordsDTO -> Post
在尝试映射PostWithKeywordsDTO-> PostProxy时,没有精确的映射匹配,因此我们必须确定最适合的映射。在这种情况下,可以使用的可能地图列表为:
PostDTO -> Post (Since PostWithKeywordsDTO inherits from PostDTO and PostProxy inherits from Post)
or
PostWithKeywordsDTO -> Post (Since PostProxy inherits from Post)
要确定要使用的地图,它会计算:
PostDTO -> Post:
Source Distance = 1 (PostDTO is one level above PostWithKeywordsDTO)
Destination Distance = 1 (Post is one level above PostProxy)
Overall Distance = 1.414
PostWithKeywordsDTO -> Post
Source Distance = 0 (since PostWithKeywordsDTO = PostWithKeywordsDTO)
Destination Distance = 1 (Post is one level above PostProxy)
Overall Distance = 1
因此,在这种情况下,由于距离最小,因此将使用PostWithKeywordsDTO-> Post映射。这似乎在所有情况下都有效,并且也满足所有AM单元测试。这里是gist所需要的代码更新(尽管我确信可能有更清洁/更有效的方法来实现)。