I am confused how DbSet is set the value in Entity Framework.
Let's say I have a code snippet as following in DataContext class (our own class inherited from DbContext class)?
public DbSet<Values> Values {get; set;}
Then I can actually retrieve values from the table (Values table) via the controller with dependency injection as following. Now my question is how DbSet is set values? I don't see how values property is set before retrieving the values from it.
this.context.Values.FirstOrDefaultAsync(list => list.id == id);
There are two major issues when talking about how a DbSet works:
IQueryable<...>
IQueryable
An object of a class that implements
IQueryable<TSource>
does not represent a sequence of similar items, it represents the potential to get an enumerable sequence of similar items.To do this, an IQueryable has two properties: an
Expression
and aProvider
. The Expression holds in some generic format information about what data must be fetched, the Provider knows who must fetch the data (usually a Database Management Systems DBMS), and what language is used to communicate with the DBMS (usually SQL).At its lowest level, to get the enumerable sequence, you need to call
IQueryable.GetEnumerator()
. This will send the Expression to the Provider, who will translate the Expression into a format that the DBMS understands. The provider will execute the query and return the fetched data as anEnumerable<TResult>
.To access the fetched items, you repeatedly call
MoveNext()
, and as long as it returns true, you can use propertyCurrent
to get the fetched TResult.To access the database, the DbSet object knows which DbContext it belongs to. The DbContext has a property
Database
, which can execute the translated SQL statement.Most people will seldom use GetEnumerator / MoveNext / Current, they use
foreach
, which deep inside uses this method to enumerate the elements.If you look closely to LINQ you'll see that there are two groups of IQueryable methods. Those that return an
IQueryable<...>
and the others.The methods that return
IQueryable<...>
won't execute the query. These methods use delayed execution, or lazy execution: Only the expression is changed. Those methods are fast, they hardly cost any processing power.The other methods, like ToList() / ToDictionary() / FirstOrDefault() / Sum() / Any() will actually execute the query: the Expression is sent to the database and the fetched data is returned accordingly.
ToList will be something like this:
Any
You should be able to understand by now, why your code doesn't work if you still have an
IQueryable<...>
and dispose the context: the Expression has been made, but it hasn't been executed yet. After disposing the dbContext the connection to the database can't be opened again:You should also understand by now, that if you have a query which you want to enumerate several times, that it is wise to make it a List, otherwise the query will be executed twice
Change items that are in the database
Every DbSet knows in which DbContext it is. Every DbContext has a
ChangeTracker
, who keeps track of all fetched items and all changes made to them.If you use
Find
to find an item, or use a query to get complete items, the original values of these items are stored in the ChangeTracker.The changeTracker contains all three cUstomer and customer1. You can access them using the following code:
fetchedCustomers will contain a
DbEntityEntry
for every customer without orders and for customer.Every
DbEntityEntry
holds for every property the original value and the current value. If you ask the DbEntityEntry for its state, it will check the original and the current value to determine if it has been changed.Add / Remove items from the database
If you want to remove an item from the database, you'll first have to fetch it. This ensures that it is also in the DbChangeTracker.
This will set the State of the DbEntityEntry of the fetchedCustomer to
Deleted
.Added items are also in the DbChangeTracker. They have a state equal to
Added
DbContext.SaveChanges
SaveChanges will fetch all DbEntityEntries to see which items are Added / Removed / Changed, and will execute the required SQL statements.
Because the DbEntityEntry knows the original value as well as the current value of each property, it knows which values to update.