Entity Framework: why does the database fall if the data is in context?

Why do we need a database to search for an entry that is already represented in ObjectContext?

So here is what I would think when you ask:

SiteUser someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0];

An undesirable example, but the idea is that I want to get the user from the table with identifier 1. Now suppose this is the first time, so I would assume that he should create a SiteUser list in context, query the database, and then populate her. Using the profiler, I see the following:

 SELECT [Extent1].[UserID] AS [UserID], [Extent1].[MainEmail] AS [MainEmail], [Extent1].[Password] AS [Password], [Extent1].[UserName] AS [UserName] FROM [TIDBA].[TI_USER] AS [Extent1] WHERE 1 = [Extent1].[UserID] 

Beautiful. He did what I expected, and in the SiteUser list (if I dig far enough using Watch), I see that there is one element in the SiteUser context list and this is a gigabyte object representing this data row.

Next, I want to change something without saving:

 someUser.UserName = "HIHIHI"; 

Now tell me for some reason that I want to capture him again. Using the same context (this is a strange example, but this is actually a test so I can prove it):

 someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0]; 

What I think is going to happen is to look at the SiteUser list in context, as this indicates the generated property. (If not null, the returned list) Then he would see if he would be there and return it. There is no database. Guess what the profiler says ...

 SELECT [Extent1].[UserID] AS [UserID], [Extent1].[MainEmail] AS [MainEmail], [Extent1].[Password] AS [Password], [Extent1].[UserName] AS [UserName] FROM [TIDBA].[TI_USER] AS [Extent1] WHERE 1 = [Extent1].[UserID] 

Hmm Okay, so I'm starting to think that maybe this is a gut check to see that something has changed in this data element and update the SiteUser object ONLY with values โ€‹โ€‹that I have not changed on the client. (Sort as context.Refresh (RefreshMode.ClientWins, context.SiteUser)) So I settled on:

 someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0]; 

Line and I change the value in the database (the "Password" column) and let it get into the database. Nothing has changed at the facility.

Something here does not seem right. It gets into the database to select an object that I have already wetted in context, but it does not apply to the change that I manually made in the database. Why does he even hit the database?

UPDATE Thanks to some of the links below, I managed to fight a bit and find this:

Combine option

There seems to be an enumeration that states how to handle loads. Now, after reading this, I saw this for MergeOption.AppendOnly:

Objects that already exist in the context of the object are not loaded from the data source. This is the default behavior for queries or when calling the Load method on EntityCollection <(Of <(TEntity>)>).

This will mean that if I have this in context, there should not be any hit in the database. However, this does not seem to be the case. It would be wise if OverwriteChanges or PreserveChanges were the default values, but that is not the case. It seems contradictory to what is going to happen. The only thing I can think of is that โ€œdownloadedโ€ simply means that there is no rewriting. However, this does not mean that there are no queries in the database.

+4
source share
4 answers

context.SiteUser is a property of type ObjectQuery. When you execute ObjectQuery, it always ends up in storage. This is what they do. If you do not want to perform a database query, do not use ObjectQuery.

It looks like what you really want is a function that says: "If the entity is already implemented in context, then just return it. If it is not, then remove it from the database." As it happens, an ObjectContext includes a function called GetObjectByKey

GetObjectByKey is trying to get an object that has the specified EntityKey from the ObjectStateManager. If the object is not currently loaded in the context of the object, the request is executed in an attempt to return the object from the data source.

+8
source

IMO, the reason EF accesses the database for the second time is to make sure that there are no extra rows in db that satisfy the query. It is possible that additional matching rows were inserted into the table after the first query was issued, and EF sees if any of them exist.

+2
source

If I understand your question, this should answer it:

Actually, the way to create an entity by default requires notification of changes to the objects of the object in a framework class called the state manager, which then keeps track of which properties have been changed. The original values โ€‹โ€‹are copied only on demand. When updates occur, these initial values โ€‹โ€‹are used in a conversation with the server only if the changed properties are marked as "concurrency tokens." That is, for any columns of the concurrency token, when the structure creates an update, the expression will include checking to make sure that the row in the database still has the original value, and if this does not result in an exception from notify the program that someone else has changed the row in the database. It is also true that the framework object does not absolutely require Notifications from the setter object, you can also determine what is changed in the application code and call an explicit method to determine the properties (but then the frame will only have a record that the property has been changed, it will not have an original meaning).

What comes from here . You can read more about this here and here .

Edited to add:

It appears that with EF there is an ObjectStateManager that tracks changes that never allow data to be disabled. To disable data, you need to call the ObjectContext.Detach method to disable your object. More can be found here and here .

+1
source

What if you avoid .ToList () and use .FirstOrDefault ()?

+1
source

All Articles