phitch
phitch

Reputation: 125

EF7 Core Many to Many Reference object not populated

I am having difficulty getting EF7 to populate the objects referenced in a many to many join. I have followed the docs at https://docs.efproject.net/en/latest/modeling/relationships.html, but the object is still null. From what I can tell you don't have to do anything specific to get EF to populate them. I copied the sample code from the docs page as follows:

public class MyContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    public MyContext(DbContextOptions<MyContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PostTag>()
            .HasKey(t => new { t.PostId, t.TagId });

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Post)
            .WithMany(p => p.PostTags)
            .HasForeignKey(pt => pt.PostId);

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Tag)
            .WithMany(t => t.PostTags)
            .HasForeignKey(pt => pt.TagId);
    }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public List<PostTag> PostTags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }
    public string Title { get; set; }
    public List<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public int PostId { get; set; }
    public Post Post { get; set; }
    public string TagId { get; set; }
    public Tag Tag { get; set; }
}

I had to add a constructor to get it to run. I also added a Title field to the Tag class so it has more than just a key. Once I populated some data in the tables, I run the following code to retrieve from the database:

var results = _context.Posts.Include(s => s.PostTags).ToList();

When I examine the results in the debugger, the Tag objects are null even though the keys to obtain them are present. Notice that the Post objects are populated. It is always the second column of the two column key that is not joined: enter image description here

This is the SQL generated by EF7:

SELECT [s].[PostId], [s].[Content], [s].[Title] FROM [Posts] AS [s] ORDER BY [s].[PostId]

SELECT [p].[PostId], [p].[TagId] FROM [PostTag] AS [p] WHERE EXISTS ( SELECT 1 FROM [Posts] AS [s] WHERE [p].[PostId] = [s].[PostId]) ORDER BY [p].[PostId]

It doesn't appear to be fetching the Tag object at all. What am I missing here?

For completeness, I have included the sample data: enter image description here

Upvotes: 1

Views: 578

Answers (2)

phitch
phitch

Reputation: 125

Thanks to @SOfanatic for pointing me in the right direction. I am not sure why EF doesn't automatically load the second reference class, but it doesn't. The following code will retrieve the Tag object (as well as the Post object even though we don't explicitly load it).

 var results = _context.Posts.Include(s => s.PostTags).ThenInclude(t => t.Tag).ToList();

Upvotes: 1

SOfanatic
SOfanatic

Reputation: 5573

You are doing a many-to-many with what is known as Payload class. The PostTag class is technically not needed for EF to create a many-to-many relationship.

You are going to have to do something like this:

var results = _context.Posts.Include(s => s.PostTags.Select(pt => pt.Tag).ToList();

Right now your linq is loading the related entities, which are just the id's so it's of no use. If your PostTags class is not going to have any other fields/properties you should look into creating a many-to-many without the payload

Upvotes: 0

Related Questions