Reputation: 4323
Using automapper 5.2.0 in combination with EF core 1.0.1 and SQL lite in memory. I can't get it to create expected database query, instead I get this:
Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Warning: The Include operation for navigation: 'a.PostTags.Tag' was ignored because the target navigation is not reachable in the final query results. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.IncludeIgnoredWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider. Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Warning: The Include operation for navigation: 'a.Responses.CreatedByUser' was ignored because the target navigation is not reachable in the final query results. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.IncludeIgnoredWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider. Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Warning: The Include operation for navigation: 'a.CreatedByUser' was ignored because the target navigation is not reachable in the final query results. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.IncludeIgnoredWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider. Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Warning: The Include operation for navigation: 'a.Category' was ignored because the target navigation is not reachable in the final query results. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.IncludeIgnoredWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider. Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory:Information: Executed DbCommand (0ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30'] SELECT 1 FROM "Posts" AS "a" WHERE "a"."PostId" = @__id_0 LIMIT 2
Here is my setup:
Startup:
public class Startup
{
private MapperConfiguration _mapperConfiguration { get; set; }
public Startup(IHostingEnvironment env)
{
...
_mapperConfiguration = new MapperConfiguration(cfg =>
{
...
cfg.AddProfile(new PostMapperProfile());
});
_mapperConfiguration.AssertConfigurationIsValid();
}
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IMapper>(sp => _mapperConfiguration.CreateMapper());
services.AddSingleton<MapperConfiguration>(sp => _mapperConfiguration);
}
}
PostMapperProfile:
public class PostMapperProfile : Profile
{
public PostMapperProfile()
{
var postDetailMap = CreateMap<PostEntity, PostDetailModel>();
postDetailMap.ForAllMembers(opt => opt.Ignore());
postDetailMap.ForMember(m => m.Category, opt => opt.MapFrom(src => src.CategoryId.HasValue ? src.Category : null));
postDetailMap.ForMember(m => m.CreatedAt, opt => opt.MapFrom(src => src.CreatedAt));
postDetailMap.ForMember(m => m.CreatedByUser, opt => opt.MapFrom(src => src.CreatedByUser));
postDetailMap.ForMember(m => m.PostId, opt => opt.MapFrom(src => src.PostId));
postDetailMap.ForMember(m => m.PostState, opt => opt.MapFrom(src => src.PostState));
postDetailMap.ForMember(m => m.PostType, opt => opt.MapFrom(src => src.PostType));
postDetailMap.ForMember(m => m.ResponsesCount, opt => opt.MapFrom(src => src.Responses.Count));
postDetailMap.ForMember(m => m.Text, opt => opt.MapFrom(src => src.Text));
postDetailMap.ForMember(m => m.Tags, opt => opt.MapFrom(src => src.PostTags.Select(x => x.Tag).ToList()));
postDetailMap.ForMember(m => m.Title, opt => opt.MapFrom(src => src.Title));
postDetailMap.ForMember(m => m.ViewsCount, opt => opt.MapFrom(src => src.ViewsCount));
postDetailMap.ForMember(m => m.VotesCount, opt => opt.MapFrom(src => src.VotesCount));
postDetailMap.ForMember(m => m.Responses, opt => opt.MapFrom(src => PagedList<ResponseEntity>.Create(src.Responses.Take(10).Select(x => AutoMapper.Mapper.Map<ResponseDetailModel>(x)).ToList(), 1, 10, src.Responses.Count)));
}
}
BlogpostService:
public class BlogpostService
{
private readonly AppDbContext m_context;
private readonly IMapper m_mapper;
private readonly MapperConfiguration m_config;
public BlogpostService(AppDbContext context, IMapper mapper, MapperConfiguration config)
{
m_context = context;
m_mapper = mapper;
m_config = config;
}
public PostDetailModel GetPostDetail(int id)
{
var s = m_context.Posts
.Include(a => a.CreatedByUser)
.Include(a => a.Category)
.Include(a => a.PostTags)
.ThenInclude(a => a.Tag)
.Include(a => a.Responses)
.ThenInclude(b => b.CreatedByUser)
.Where(x => x.PostId == id);
var d = s.Single();
return s.ProjectTo<PostDetailModel>(m_config).Single();
}
}
Test:
public class PostRepositoryTests
{
public TestServer server { get; }
public HttpClient client { get; }
private readonly AppDbContext Context;
private readonly UserManager<UserEntity> UserManager;
private readonly IMapper Mapper;
private readonly MapperConfiguration Config;
public PostRepositoryTests()
{
...
Context = serviceProvider.GetRequiredService<AppDbContext>();
UserManager = serviceProvider.GetRequiredService<UserManager<UserEntity>>();
Mapper = serviceProvider.GetService<IMapper>();
Config = serviceProvider.GetService<MapperConfiguration>();
}
[Fact]
public void CreatePost()
{
var user = new UserEntity();
user.Email = "[email protected]";
var ct = UserManager.CreateAsync(user, "Testing123..");
var service = new BlogpostService(Context, Mapper, Config);
var blogpost = new CreateBlogpostRequest();
blogpost.Title = "Some title";
blogpost.Content = "Some content";
blogpost.Tags = new List<TagDTO>(){
new TagDTO{
Label = "Tag1",
},
new TagDTO{
Label = "Tag2",
},
new TagDTO{
Label = "Tag3",
}
};
var response = service.CreateBlogpost(blogpost, user.Id);
var postDetail = service.GetPostDetail(response.PostId);
Assert.Equal(postDetail.Tags.Count, 3);
}
}
Upvotes: 1
Views: 366
Reputation: 4323
@IvanStoev pointed me to right direction, I managed to solve it, I think
postDetailMap.ForAllMembers(opt => opt.Ignore());
Somehow masked other errors, after I removed that line I got error saying some of the properties are not mapped, so I added explicit ignore for those members and for the Responses property.
After that it actually tried to make query but failed beacuse of .ToList() in tags (I accidently added that when upgrading from MVC4, AM 4.x), so after getting rid of .ToList() things worked.
Modified map that works:
var postDetailMap = CreateMap<PostEntity, PostDetailModel>();
postDetailMap.ForMember(m => m.ResponsesShowing,opt => opt.Ignore());
postDetailMap.ForMember(m => m.Responses, opt => opt.Ignore());
// postDetailMap.ForAllMembers(opt => opt.Ignore());
postDetailMap.ForMember(m => m.Category, opt => opt.MapFrom(src => src.CategoryId.HasValue ? src.Category : null));
postDetailMap.ForMember(m => m.CreatedAt, opt => opt.MapFrom(src => src.CreatedAt));
postDetailMap.ForMember(m => m.CreatedByUser, opt => opt.MapFrom(src => src.CreatedByUser));
postDetailMap.ForMember(m => m.PostId, opt => opt.MapFrom(src => src.PostId));
postDetailMap.ForMember(m => m.PostState, opt => opt.MapFrom(src => src.PostState));
postDetailMap.ForMember(m => m.PostType, opt => opt.MapFrom(src => src.PostType));
postDetailMap.ForMember(m => m.ResponsesCount, opt => opt.MapFrom(src => src.Responses.Count));
postDetailMap.ForMember(m => m.Text, opt => opt.MapFrom(src => src.Text));
postDetailMap.ForMember(m => m.Tags, opt => opt.MapFrom(src => src.PostTags.Select(x => x.Tag)));
postDetailMap.ForMember(m => m.Title, opt => opt.MapFrom(src => src.Title));
postDetailMap.ForMember(m => m.ViewsCount, opt => opt.MapFrom(src => src.ViewsCount));
postDetailMap.ForMember(m => m.VotesCount, opt => opt.MapFrom(src => src.VotesCount));
//postDetailMap.ForMember(m => m.Responses, opt => opt.MapFrom(src => PagedList<ResponseEntity>.Create(src.Responses.Take(10).Select(x => AutoMapper.Mapper.Map<ResponseDetailModel>(x)).ToList(), 1, 10, src.Responses.Count)));
Upvotes: 1