Search code examples
entity-frameworkmany-to-many

Many to Many Relationship doesn't work both ways?


I've been trying to grok EF many-to-many relationships for the past two days now and I'm still missing something even after scouring a dozen different questions here.

I've got a model named Text that can have an arbitrary number of Tag models associated with it, and obviously, at least in theory, each Tag can be associated with an arbitrary number of Texts. Entity Framework seems to understand this well enough to create a table named TextTags in the database without me asking it to do so, and I can access Text.Tags without trouble, but when I attempt to access Tag.Texts in my code, I get a null reference exception.

Now, I could just add every text to every tag manually (or could I? that seems to throw some kind of error), but that would seem to defeat the purpose... Besides which, it also seems error prone. What am I failing to understand?

Code as requested:

Text model:

public class Text
{
    public int ID { get; set; }
    public Author Author { get; set; }
    public string Content { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
}

Tag model:

public class Tag
{
    public int ID { get; set; }
    public string Name { get; set; }
    public ICollection<Text> Texts { get; set; }
}

Data insert:

using (var db = new TextDbContext())
{
    db.Authors.Add(new Author()
    {
        Name = "Poe"
    });

    db.Tags.Add(new Tag() { Name = "lame" });
    db.Tags.Add(new Tag() { Name = "example" });

    db.SaveChanges();

    db.Texts.Add(new Text()
    {
        Author = db.Authors.First(),
        Tags = db.Tags.ToList(),
        Content = "This is the first text by Poe."
    });

    db.Texts.Add(new Text()
    {
        Author = db.Authors.First(),
        Tags = db.Tags.ToList(),
        Content = "This is the second text by Poe."
    });

    db.Texts.Add(new Text()
    {
        Author = db.Authors.First(),
        Tags = db.Tags.ToList(),
        Content = "This is the third text by Poe."
    });

    db.SaveChanges();
}

Error:

foreach (var tag in db.Tags)
{
    foreach (var text in tag.Texts)
    {
        Console.WriteLine("Tag: {0}\tText: {1}", tag.Name, text.Content);
        // Null reference on line above.
    }
}

Solution

  • You get a NullReferenceException because your navigation property Tag.Texts is not marked as virtual. As a result lazy loading does not work to load the Tag.Texts collection when you access it and the collection is null. (Text.Tags is virtual, hence no exception here.)