Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting "attempt was made to lazy-load navigation property on detached entity" despite eager fetch

Tags:

ef-core-2.1

I'm using Entity Framework Core 2.1.2 with lazy loading enabled and am performing a query using AsNoTracking. I'm using Include to bring in my navigation property (a collection).

If all my entities have at least one child in their collection then it all works fine.

However, if any of my entities have no children then I get an error:

System.InvalidOperationException: Error generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.DetachedLazyLoadingWarning: An attempt was made to lazy-load navigation property 'Children' on detached entity of type 'ParentProxy'. Lazy-loading is not supported for detached entities or entities that are loaded with 'AsNoTracking()'.'

Here's a reproduction of the problem (it can be run from a console app after using NuGet to bring in Microsoft.EntityFrameworkCore 2.1.2, Microsoft.EntityFrameworkCore.Proxies 2.1.2, Microsoft.EntityFrameworkCore.InMemory 2.1.2):

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace LazyLoadingIssue
{
    public class Parent
    {
        public int Id { get; set; }
        public string ParentName { get; set; }
        public virtual ICollection<Child> Children { get; set; }
    }

    public class Child
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public virtual Parent Parent { get; set; }
        public string ChildName { get; set; }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            SetupDatabase(setupToFail: true);
            PerformTest();

            Console.WriteLine("Press any key to finish");
            Console.ReadLine();
        }

        private static void PerformTest()
        {
            using (var db = new MyContext())
            {
                try
                {
                    IQueryable<Parent> parents = db.Rounds.Include(r => r.Children).AsNoTracking();
                    foreach (Parent parent in parents)
                    {
                        Console.WriteLine($"Parent (Id={parent.Id}) '{parent.ParentName}'");
                        foreach (Child child in parent.Children)
                        {
                            Console.WriteLine($"  - Child (Id={child.Id}, ParentId={child.ParentId}) '{child.ChildName}'");
                        }
                    }

                    Console.WriteLine("** WORKED **");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("** FAILED **");
                    Console.WriteLine(ex);
                }
            }
        }

        private static void SetupDatabase(bool setupToFail)
        {
            using (var db = new MyContext())
            {
                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                var parent1 = new Parent
                {
                    ParentName = "First sample parent (has children)",
                    Children = new List<Child>
                    {
                        new Child {ChildName = "child-1"},
                        new Child {ChildName = "child-2"},
                        new Child {ChildName = "child-3"}
                    }
                };
                var parent2 = new Parent
                {
                    ParentName = $"Second sample parent ({(setupToFail ? "with no children" : "has children")})",
                    Children = new List<Child>()
                };
                if (!setupToFail)
                    parent2.Children.Add(new Child {ChildName = "child-4"});
                db.AddRange(parent1, parent2);
                db.SaveChanges();
            }
        }
    }


    public class MyContext : DbContext
    {
        public DbSet<Parent> Rounds { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
//                .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=_ModelApp;Trusted_Connection=True;Connect Timeout=5;ConnectRetryCount=0")
                .UseInMemoryDatabase(databaseName: "_modelApp")
                .UseLazyLoadingProxies()
                ;
        }
    }

}

Am I doing something wrong? Or is this a bug in EF Core? (I've posted an issue there too.)

like image 219
Jonathan Moffatt Avatar asked Aug 31 '18 02:08

Jonathan Moffatt


People also ask

How do I stop lazy loading in Entity Framework?

We can disable lazy loading for a particular entity or a context. To turn off lazy loading for a particular property, do not make it virtual. To turn off lazy loading for all entities in the context, set its configuration property to false.

What is the difference between eager and lazy loading in Entity Framework?

Lazy Loading vs. Eager Loading. While lazy loading delays the initialization of a resource, eager loading initializes or loads a resource as soon as the code is executed. Eager loading also involves pre-loading related entities referenced by a resource.

Is eager loading better than lazy loading?

Lazy Loading vs. Eager Loading. Eager loading generates all web page content as soon as possible, while lazy loading delays the display of non-essential content. Lazy loading is a great option for large page files with more content.

Which entity will allow lazy loading?

EF Core will then enable lazy loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from. For example, in the following entities, the Post. Blog and Blog. Posts navigation properties will be lazy-loaded.


1 Answers

There's no bug. Since you are not tracking changes using .AsNoTracking, lazy loading will never work. You can use .Include("ChildEntity") in your query or give up of using .AsNoTracking.

like image 111
Robertson Solani Vicente Avatar answered Sep 21 '22 17:09

Robertson Solani Vicente