1

i rare use EF. so i have a question. if we do not use virtual keyword with dbset then lazy load will not work?

i read a article from this link https://stackoverflow.com/a/24114284/5314244 they provide code like

public class AppContext : DbContext
{
    public AppContext()
    {
        Configuration.LazyLoadingEnabled = true;
    }

    public virtual DbSet<AccountType> AccountTypes { get; set; }
}

public class AccountType
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AccountCode> AccountCodes { get; set; }
}

public class AccountCode
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Guid AccountTypeId { get; set; }
    public virtual AccountType AccountType { get; set; }
}

they said :

The virtual keyword on the navigation properties are used to enable lazy loading mechanism, but the LazyLoadingEnabled property of the configuration must be enabled.

The virtual keyword on AccountType::AccountCodes navigation property will load all account codes the moment there is a programmatically access to that property while the db context are still alive

if we declare this code without virtual keyword public DbSet<AccountType> AccountTypes { get; set; } then when this line execute var accountCodes = accountType.AccountCodes; what will happen?

Error will thrown or null will be stored in accountCodes variable?

second question what is default in EF---> lazy load or eager loading? how many type of loading option available ?

thanks

  • 1
    *if we do not use virtual keyword with dbset then lazy load will not work?* it seems not, as you can't override the method. please note that EF6 uses proxy classes to enable lazy load. While EF7 (or some prefer says EF Core) did not have the lazy load feature - hence unnecessary. – Bagus Tesa Oct 09 '17 at 11:38
  • sorry not clear what you try to say. are you try to say if we do not use virtual keyword then also lazy load is possible.....if yes then why we should use virtual keyword? –  Oct 09 '17 at 11:46
  • 1
    i should have rephrased my clues. well, if `virtual` is not specified, Entity Framework 6 won't be able to create proxy classes which will handle the lazy load.. The proxy classes are basically a subclass of your model class that overrides those `virtual` methods. This however, different on Entity Framework 7 (Core). – Bagus Tesa Oct 09 '17 at 11:48
  • can you give me a link from where i can how proxy class look which is sub class of my model class generated by EF. thanks –  Oct 09 '17 at 11:58

1 Answers1

1

The approach you want to achieve, lazy loading, means that the data provider, in your case Entity Framework, retrieves certain data at the time you need it. If, for example, you access all account types you probably don't need to access all account codes for these account types thus retrieving them from the database would be unnecessary.

If you had to write the SQL yourself, you would most likely write a plain select statement without joining the child relations:

SELECT * FROM AccountTypes;

Another use-case on the other hand might need access to this data so you would either end up with eagerly loading it which results in a join operation...

SELECT * FROM AccountTypes JOIN AccountCodes ON AccountCodes.AccountTypeId = AccountTypes.Id;

...or to rely on Entity Framework's lazy loading ability.

The latter case requires Entity Framework to do the following: First, selecting all account types and then, at the time your user-code accesses the account codes, emitting another select statement to retrieve the codes for the respective account type. To do so, Entity Framework needs a hook, i.e. it must know when you access the navigation property, which is in general impossible because this would need additional code in the getter of this property:

public class AccountType
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AccountCode> AccountCodes
    { 
        get { /* Load child data here */ }
        set { /* ... */ }
    }
}

Unfortunately Entity Framework is unable to alter the POCO class because it's your user-code, but what it can do is deriving from your class and overriding the property to inject it's magic. For this to work it creates what is called a proxy, i.e. it creates a class at runtime which derives from your POCO class, like:

public class AccountTypeProxy : AccountType
{
    public override ICollection<AccountCode> AccountCodes
    { 
        get { /* Load child data here */ }
        set { /* ... */ }
    }
}

So what you get when you retrieve the account types is actually a bunch of proxy instances, which you can confirm during debugging.

If you would not add the virtual keyword to the navigation property or if you sealed the class, Entity Framework could not override the property or could not derive from the class at all and thus lazy loading would not work.

Peit
  • 882
  • 6
  • 17
  • thanks for your answer. when EF retrieve data by joining when we eager load? in case lazy load EF hit db twice and generate two sql select to get data? –  Oct 09 '17 at 12:14
  • I am not sure if I understood you correctly, but yes: Lazy loading usually emits multiple statements one after the other so it produces multiple roundtrips. Entity Framework generates joins when it is necessary (like when you already access a navigation property in a where criteria or similar) but then lazy-loads the remainder afterwards in subsequent round-trips. – Peit Oct 09 '17 at 12:19
  • this is not clear what you said "So Entity Framework generates joins when it is necessary (like when you already access a navigation property in a where criteria or similar) but then lazy-loads the remainder afterwards in subsequent round-trips" can elaborate with example code in your answer. –  Oct 09 '17 at 12:22
  • 1
    If you write AccountTypes.Where(accountType => accountType.AccountCodes.Any(accountCode => accountCode.Name == "...")) it will probably already create a join. – Peit Oct 09 '17 at 12:23