Entity Framework Core many-to-many navigation issues

.net-core asp.net-core c# entity-framework entity-framework-core

Question

Entity Framework Core has yet to implement many-to-many relationships, as tracked in GitHub issue #1368; however, when I follow the navigation examples in that issue or similar answers here at Stack Overflow, my enumeration fails to yield results.

I have a many-to-many relationship between Photos and Tags.

After implementing the join table, examples show I should be able to:

var tags = photo.PhotoTags.Select(p => p.Tag);

While that yields no results, I am able to to load via:

var tags = _context.Photos
    .Where(p => p.Id == 1)
    .SelectMany(p => p.PhotoTags)
    .Select(j => j.Tag)
    .ToList();

Relevant code:

public class Photo
{
    public int Id { get; set; }
    public virtual ICollection<PhotoTag> PhotoTags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public virtual ICollection<PhotoTag> PhotoTags { get; set; }
}

public class PhotoTag
{
    public int PhotoId { get; set; }
    public Photo Photo { get; set; }

    public int TagId { get; set; }
    public Tag Tag { get; set; }
}

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    builder.Entity<PhotoTag>().HasKey(x => new { x.PhotoId, x.TagId });
}

What am I missing from other examples?

1
2
5/23/2017 12:02:42 PM

Accepted Answer

In fact this is not a specific for many-to-many relationship, but in general to the lack of lazy loading support in EF Core. So in order to have Tag property populated, it has to be eager (or explicitly) loaded. All this is (sort of) explained in the Loading Related Data section of the EF Core documentation. If you take a look at Including multiple levels section, you'll see the following explanation

You can drill down thru relationships to include multiple levels of related data using the ThenInclude method. The following example loads all blogs, their related posts, and the author of each post.

and example for loading the Post.Author which is pretty much the same as yours:

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
            .ThenInclude(post => post.Author)
        .ToList();
}

So to make this working

var tags = photo.PhotoTags.Select(p => p.Tag);

the photo variable should have been be retrieved using something like this:

var photo = _context.Photos
   .Include(e => e.PhotoTags)
       .ThenInclude(e => e.Tag)
   .FirstOrDefault(e => e.Id == 1);
4
5/2/2017 6:44:48 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow