How does Entityframework’s Tracking / Chache work?

Asked

Viewed 868 times

3

Every time I used the Entityframework in a project, to add a record in the database through it, of a class that contains class type members (usually on account of Foreignkeys), I would do this:

  1. After the entity instance, it filled the properties of primitive types;
  2. Set class type properties to null.

And so it always worked for me, until in a specific case like the one I presented in that question: Entityframework complaining of Identifier duplicity even with the property being null.

There are some issues here that address this issue, always isolated cases and a way around.

  1. How, after all, does the Tracking of objects of Entityframework?

  2. When working with ASP.NET MVC (web projects) it works in a way that does not keep information in memory. That is, the context of Entityframework and all other things are instantiated when a request is made and destroyed when it finishes returning its result. So whenever I make a POST for a Action, that will make the bind of a class as a parameter of Action, I know I have an instance not monitored by Entityframework, right? Why then a simple:

    if (ModelState.IsValid)
    {
        _context.Add(model);
        _context.SaveChanges();
    }

Works?

  1. As in the case of the question I referenced, because there were monitoring problems?

  2. What would be the most "complete" way to deal with Inserts and Updates and have no problems with the Tracking object?

  3. Enable or Disable the LazyLoading and creation of proxies and monitoring of changes (AutoDetectChangesEnabled) affects how the Tracking?

2 answers

3

It’s a lot of questions to answer, and a little complicated, but I’ll try

1 - Entityframework Changetracking now monitors an instance of an entity after it is "attached" (attached) to an instance of a Dbcontext.

2 - When you call a method from a Dbset, for example, Add(T model), the Entityframework will attach the object to Dbcontext and mark its state as Added.

Remembering that you cannot attach the same object to two distinct instances of Dbcontext

You can check the current state of an object through the Entry(Object instance method).

3 - Entityframework uses the Identity Map Pattern. With this, an instance of Dbcontext can only have a single instance of an object with the same key (Primary Key) attached to the Changetracker. For example, if you loaded the record with the Id = 1 of a Person entity, you cannot attach another object with the same Id = 1, otherwise you will have problems.

4 - The best way to treat Inserts and updates is to isolate operations with entities in a manner similar to a transaction. An instance of a Dbcontext must have a short life, that is, you instantiate Dbcontext, do what you need and then discard the instance. I need to improve the answer to this question 4, but I’ll say that there is a very good Nuget package to treat more complex updates, called Graphdiff.

5 - I do not use Lazyloading, nor recommend, I prefer to have full control over what I want to load from my database and when necessary I upload the data I will need, using Include(). So by default I disable. Lazy Loading requires the creation of proxy classes, and requires the Entity properties to be virtual. Regarding Autodetectchanges, if disabled, you will have to call the Detectchanges() method manually so that Entityframework discovers which properties have been modified when generating SQL updating entities.

  • +1 by Graphdiff. Sensational the package.

  • Unfortunately I can no longer interact on the other issue. I can’t vote here either. Just mark the answer as accepted. Well, on the other question, about the method AtualizarPrecoProdutoAsync, truly get the one instance of the product there to then change the property Preco and then record the change. However, error happens in the method AddAsync, at that time the AtualizarPrecoProdutoAsync.

  • 1

    In fact, it is not a good practice to pass an instance of a context as a parameter in several layers of your application (controller, service, repository, etc.). Create your Dbcontext only in the implementation of your Repository. Unitofwork + Repository + Entityframework I do not recommend because you never know what was done in a Dbcontext in the other layers, you get lost very easy. http://rob.conery.io/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/

  • About the AtualizarPrecoProdutoAsync, reiterate, is what you said yes, exactly that! I messed up the debug with the async's of life. Thank you so much for your explanation.

3


How, after all, does the Tracking of objects of Entityframework?

First it is important to explain that a context exists during the life cycle of a Controller, in the specific case you are working using MVC. The values loaded by Controller are only expired when the Controller in question suffers a Dispose, making the context also suffer a Dispose.

When loading an object (in English they call entry, and I’ll call it a record), the record is loaded with the original values of the database, and they stay there even if you change the record. This makes it clear that when modifying a record, the Entity Framework knows that this record has already been loaded and knows what to change.

The record is usually located by its key. To understand this better, you need to understand the implementation of DbSet<T>. He’s an observable collection (ObservableCollection). By calling SaveChanges, what is being done is:

  1. Check if any collection is marked as changed;
  2. For each record that has been marked as changed, the Entity Framework performs some logical operation to insert, update, or delete a record in the database. The executed instruction is linked to EntityState, one Enum used to identify the state of the registry within the Entity Framework.

So, whenever I do a POST for an Action, which will bind a class according to the Action parameter, I know that I have an instance not monitored by the Entityframework, right?

Wrong. If the record has already been loaded previously (in a Edit, for example), it is already being "observed". When applying a POST to save the edit, the object coming from View is synchronized as follows:

context.Entry(obj).State = EntityState.Modified;

Note that the context here is a new context, without the information of the previous request. When I say "the record has already been loaded," I say it was loaded on screen, not on Controller. When performing the POST, you are sending information and the context works optimistically, that is, the record is again loaded in Entry() if it has not already been loaded.

In this line are made two things:

  1. Locate a record based on another;
  2. Update the values of the observed record based on the on-screen records.

The case of Add is simpler. You are telling the context that the object does not exist, so the context considers the object as new.

As in the case of the question I referenced, because there were monitoring problems?

I’ll answer the question.

What would be the most "complete" way to deal with Inserts and Updates and have no problems with the Tracking object?

  • Not implementing repositories (Entity Framework already implements repositories);
  • Not implementing Service layer (Controller is already a different layer of services, but is);
  • Not instantiating two contexts unnecessarily;
  • Not selecting objects in one context and trying to save in the other;
  • Using the simplest code possible, as (almost) all examples illustrated here and on other specialized websites.

Enable or Disable the LazyLoading and creation of proxies and monitoring of changes (AutoDetectChangesEnabled) affects how the Tracking?

Lazy loading and monitoring are independent things. One does not affect the other and vice versa.

AutoDetectChangesEnabled does the tracking does not work. Any change has to be invoked by manual methods.

  • 2

    Note that the answer you gave assumes that a Visual Studio template controller is being used that adds a Dbcontext at the beginning of the request and calls a Dispose at the end. Which is not always the case. If I am using a WPF or Windows Forms application, for example, there are no Controllers, that is, the life cycle of a Context is not related to a Controller. But I like your teaching to explain, I have to improve on my answers.

  • 1

    What an excellent explanation, thank you! But I did not understand the conroller life cycle. I perform a Edit and I get a record (I thought that when I got my page loaded in the browser the controller, and of course the context, would have been destroyed on the server), and when I did the POST that everything would already have been lost and with a new controller instance and context there would be no records already under observation by entityFramework. Then the Controller is only destroyed when I, in my session, switch from one controller to another by browsing?

  • On the other issue what I do is load the products to create a DropDownList with the products, but I use the AsNoTracking(). Already the ProdutoId come from View, the value selected in DropDownList.

  • From what I understand here what-is-the-Lifetime-of-a-Asp-net-mvc-controller, the controller (and consequently the Dbcontext created for it, are created with each request and destroyed when the request happens. Then a GET at the Action Edit and then a POST at the Action [HttpPost] Edit(Model model) is related to create and destroy the controller (and Dbcontext) twice. Anyway, each time I am not understanding more... rs =(

  • In the above comment I pointed out that the life cycle of Dbcontext has nothing to do with the life cycle of a Controller. What happens is that by default a Dbcontext is instantiated with the Controller.

  • @Severo That’s right. To each request, the Controller destroys the DbContext at the end of the request. A part of the reply did not make this clear, so I will edit.

  • @Jonepolvora In his case, he makes it clear that he is using ASP.NET MVC, not WPF, but the principle is the same. About your answer, it is complementary to mine and could very well be the accepted answer. I just wish you’d learn the Markdown syntax. It is quite simple and can help a lot when formatting an answer. The links for help with Markdown are next to the response panel.

  • @Gypsy I know markdown, it was laziness even rsrs. I will start to improve the formatting and didactics in the answers. Valew.

  • 1

    um contexto existe durante o ciclo de vida de um Controller. This is true only if you do the right thing. That is, if the context is not a class field but an instance field.

  • @Brunocosta It’s usually like this, right? Something else. Even if it’s classy, the Dispose discards the context at the end of the execution of the request, with the discard of the Controller.

  • Related: http://answall.com/questions/51536/quando-usar-entity-framework-com-repository-pattern/80696#80696

Show 6 more comments

Browser other questions tagged

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