'Inner properties became null when mapping using ProjectTo<> AutoMapper
I'm new to AutoMapper and its mapping mechanism, I have some models that need to process and query depending on its relationship. For example, I have Tag and Post with 1-to-N relationship.
Post.cs model
public class Post
{
public Guid Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Content { get; set; }
public DateTime Date { get; set; } = DateTime.UtcNow;
public ICollection<TagPost> TagPosts { get; set; }
public ICollection<Photo> Photos { get; set; }
public ICollection<Comment> Comments { get; set; }
public ICollection<PostFollowing> PostFollowers { get; set; }
}
Tag.cs model
public class Tag
{
public Guid Id { get; set; }
public string TagName { get; set; }
public ICollection<TagPost> TagPosts { get; set; }
}
And the appropriate DTOs of both are
PostDto.cs
public class PostDto
{
public Guid Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Content { get; set; }
public string PosterName { get; set; }
public bool IsFound { get; set; }
public ICollection<PostParticipantDto> PostParticipants { get; set; }
[Required]
public TagDto Tag { get; set; }
public ICollection<Photo> Photos { get; set; }
public DateTime CreatedAt { get; set; }
}
Join table
public class TagPost
{
public Guid PostId { get; set; }
public Post Post { get; set; }
public Guid Tag1Id { get; set; }
public Tag1 Tag1 { get; set; }
}
TagDto.cs
public class TagDto
{
public Guid Id { get; set; }
public string TagName { get; set; }
public ICollection<PostDto> Posts { get; set; }
}
As the relationship between Tag and Post is 1-To-N, so I want to retrieve all posts that have the same tag. I don't want to use .Include() and .ThenInclude() to get related data, instead I want to use .ProjectTo<> from AutoMapper. So I configured the Mapping profiles like
MappingProfiles.cs
public MappingProfiles()
{
CreateMap<Post, PostDto>()
.ForMember(dest => dest.PosterName,
o => o.MapFrom(src => src.PostFollowers.FirstOrDefault(x => x.isPoster).ApplicationUser.UserName))
.ForMember(dest => dest.PostLocation, o => o.MapFrom(src => src.PostLocation))
.ForMember(dest => dest.Tag, o => o.MapFrom(src => src.TagPosts.Select(tag => tag.Tag).FirstOrDefault()))
.ForMember(dest => dest.Photos, o => o.MapFrom(src => src.Photos))
.ForMember(dest => dest.PostParticipants, o => o.MapFrom(src => src.PostFollowers))
.ForMember(dest => dest.CreatedAt, o => o.MapFrom(src => src.Date));
CreateMap<Tag, TagDto>()
.ForMember(dest => dest.Tag1Name, o => o.MapFrom(src => src.TagName));
CreateMap<Tag, PostDto>()
.ForMember(dest => dest.Tag, o => o.MapFrom(src => src.TagPosts.Select(tag => tag).FirstOrDefault()));
CreateMap<TagPost, PostDto>()
.ForMember(dest => dest.Tag, o => o.MapFrom(src => src.Tag));
}
And the method perform the query
public async Task<Result<TagDto>> Handle(Query request, CancellationToken cancellationToken)
{
var result = await _context.Tags
.Where(tag => tag.Id == request.TagId)
.ProjectTo<TagDto>(_mapper.ConfigurationProvider
, new { currentUsername = _userAccessor.GetUserName() }
)
.FirstOrDefaultAsync();
}
The mapping configuration seems correct, and the response data has all the mapped properties as I expected, however all of those properties became null. Here is the json response I receive when make a request.
{
"id": "b7049a66-ea0d-4917-a52d-feb2b6dbe483",
"tagName": "dog",
"posts": [
{
"id": "00000000-0000-0000-0000-000000000000",
"title": null,
"content": null,
"posterName": null,
"isFound": false,
"postParticipants": null,
"tag": {
"tagName": "dog"
},
"photos": null,
"createdAt": "0001-01-01T00:00:00"
},
{
"id": "00000000-0000-0000-0000-000000000000",
"title": null,
"content": null,
"posterName": null,
"postParticipants": null,
"tag": {
"tagName": "dog"
},
"photos": null,
"createdAt": "0001-01-01T00:00:00"
},
{
"id": "00000000-0000-0000-0000-000000000000",
"title": null,
"content": null,
"posterName": null,
"postParticipants": null,
"tag1": {
"tagName": "dog"
},
"photos": null,
"createdAt": "0001-01-01T00:00:00"
}
]
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
