2

I have the following Entities

public class A
{
    public int AId {get; set;}
}

public class B
{
    public int BId {get; set;}
    public virtual A Child1 {get; set;}
    public virtual A Child2 {get; set;}
}

with the following configuration

modelBuilder.Entity<B>()
            .HasRequired(x => x.Child1)
            .WithMany()
            .Map(x => x.MapKey("Child1Id"))

modelBuilder.Entity<B>()
            .HasRequired(x => x.Child2)
            .WithMany()
            .Map(x => x.MapKey("Child2Id"))

For some reason Entity Framework does not lazy load the Child1 or Child2 properties when requesting an Entity of object B. That is

var b1 = context.Bs.FirstOrDefault();
Assert.IsNull(b.Child1) // true
Assert.IsNull(b.Child2) // true

but it works if i explcitly loads them.

var b2 = context.Bs.Include(x => x.Child1).Include(x => x.Child2).FirstOrDefault();
Assert.NotNull(b2.Child1) // true
Assert.NotNull(b2.Child2) // true

Does anyone know why the properties does not get lazy loaded?

EDIT

It seems that

context.Bs.FirstOrDefault()

returns the entity itself and not a proxy type. This indicates that the property ProxyCreationEnabled is false but I have double checked it and it is set to true.

EDIT 2

Ok, finally found the problem. I had set Bs constructor to private which of course makes it impossible to extend the object with a proxy class. And hence, the navigation properties where set to null.

olif
  • 2,781
  • 1
  • 21
  • 22
  • They have been lazy loaded...... because they don't exist (the lazy part) – Callum Linington Aug 10 '16 at 12:18
  • Read this excellent article on [loading entities from msdn](https://msdn.microsoft.com/en-gb/data/jj574232.aspx) – Callum Linington Aug 10 '16 at 12:20
  • No the properties exists, it works if I explicitly includes them in the query. – olif Aug 10 '16 at 12:20
  • 1
    Have you enabled lazy loading in EF options? – Nick L. Aug 10 '16 at 12:23
  • Yes it is enabled. – olif Aug 10 '16 at 12:24
  • What does `context.Configuration.LazyLoadingEnabled` return right before you do `var b1 = context.Bs.FirstOrDefault();`? – Good Night Nerd Pride Aug 10 '16 at 12:27
  • It all depends on where you're accessing the navigation properties from. If you're just returning an `IQueryable` from your query, you'll then still be able to access the navigation properties within - hence "lazy" loading (only accessing from the query, when necessary). If you're flattening it to an `IEnumerable` (e.g. `ToList()`, `FirstOrDefault()` etc.), this is where you'd have to use eager loading to populate the properties before the objects are flattened. Your question is unclear, could you please clarify what exactly you're not sure about? – Geoff James Aug 10 '16 at 12:33
  • FirstOrDefault() on an IQueryable (as in my example above) returns a proxy class as far as I understand it. As long as the context is alive, the navigation properties should be lazy loaded when navigating to the property. – olif Aug 10 '16 at 12:51

1 Answers1

1

I have modified your code so that working with lazy loading. just copy paste and everything should be fine.

Check the b1 with VS before the write line, you will see the lazy loaded objects and do not scare from the long entity name this because I have enabled the proxy creation.

 public class Program
 {
     public static void Main(string[] args)
     {
         Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());

         using (var myDbContext = new MyDbContext("DefaultConnection"))
         {
             var a1 = new A();
             var a2 = new A();

             var b1 = new B
             {
                 Child1 = a1,
                 Child2 = a2
             };

             myDbContext.Bs.Add(b1);
             myDbContext.SaveChanges();
         }

         using (var myDbContext = new MyDbContext("DefaultConnection"))
         {
             var b1 = myDbContext.Bs.FirstOrDefault();
             b1.ToString();
             Console.WriteLine(b1.ToString());
         }
     }

     public class A
     {
         public int AId { get; set; }
     }

     public class B
     {
         public int BId { get; set; }
         public virtual A Child1 { get; set; }
         public virtual A Child2 { get; set; }
     }

     public class MyDbContext : DbContext
     {
         public DbSet<A> As { get; set; }
         public DbSet<B> Bs { get; set; }

         protected override void OnModelCreating(DbModelBuilder modelBuilder)
         {
             modelBuilder.Entity<B>()
                 .HasRequired(x => x.Child1)
                 .WithMany()
                 .Map(x => x.MapKey("Child1Id")).WillCascadeOnDelete(false);

             modelBuilder.Entity<B>()
                 .HasRequired(x => x.Child2)
                 .WithMany()
                 .Map(x => x.MapKey("Child2Id")).WillCascadeOnDelete(false);

             base.OnModelCreating(modelBuilder);
         }

         public MyDbContext(string connectionString)
           : base("name=" + connectionString)
         {
             this.Configuration.LazyLoadingEnabled = true;
             this.Configuration.ProxyCreationEnabled = true;
         }
     }
 }
Bassam Alugili
  • 13,927
  • 7
  • 49
  • 67
  • I will try it as soon as Visual Studio has been updated.. but is not ProxyCreationEnabled true per default? – olif Aug 10 '16 at 13:00
  • Yes correct 100% I just added to make sure that you did not disabled the proxy somewhere becasue if you set ProxyCreationEnabled = false this apporach will not working – Bassam Alugili Aug 10 '16 at 13:07
  • It's strange because your example works perfectly but my code does not and I cannot find any differences anywhere. Will continue to investigate this later this week. – olif Aug 10 '16 at 16:31
  • My problem is that the context does not return a proxy type but only the entity itself. I cannot understand what the difference is between your example and my code. – olif Aug 10 '16 at 19:27
  • 1
    @olif You have to make sure from the following: A POCO class must be declared with public access. A POCO class must not be sealed (NotInheritable in Visual Basic) A POCO class must not be abstract (MustInherit in Visual Basic). Each navigation property must be declared as public, virtual Each collection property must be ICollection ProxyCreationEnabled option must NOT be false (default is true) in context class – Bassam Alugili Aug 10 '16 at 21:18
  • Found the problem, see my edit above. Thanks for the help. – olif Aug 11 '16 at 08:45