4

I have Backpack and Book entities. Book references Backpack (one to many). I am creating an instance of Backpack and bunch of Books. So in this case backpack has bunch of books. I am saving those entities to the db. I am verifying that those got saved to the db. When I try to load backpack it loads fine and all the properties are set except the navigation properties. I am also checking that LazyLoading is not disabled. My navigation properties has the virtual keyword. I am not sure what I am doing wrong. If I try to Load the backpack with Include() it loads the books:

dbContext.Backpacks.Where(b=>b.Name!="").Include("Books").FirstOrDefault()

I am trying to figure out why it is not loading the books lazily? I have the same problem with loading the book. When I load the book, it doesn't have the backpack attached. I see that the BackpackId is there.

In my property getter/setter I have some logic that will be fired, but I am not sure how that could be a problem.

Dilshod
  • 3,189
  • 3
  • 36
  • 67
  • Can you show your entities? – DavidG Apr 19 '17 at 21:38
  • Is ProxyCreation also enabled? It is necessary for Lazy Loading to work – Sentry Apr 20 '17 at 06:04
  • @Sentry I just checked and ProxyCreation was enabled. I set it to false, but still problem didn't go away. – Dilshod Apr 20 '17 at 12:17
  • ProxyCreation must be true, not false. Is the context still open? Can you show more code, especially from where you create the context until you dispose it? – Sentry Apr 20 '17 at 17:32
  • @Sentry I am not allowed to post the code on the web, because it is not just two entities. If you are OK we could skype and I could share my screen with you. Thanks for the help! – Dilshod Apr 20 '17 at 17:46
  • Unfortunately, I don't have the free time for that. You could (and should actually) try to build an [MCVE](https://stackoverflow.com/help/mcve) that demonstrates and reproduces your problem without making any sensitive information public. – Sentry Apr 20 '17 at 18:11
  • @Dilshod You need to post more code. Your description of the problem males absolutely no sense. – Seany84 Apr 20 '17 at 22:09
  • @Seany84 I am working on that now. Once I can reproduce the issue on a smaller project I will post it. – Dilshod Apr 21 '17 at 13:35

4 Answers4

4

With the limited information at hand, I can see the following explanations for your problem.

Lazy Loading or Proxy Creation are disabled

Make sure that lazy loading and proxy creation are enabled, the first doesn't work without the latter.

dbContext.Configuration.ProxyCreationEnabled = true;
dbContext.Configuration.LazyLoadingEnabled = true;

(see this SO post for details)

Accessing the entities after disposing the context

Simplified, Lazy Loading works like this:

  • Whenever you retrieve an entity from the context, you actually get an object of an automatically created subclass of the class you expected that overrides your virtual navigation properties, which is the proxy.
  • Whenever you then access said navigation properties, the proxy will access the database and load the linked entities when needed.

This last step is, of course, only possible if the entity/proxy is still attached to the context and can therefore query the database to retrieve said objects.

using( var dbContext = new MyContext() )
{
    dbContext.Configuration.ProxyCreationEnabled = true;
    dbContext.Configuration.LazyLoadingEnabled = true;
    // Note: You can modify or derive from `MyContext` so that this is
    //       done automatically whenever a new `MyContext` is instantiated

    var backpack = dbContext.Backpacks.Where(b=>b.Name!="").FirstOrDefault();

    // This should work
    var firstBook = backpack.Books.FirstOrDefault();
}

// This will probably not, because the context was already disposed
var firstDrink = backpack.Drinks.FirstOrDefault();

I hope this helps, but feel free to provide more information if it doesn't

Community
  • 1
  • 1
Sentry
  • 4,102
  • 2
  • 30
  • 38
  • 1
    ProxyCreation was enabled by default. I though that being enabled makes the LazyLoad issue. So in my case ProxyCreation is enabled and LazyLoading is enabled. I am going to test some things and get back. – Dilshod Apr 20 '17 at 19:15
4

After a several days of debugging, finally figured out the problem. As people mentioned above, you have to enable the LazyLoading and ProxyCreating. I had the issues even after having the enabling the LazyLoading and ProxyCreating. Also make sure you declare your navigation properties as virtual, otherwise EF will not be able to load entities lazily.

So the issue I had was, EF wasn't crating Proxies because of my entity didn't have a public or protected constructor with NO parameters. After creating public (in my case protected) constructor without parameter it worked.

NOTE: Not having public/protected constructor without parameters will not affect the eager loading.

Here is a link that explains the requirements for the LazyLoading

Dilshod
  • 3,189
  • 3
  • 36
  • 67
2

Eager loading is achieved using the Include() method and as a result you are forcing eager loading by using Include("Books").

Change this:

dbContext.Backpacks.Where(b=>b.Name!="").Include("Books").FirstOrDefault()

to this:

dbContext.Backpacks.Where(b=>b.Name!="").FirstOrDefault()

You should now see that the Books are no longer being loaded eagerly.

Reference:

  1. http://www.entityframeworktutorial.net/EntityFramework4.3/eager-loading-with-dbcontext.aspx
  2. https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
Seany84
  • 5,526
  • 5
  • 42
  • 67
  • I know Include will load the entities eagerly, I just tested it with Include to see if it works. If I try to load the backpack without using the include then I am not getting the books on navigation list. – Dilshod Apr 20 '17 at 12:18
  • @Dilshod You need to post more code. Your description of the problem males absolutely no sense. – Seany84 Apr 20 '17 at 22:09
1

Steps I had to do using .NET Core 3.1 and Microsoft.EntityFrameworkCore 3.1.5...

1) Add the Microsoft.EntityFrameworkCore.Proxies NuGet package

enter image description here

2) Configure your DbContext to UseLazyLoadingProxies (in Startup.cs)

public void ConfigureServices(IServiceCollection services)
{
   ...
    services.AddDbContext<DataContext>(optionsBuilder =>
    {
        optionsBuilder
            .UseLazyLoadingProxies() // <--- You need this bit
            .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    });
   ...
}

3) Mark all appropriate properties (those you want lazy loaded) as virtual

public class MyEntity 
{
    public virtual OtherEntity? { get; set; }  // Lazy loaded coz `virtual`

    public ICollection<OtherEntity> { get; set; }  // NOT lazy loaded coz not `virtual`
}

Oliver Pearmain
  • 19,885
  • 13
  • 86
  • 90