2

First of all, I know this question might be a dupplicate of Automapper entity framework foreign key is null, but none of the answers made it work for me and I'm facing some other weird issues and on top of that, I'm totally new to .NET Core, EF and Automapper :-).

The problem is that whenever I create or update a Post, which is linked to a Category, with Automapper, the category is saved in the Database, but it's not returned while it's supposed to and I really don't understand why. It's actually returning null. The GetPost method returns what I expect tho; this means that the FK are properly set.

This is the response I get, notice the "Category": null,

enter image description here

Here is my Post entity

public class Post
{
    [Key]
    [StringLength(450)]
    public string Id { get; set; }

    [Required]
    [MaxLength(150)]
    public string Title { get; set; }

    [Required]
    [MaxLength(2500)]
    public string Content { get; set; }

    [ForeignKey("UserId")]
    public User User { get; set; }
    public string UserId { get; set; }

    [ForeignKey("CategoryId")]
    public Category Category { get; set; }
    public string CategoryId { get; set; }
}

My Category entity :

public class Category
{
    [Key]
    [StringLength(450)]
    public string Id { get; set; }

    [Required]
    [MaxLength(150)]
    public string Name { get; set; }

    public int Weight { get; set; }
}

My Automapper configuration for a Post creation :

AutoMapper.Mapper.Initialize(config =>
        {
            config.CreateMap<Category, CategoryDto>();
            config.CreateMap<Post, PostDto>();
            config.CreateMap<PostForCreationDto, Post>();
        });

And my PostForCreationDto :

public class PostForCreationDto
{
    [Required]
    [MaxLength(150)]
    public string Title { get; set; }

    [Required]
    [MinLength(25)]
    [MaxLength(2500)]
    public string Content { get; set; }

    [Required]
    public string CategoryId { get; set; }
}

Here is how I try to create a post in my PostController :

public async Task<IActionResult> CreatePost([FromBody] PostForCreationDto post)
    {

        if (post == null)
        {
            return BadRequest();
        }

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var user = await _userManager.GetUserAsync(HttpContext.User);

        var result = Mapper.Map<Post>(post);
        result.UserId = user.Id;
        _postRepository.CreatePost(result);

        if (!await _postRepository.SaveAsync())
        {
            return BadRequest();
        }

        var createdPost = Mapper.Map<PostDto>(result);

        return Ok(createdPost);
    }

The PostDto contains an Id, Title, Content, and both Category and User DTO's, nothing special here. The _postRepository.CreatePost(result); simply adds the Post to the Posts list of the DbContext (_context.Posts.Add(post);)

The CategoryId is in the payload I send so it should already be included.. But it doesn't seem to work. I tried to hardcode it (result.CategoryId = post.CategoryId;) with no luck. So I figured the only difference with the user is that I load it.. This makes no sense, but I just loaded the category and stocked it in a var (var category = _context.Categories.Where(c => c.Id == post.CategoryId).FirstOrDefault()) without even applying it, and suddenly the post.Category is filled, don't ask me why. Anyway, this is not a solution, I just tried it and couldn't find an answer.

I guess there is a way to create a Post and return all the created fields, including FK's, so if anyone here could guide me through, it would be more than appreciated !

6
  • Could you share EF Command? And Automapper confg (if you have any)? Commented Apr 29, 2019 at 13:35
  • EF Command? About the Automapper config, this is the basic config (I'll update my previous post) Commented Apr 29, 2019 at 13:46
  • In the DBContext.Post call, add include Category Commented Apr 29, 2019 at 14:01
  • I'm not making any API call but the one that adds a Post in the context : _context.Posts.Add(post); . I'm already doing the .Include(p => p.Category) in the "GetPost" method, but this is not really related to this subject and it works perfectly, the only problem comes from the Create/Update methods, neither of them are fetching data from the DB so I guess there is no way to Include anything Commented Apr 29, 2019 at 14:09
  • 1
    This an ORM, it works with objects. Category should be filled before saving your post, as you did with your 'var category = _context.Categories.Where(c => c.Id == post.CategoryId).FirstOrDefault()' Commented Apr 29, 2019 at 22:56

1 Answer 1

0

So, as Hugo pointed out, the relation needs to be loaded in order to be included in the object EF sends. This is something I have troubles to understand.. Right now, I'm loading the linked category right after I add the Post in my Posts context

_context.Posts.Add(post);
_context.Entry(post).Reference("Category").Load();

And I'm just Including it in my GetPost method :

public Post GetPost(string postId)
    {
        return _context.Posts
                       .Include(p => p.User)
                       .Include(p => p.Category)
                       .FirstOrDefault(p => p.Id == postId);
    }

But I'm pretty sure this is not the best practice as I'm hitting the DB to load the category. Here this is just an exercice for testing purposes, I believe a good practice would be to return a NoContent response on creation/update as long as I don't have any other information to add to my response. In this case, I could skip the "load linked entities" part. This could become a problem tho, so right now I understand what the problem is, but this doesn't "solve" the problem, if there is anything to solve.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.