What is the difference between using virtual property or not in EF?

Asked

Viewed 4,038 times

24

I have my models

public class Cliente
{
   public int Id {get;set;}
   public string Nome {get;set;}
}

and

public class Pedido
{
   public int Id {get;set;}
   public int ClienteId {get;set;}
   public virtual Cliente Cliente {get;set;}
}

Following the example above, what is the difference between

public virtual Cliente Cliente {get;set;}

and

public Cliente Cliente {get;set;}

I always notice that when using virtual My Client model is loaded

for ex:

var pedido = dbo.Pedidos.Find(1);

If I try to access pedido.Cliente he’s not null when you’re virtual

What’s the difference? Like the EF makes the query ? Which way do I get the most performance?

3 answers

39


Lazy Loading

Lazy Loading is the mechanism used by persistence frameworks to load demand information. This mechanism makes lighter entities, as their associations are only loaded in the when the method making the associative data available is called. So when objects are returned by a query, the related objects are not loaded at the same time, instead they are automatically loaded when the navigation property is accessed.

The modifier virtual is used by the RU to make the Lazy Loading, that needs to create proxy instances that will be replaced in these virtual properties.

So you have the impression that this object is always loaded. But in fact it is not. A virtual property is only loaded via Lazy loading at the time when there is the first reference to that property. At this time the EF performs a new query in the database requesting only the data below the hierarchy of the object of this property.

This can be noticed by debugging your code in Visual Studio, the property virtual will be loaded at the exact moment you verify its value.

The use of Lazy Loading may cause performance problems. In your example, if you make a query that returns, say, 100 Pedidos, without explicitly loading clients (Eager loading), when referencing any information from Cliente, for each different client, a new query will be made in the database. Therefore in this case you could have 100 new queries to the database.

var pedidos = db.Pedidos.ToList(); // não traz nenhuma informação de cliente
foreach (var pedido in pedidos) 
{
    var nome = pedido.Cliente.Nome; // aqui é feita a carga por Lazy Loading
}

Eager Loading

To avoid this effect it is possible to inform that you want the EF to do Eager Loading of customers using a clause Include.

var pedidos = db.Pedidos.Include(m => m.Cliente).ToList(); // inclui cliente na query
foreach (var pedido in pedidos) 
{
    var nome = pedido.Cliente.Nome; // os dados de cliente já estão carregados
}

This way the SQL query will include clients in the query.

In cases where we do not use the modifier virtual we will only be able to access the Cliente for Eager Loading.

The EF uses Lazy Loading by default in properties marked with the modifier virtual, otherwise it is switched off and so the reference to the object is null (except when we use Eager loading through Include).

It is possible to configure the EF so that it does not work with Lazy Loading even when we have virtual properties.

public class MeuContexto : DbContext 
{ 
    public MeuContexto () 
    { 
        this.Configuration.LazyLoadingEnabled = false; 
    } 
}
  • 1

    all the answers were great and enlightening, however yours passes an easy explanation and with examples, so the choice of the best, thanks @iuristona

13

I agree with the reply by @Maniero,and I have a few points to add.

Although not working effectively with EPH, I know he’s like Nhibernate (ORM similar to EF), creates a derivative class named proxy, with the main aim of creating triggers for getters and setters in its entity, so that the ORM can control, how, how and whether an attribute/property of its persistent entity has been changed, in order to be able to take appropriate actions in relation to that entity.

So if you don’t leave an attribute virtual, you are explicitly saying that the same cannot be overwritten (override) in a class derived from yours, so the ORM will not be able to implement a proxy for this attribute/property and therefore cannot implement triggers.

Note: with Nhibernate it is mandatory to use the virtual modifier in all properties mapped to the ORM, if this does not occur an error is generated at the time of creation of the settings of the same. So he needs the creation of proxis for its use, already the EF, not explicitly needing the virtual (hence, no longer understand how it does, a possible explanation may be the comment from @Tobias Mesquita).

  • 2

    In the case of EF these triggers are necessary to do the Lazy load and tracking of the entities, if you choose to disable the tracking and go to consult the relationships manually or through Eager load, it is not necessary to keep the virtual.

  • 5

    kittens oo kkk

  • 2

    @Rod, we had not even seen, kkkkkk. My mistake, I missed some Portuguese lessons, I will correct. I was thinking about kittens I think, when I was writing. Hehe.

  • @Rod, wow and put 2 times. kkkkk. Missed any more? Hehe.

11

It creates a class derived from your class to work internally. This is a standard called Dynamic proxy. This makes data navigation easier.

Polymorphism

How the class is derived the call needs to be made in the method get of the derived class property that will have the effective load logic of the die. Without the virtual there is no polymorphism and the method called is that of its class Pedido which does not contain any data load logic, so it is consistent to have null in this case.

The virtual is used precisely to create an indirect, so the method to be called is not called directly but rather through a table that will indicate which is the correct method to be called in the class hierarchy according to the concrete data being manipulated.

If you call a method that has not been defined as virtual, the call is made directly and fixed, ie if the method to be called is the get Cliente class Pedido, it is always himself who will be called and there will be no way to call the get Cliente of a derived class even if the object is of the derived class type.

Completion

The execution of query will be the responsibility of get Cliente existing in the internal class of the RU derived from its class Pedido. The data load occurs on demand, the so-called Lazy loading, and without the virtual the load should be done manually, perhaps in its class Pedido in immediate form Eager loading. To query is mounted with some form of LINQ (see Tobymosque comment below).

The performance of each depends on how you use it. In many cases the "lazy load" can save processing but in some access patterns this can turn against you.

  • 5

    is more than correct, in the case of EF, the object knows to which context it belongs and the context knows the Mappings, in the above case the property Cliente will perform context.Clientes.Find(this.ClienteId), as well as a property Pedidos in Clients (if added to the above template) will be translated as context.Pedidos.Where(pedido => pedido.ClienteId == this.Id)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.