Entity Framework 6 context not retrieving navigation properties

c# entity-framework lazy-loading linq

Question

I have found many other posts but they are nt facing exactly the same problem. And they are using a slightly different code. SO I think it is worth reviewing this.

I´m using EF6 code first, and I created a Client Entity that has some navigation properties.

I´ll post just the relevant code, consider there are a few more properties and foreign keys as well, but not relevant to the problem. Model is generating ok.

public class Client
{
    public Client()
    {
        JobsExperiences = new Collection<JobsExperience>();
        CapacitationCourses = new Collection<CapacitationCourse>();
        ScholarLevelDetails = new Collection<ScholarLevelDetail>();
        Relatives = new Collection<Relative>();
    }
    public long ClientID { get; set; }
    public virtual ICollection<ScholarLevelDetail> ScholarLevelDetails { get; set; }

    public virtual ICollection<JobsExperience> JobsExperiences { get; set; }
}

Now I created a ClientServices class where I put all methods that get or send data from and to the data base., ther I have this code, which is working randomly, I´ll try to explain clearly.

    internal Client GetClient(string userId, bool lazyLoadingEnabled = true)
    {
        using (var context = new ApplicationDbContext())
        {
            context.Configuration.LazyLoadingEnabled=lazyLoadingEnabled;

            var client = (from _client in context.Client
                          where _client.ApplicationUserId == userId
                          select _client).FirstOrDefault();

            return client;
        }
    }

My objective some cases is to retrieve just the client attributes, and sometimes all attributes including navigation properties.

In my controller I have a line like this

var client = uuc.GetClient(user.Id, false);

or this

var client = uuc.GetClient(user.Id);

When I run the first sentence, the navigation properties are initialized but all has Count=0, even when my DB has records associated. I think, if lazy loading is disabled, it means eager loading is enabled, but it seems not. However, there is no Load() Method in the navigation properties to force load.

When I run the second sentence, the navigation properties throws an exception 'client.ScholarLevelDetails' threw an exception of type 'System.ObjectDisposedException'. This is thrown one line after the sentence, loking at the navigation properties in the watch. However, and this is the weirdest part, if I step back to the sentence and debug stepping into the method, All navigation properties are loaded correctly.

Why the code behaves differently if running at once than running stepping into the method? I presume the using statement scope finishes before that the navigation properties load, but why disabling lay loading doe snot retrieve them either? How can I code this to have a consistent behaviour?

1
5
2/6/2014 3:45:38 PM

Accepted Answer

I change the query code Ihad in Linq with this one.

    internal Client GetClient(string userId, bool lazyLoadingEnabled = true)
    {
        using (var context = new ApplicationDbContext())
        {
            context.Configuration.LazyLoadingEnabled = lazyLoadingEnabled;

            var client = context
                        .Client
                        .Include(s => s.ScholarLevelDetails)
                        .Include(s => s.JobsExperiences)
                        .Include(s => s.CapacitationCourses)
                        .Include(s => s.Relatives)
                        .FirstOrDefault(s => s.ApplicationUserId == userId);

            return client;
        }
    }

And now it works. however I still have some questions I´d lve to discuss with you readers and colleagues.

Why plain Linq doesn´t work? Why it doesn matter if lazyloading is enabled or not, this code works the same everytime?

6
2/6/2014 4:39:58 PM

Popular Answer

The problem is that your context fell out of scope before the navigational properties could be loaded.

To do what you want, you would need to change how you are working with your context, or just eager load the entities you are going to need via table join(s) using the query syntax that you are using, or via .Include() lambda expressions if you use the lambda query syntax.

    using (var context = new ApplicationDbContext())
    {
        context.Configuration.LazyLoadingEnabled=lazyLoadingEnabled;

        var client = (from _client in context.Client
                      where _client.ApplicationUserId == userId
                      select _client).FirstOrDefault();

        return client; //at this point, your context is gone, and no 
        //navigational properties will be loaded onto your client object, 
        //even if you try to navigate them. You may even get exceptions if
        //attempting to navigate to some properties.
    }

Here is a join example:

var client = (from _client in context.Client
              join t in context.Table on _client.Val equals t.val //this will eager load Table property on client.
              where _client.ApplicationUserId == userId
              select _client).FirstOrDefault();


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