Persistent States and Transitions
This article will present to you the states and the transitions an object existing in the database could go through. It describes the main processes that the object can participate in:
Additionally, a special attention is dedicated to the NotLoaded state.
The code snippets in this article are based on the Northwind sample database.
Update A Record in The Database
Let's consider the scenario where a given record in the database needs to be updated. The states and the transitions a corresponding in-memory object has to go through are demonstrated in the following code snippet:
using (FluentModel dbContext = new FluentModel())
{
//Query for an object
Category someCategory = dbContext.Categories.First();
//The initial state is Clean.
//The returned value is MaskLoaded|MaskManaged|MaskNoMask
ObjectState state = dbContext.GetState(someCategory);
//Edit a property
someCategory.CategoryName = "New Name";
//The state changes to Dirty.
//The returned value is MaskDirty|MaskLoaded|MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory);
//Persist the changes
dbContext.SaveChanges();
//The state changes to NotLoaded.
//The returned value is MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory);
}
Using dbContext As New FluentModel()
'Query for an object
Dim someCategory As Category = dbContext.Categories.First()
'The initial state is Clean.
'The returned value is MaskLoaded|MaskManaged|MaskNoMask
Dim state As ObjectState = dbContext.GetState(someCategory)
'Edit a property
someCategory.CategoryName = "New Name"
'The state changes to Dirty.
'The returned value is MaskDirty|MaskLoaded|MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory)
'Persist the changes
dbContext.SaveChanges()
'The state changes to NotLoaded.
'The returned value is MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory)
End Using
A detailed description of the object states is available in the Object Life-cycle Overview article.
When a SELECT statement returns an object that exists in the database, its initial state is Clean (the context is tracking the changes about that object and the relevant data are loaded in-memory). Here, if the transaction state changes or the execution of the process is terminated, the object's data will be unloaded from the memory and someCategory will be transferred to NotLoaded state.
When a property of someCategory changes its value in-memory, its state will become Dirty (on SaveChanges() an UPDATE statement will be generated about it). At that point, if the transaction is rolled back or the execution of the process is terminated, no new data will be persisted to the database and the object state will change to NotLoaded.
If the transaction is committed, however, the new values will be persisted to the database and the object state will become NotLoaded.
Delete A Record from The Database
Let's take a look at the scenario where a given record in the database needs to be deleted. The states and the transitions a corresponding in-memory object has to go through are demonstrated in the following code snippet:
using (FluentModel dbContext = new FluentModel())
{
//Query for an object
Category someCategory = dbContext.Categories.First();
//The initial state is Clean.
//The returned value is MaskLoaded|MaskManaged|MaskNoMask
ObjectState state = dbContext.GetState(someCategory);
//Remove the object from the context
dbContext.Delete(someCategory);
//The state changes to Deleted.
//The returned value is MaskDeleted|MaskDirty|MaskLoaded|MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory);
//Presist the changes
dbContext.SaveChanges();
//The state changes to NotLoaded.
//The returned value is MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory);
}
Using dbContext As New FluentModel()
'Query for an object
Dim someCategory As Category = dbContext.Categories.First()
'The initial state is Clean.
'The returned value is MaskLoaded|MaskManaged|MaskNoMask
Dim state As ObjectState = dbContext.GetState(someCategory)
'Remove the object from the context
dbContext.Delete(someCategory)
'The state changes to Deleted.
'The returned value is MaskDeleted|MaskDirty|MaskLoaded|MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory)
'Presist the changes
dbContext.SaveChanges()
'The state changes to NotLoaded.
'The returned value is MaskManaged|MaskNoMask
state = dbContext.GetState(someCategory)
End Using
A detailed description of the object states is available in the Object Life-cycle Overview article.
When a SELECT statement returns an object that exists in the database, its initial state is Clean (the context is tracking the changes about that object and the relevant data are loaded in-memory). Here, if the transaction state changes or the execution of the process is terminated, the object's data will be unloaded from the memory and someCategory will be transferred to NotLoaded state.
When someCategory is passed to the Delete() method of the context, its state will become Deleted (on SaveChanges() a DELETE statement will be generated about it). At that point, if the transaction is rolled back or the execution of the process is terminated, no statements will be executed against the database and the object's state will change to NotLoaded.
If the transaction is committed, however, the corresponding record in the database will be deleted, the context will not track any changes about that the object and its state will become NotManaged.
The NotLoaded State
If you take a look at the Persistent States and Transitions diagram in this article and the Persistent New States and Transitions diagram in the previous one, you will notice that after INSERT and UPDATE statements, the corresponding objects end up in the NotLoaded state.
Generally, this state means that the object is managed by the context, exists in memory but the data from its properties are unloaded from the memory. In real-life scenarios when the context is not disposed immediately after the transaction is committed\rolled back, the user through the UI may need to perform additional actions.