0

I'm new to Entity Framework. I'm using the code first option. Below I have a basic model. The database was created properly and the records persist properly but when I run the test method (defined after the model) a second time my header record loads fine, but my navigation property, Details, does not reload. What am I doing wrong?

class Header
{
    public int HeaderId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Detail> Details { get; set; } = new List<Detail>();
}

class Detail
{
    public int DetailId { get; set; }
    public string Name { get; set; }
    public int HeaderId { get; set; }
    [ForeignKey("HeaderId")]
    public virtual Header Header { get; set; }
}

class MyContext : DbContext
{
    public MyContext() : base("EfTest")
    {
        this.Configuration.LazyLoadingEnabled = true;
    }

    public virtual DbSet<Header> Headers { get; set; }
}

private static void DoIt()
{
    using (var ctx = new MyContext())
    {
        var hdr = (
            from header in ctx.Headers
            where header.Name == NAME
            select header).FirstOrDefault();
        if (hdr == null)
        {
            hdr = new Header();
            hdr.Name = NAME;
            ctx.Headers.Add(hdr);
            MessageBox.Show("Header not found; created.");
        }
        else
        {
            MessageBox.Show("Header found!");
        }

        var det = hdr.Details.FirstOrDefault();
        if (det == null)
        {
            det = new Detail() { Name = "Hi" };
            hdr.Details.Add(det);
            MessageBox.Show("Detail not found; created.");
        }
        else
        {
            MessageBox.Show("Detail found!");
        }

        ctx.SaveChanges();
    }
}
2
  • Is the DetailId primary key? If yes is it identity? Commented Feb 26, 2017 at 14:28
  • Yes, by code first conventions it's automatically a PK defined as an identity. Commented Feb 26, 2017 at 14:33

1 Answer 1

1

Here Entity Framework use Lazy-Loading. In order to get details you should use Eager-Loading.You should use Include method in System.Data.Entity namespace to accomplish Eager-Loading.Change your query like the following.

       var hdr = (
                from header in ctx.Headers
                where header.Name == NAME
                select header).Include(h=>h.Details).FirstOrDefault();
Sign up to request clarification or add additional context in comments.

13 Comments

In my real case I want to use the lazy loading though. Shouldn't the detail record be loaded when I reference it?
Lazy loading only loads what you ask it to. You asked for the Header and you got the Header, if you also want the Detail you need to explicitly ask for Detail(using Include).
The point being that it doesn't load anything unnecessary or unasked for. Imagine having a table with 50 table references, but you only need the main one. Without lazy loading in this case that kind of query would take a long time to load.(For example you need to get a User, but you are not interested in that User's Orders. Lazy will not load the Orders since you didn't ask)
But I did ask for it. var det = hdr.Details.FirstOrDefault();
Definitely. ctx.Entry(hdr).Collection(h => h.Details).Query().Where(d => d.DetailId == 1).Load();
|

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.