5

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?

Ricker Silva
  • 1,137
  • 4
  • 17
  • 37

5 Answers5

6

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?

Ricker Silva
  • 1,137
  • 4
  • 17
  • 37
  • Your line `context.Configuration.LazyLoadingEnabled = lazyLoadingEnabled;` isn't doing anything for you. You are eager loading all of your navigation properties, and that's why it worked. Plain linq does work, but without seeing the examples you tried, we wouldn't be able to tell you why your implementation didn't work. – Joe Brunscheon Feb 06 '14 at 16:41
  • Besides what I posted in the question, what is needed to see if we can unravel the reason why Linq didn´t work? COunt on me to post everything I can – Ricker Silva Feb 06 '14 at 16:58
  • It appears that you would need to select an anonymous type that included all of the data you need if you wanted to use the query syntax. http://stackoverflow.com/questions/6761104/how-does-linq-expression-syntax-work-with-include-for-eager-loading – Joe Brunscheon Feb 06 '14 at 17:10
  • Gonna try that at a later moment, sure I´ll be in that portion of code sooner again. However. what is the real purpose of lazyLoadingEnabled configuration key? I mean, it proof to be almost irrelevant for any of the queries I tried to perform. – Ricker Silva Feb 06 '14 at 17:38
  • 1
    Lazy loading is faster than eager-loading your data. However, for it to be used, you need to keep the database `context` around for longer than just the lifetime of the initial query. This can be accomplished by changing your database interactions through EF into a Repository pattern that manages your context. Performance: http://stackoverflow.com/questions/15778375/lazy-vs-eager-loading-performance-on-entity-framework. – Joe Brunscheon Feb 06 '14 at 17:54
  • That´s a good post. and practically closes the discussion down to a proyect basis decision. Even more, an action basis decission, as in my case, where I have more data access methods. How can I perform a lazy loading on demmand if I have no longer the load Method? I mean, just a new call to the context and filling the property could be a good practice here? ANd finally, sorry tosound like abroken record. Lazyloadingenabled property is useless? I mean, my code is eager regardless its value – Ricker Silva Feb 06 '14 at 18:18
  • 1
    It's not useless, but it's generally not something you need to mess with. What I was saying was that, in your case, it was in fact useless, since you were eager loading. http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontextoptions.lazyloadingenabled(v=vs.100).aspx – Joe Brunscheon Feb 06 '14 at 18:49
3

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();
Joe Brunscheon
  • 1,949
  • 20
  • 21
  • I tried it with one of the navigation properties, with both true and false in the lazy loading but doesn´t work. When lazyloading is enabled the nav properties are not loaded within context lifetime; when is disabled, properties are initialized but empty. – Ricker Silva Feb 06 '14 at 16:28
2

You should use Include method for properties.

_context.Client.Include(c=>c.JobsExperiences) ... and all props like this

but for you is better not to use lazy loading. Cause context become inactive after you return from method.

Sergey Shuvalov
  • 2,098
  • 2
  • 17
  • 21
  • just add that after the lazy loaidng line within the using, one for each nav property but it doesn´t work. I tried lazy loading enabled and disabled. And in both cases nav properties are initialized but empty. – Ricker Silva Feb 06 '14 at 16:25
1

Using EF 6, I had to use (lamba's and Client keyword is unavailable):

    using (var context = new SyntheticData.EF.DBContext())
    {
        var item = (from t in context.TEMPLATEs
                        .Include("DATASET")
                        .Include("COLUMNs")
                        .Include("SORTs")
                        .Include("FILTERs")
                    where t.USERID == identityname && t.ID == id select t).FirstOrDefault();
        return item;
    }

This filled in relationship classes with syntax like:

  • item.DATASET.xxx
  • item.COLUMNs[0].xxx

The "using (var context" construct is good practice because it insures your connection to the database will be released back to the pool. If you don't, you can end up running out of conenctions for busy systems.

jlo-gmail
  • 4,453
  • 3
  • 37
  • 64
0

Just in case this helps someone I was not getting any navigation properties and my problem seemed to be that EF did not properly hookup the properties because I was using an interface as the navigational property type and I needed to use actual type. Unless anyone knows how to use annotations or something to tell EF the actual type that the property is mapped to.

Andrew Hawes
  • 386
  • 1
  • 5
  • 17