Introduction
In enterprise applications, one situation faced frequently by developers is how to design a temporary data saving feature. The temporary data saving feature will reduce
the chance of data loss caused by session timeouts, computer down, or network issues. Also, a temporary data saving feature allows certain business processes to last for several
business days without locking the existing data or slowing down other business processes. There are many ways to design temporary data saving, such as saving the data into
a business table with a status flag or saving the data into a mirror table of your business table. In this article, I will show you how to use
the Entity Framework Self-Tracking Entity (acronym STE) to handle this situation that will not just keep the cleanness of your data model (database tables) but also give enough extensibility
for handling more temporary data saving requests.
Temporary Data Saving
In a normal enterprise application, when there is a lot of data that needs to be entered into a system at a time or the entered data must be verified
and approved before seen by other users, the user could face a data loss problem or draft data issue. Thinking about this, if the user typed data for several
hours in a web based application, then hit Submit button to try to save the data, they will sometimes get a “Session Timeout” instead of a “Save Success”.
How frustrated will the user be? Also, thinking about this, when new customer data is entered into a system by a business analyst, before the customer data
gets verified and approved by the business department manager, other department users can already see and use this customer to do business. How serious
consequences could be caused and how big a mess needs to be cleaned up later on? To resolve the above mentioned issues, we can provide the user a way to save data temporarily.
The temporary saved data is only seen by the data input person or certain people, this gives the data input person the ability to periodically save the work without
waiting till the end. This also prevents other department users from seeing and using the draft data.
Entity Framework Self-Tracking Entity
Before applying domain driven design, all the business data is processed with a RecordSet in the enterprise application, therefore the most common solution
for temporary data saving is either having a status flag in the business table to indicate the business data is not finalized or having a mirror table of
the business table to store temporary data. The drawbacks of the solutions are they are very cumbersome and requires a lot of changes to
the existing application. Also, those solutions will result in a big amount of additional data or tables that mess up the entire application. After applying domain driven design, we design
the application to have a centralized domain layer that uses the domain entity to store business data. We can serialize the domain entity and save it into a temporary location.
How do we support domain entity serialization and deserialization?
We can design the domain entity as an Entity Framework Self-Tracking Entity. Entity Framework
is an ORM framework from Microsoft. It is used to handle paradigm mismatch between domain model and data model. By using it, we will have an elegant domain model without
worrying about how to persist it into the database. There are many ways to design a domain model. Such as POCO style domain model. The POCO style domain model is a simple
.NET class that can be directly handled by the ORM framework without any ORM required properties or methods. The cleanness and simplicity attracts many enterprise
application developers to adopt it in their applications. However, it needs more work on supporting change tracking and serialization, especially XML serialization.
To have a class support change tracking and serialization, Entity Framework has come up with the Self-Tracking Entity idea. Strictly speaking, the Self-Tracking Entity
is not a pure POCO style domain entity because there are many properties and methods added in the domain entity to support self-tracking and serialization.
But, because all the self-tracking related properties and methods and serialization/deserialization support are added at the source code level, it is not tied
with the Entity Framework, so you should still be able to reuse the domain model with other ORM frameworks such as NHibernate. The design purpose of the Self-Tracking Entity
is to allow the domain entity to be serialized into XML in a service application, and send it over a web service, and deserialize it back in a web service client application.
In here, I am not really using it this way. I will use the serializing ability of the Self-Tracking Entity to help with temporary data saving.
The Idea
The idea is to use the Self-Tracking Entity as a domain entity and have a table store the serialized XML of the domain entity object. By doing this, we have the following benefits:
- Temporary data is completely separated from finalized data.
- Easy to implement. Only one table needs to be added and no change is required to any of the current business tables or code.
- Extensible. Adding temporary data saving support for additional domain entities will not cause changes to any existing tables or code.
The Implementation
A simple ASP.NET MVC application is used here to demonstrate how to implement this idea. The demo application is designed as an employee information management application.
You can add, update, or delete employees.
Data Model
There are only two tables in the demo application, a business table Employee and a temporary data storage table TempData.
The Data field in the TempData table is used to store the serialized business object XML. Obviously, the DataType is XML
.
Visual Studio Solution Structure
There are four projects in the demo application: Demo.Base, Demo.Data, Demo.Models, and Demo.Web.
- Demo.Base: The common library of the demo application. It contains a DataHelper to handle serialization and deserialization, and an interface for Dependency Injection.
- Demo.Data: The data access layer. It implements the Repository pattern to wrap up Entity Framework.
- Demo.Models: The domain layer. It contains self-tracking entity style domain entities and data access interfaces.
- Demo.Web: The web user interface layer. It’s built with ASP.NET MVC that has good separation of concerns and a test friendly structure.
Entity Class and Context Class
The most important classes supporting temporary data saving reside in the Demo.Data project and the Demo.Models project. Let’s take a look at how to generate them.
First, we need to add EntityDataModel.edmx from the data model (database) with the Entity Framework Entity Data Model wizard.
By selecting the “Add Code Generation Item…” in the Entity Data Model context menu, you can add an ADO.NET Self-Tracking Entity Generator template into the project.
Give the name STE.tt.
After clicking the Add button, you will see there are two files added in the project, STE.tt and STE.Context.tt. STE.tt is the T4 template file
of your domain entity classes and STE.Context.tt is the T4 template file of the Entity Framework context classes. Because the Demo.Data project is the data access layer,
we need to move the STE.tt file into the Demo.Models project. After moving it, a small change is needed in the STE.tt file to point the input file,
EntityDataModel.edmx, to the original place.
At the same time, STE.Context.tt needs to be updated to have the right Custom Tool Namespace, so the context classes can still reference the domain entity classes.
If you look at the generated Employee
class, you can see there are some extra properties and methods in addition to the properties from the Employee table.
The extra properties and methods added by the Self-Tracking Entity template are for tracking changes and for serialization/deserialization purposes.
Also, the class has a DataContract
attribute on the class and a DataMember
attribute on properties to support serialization and deserialization.
Saving Temporary Data
The Demo.Web project uses Self-Tracking Entity classes and Context classes together to implement temporary data saving.
From the CreatTempData
action method in the Employee
controller, we can see how we save the newly created Employee
object as temporary data.
- Create an
Employee
object with user entered data.
- Serialize the new
Employee
object to an XML string.
- Clear context.
Note: because we are using the Unit-of-Work pattern in here to handle database operations, we need to call Context.Clear()
to close the current Unit-of-Work,
so the following Context.Commit
will be in a new Unit-of-Work. This will prevent objects other than TempData
from being committed into the database.
- Create a
TempData
object and store the XML of the new Employee
object into the Data
property.
- Call the
Create
method of the repository to attach the new TempData
object with the context.
- Commit the newly created
TempData
object into the database.
The CreateTempData
action method code snippet is shown here:
[HttpPost]
[ActionName("Create")]
[AcceptVerbs(HttpVerbs.Post)]
[AcceptParameter(Name="button", Value="Create Temp Data")]
public ActionResult CreateTempData(EmployeeVM employeeVM)
{
IContext context = Demo.Models.Registry.Context;
ITempDataRepository tempDataRepository =
Demo.Models.Registry.RepositoryFactory.GetTempDataRepository();
if (ModelState.IsValid)
{
Employee employee = new Employee();
employee.Name = employeeVM.Name;
employee.Title = employeeVM.Title;
employee.Address = employeeVM.Address;
string xml = DataHelper.SerializeEntity<Employee>(employee);
context.Clear();
TempData tempData = new TempData();
tempData.Data = xml;
tempData.LastUpdated = DateTime.Now;
tempDataRepository.Create(tempData);
context.Commit();
return RedirectToAction("Index", "Home");
}
From the SaveEmployee
action method of the Employee
controller, we can see how to save temporary data into a real business table.
- Find the
TempData
object from the database.
- Deserialize the value of the
Data
property back to the Employee
object.
- Attach the
Employee
object into the context.
- Update the
Employee
object with the latest data entered by the user.
- Delete the
TempData
object from the context.
- Commit the
Employee
object into the database and delete the TempData
object from the database.
The SaveEmployee
action method code snippet is listed here:
[HttpPost]
[ActionName("UpdateTempData")]
[AcceptVerbs(HttpVerbs.Post)]
[AcceptParameter(Name = "button", Value = "Save Employee")]
public ActionResult SaveEmployee(EmployeeVM employeeVM)
{
IContext context = Demo.Models.Registry.Context;
ITempDataRepository tempDataRepository =
Demo.Models.Registry.RepositoryFactory.GetTempDataRepository();
IEmployeeRepository employeeRepository =
Demo.Models.Registry.RepositoryFactory.GetEmployeeRepository();
if (ModelState.IsValid)
{
TempData tempData =
tempDataRepository.Find(td => td.TempDataID == employeeVM.LinkTempDataID);
Employee employee = DataHelper.DeserializeEntity<Employee>(tempData.Data);
employeeRepository.Attach(employee);
employee.Name = employeeVM.Name;
employee.Title = employeeVM.Title;
employee.Address = employeeVM.Address;
tempDataRepository.Delete(tempData);
context.Commit();
return RedirectToAction("Index", "Home");
}
return View(employeeVM);
}
Following is the sample Employee
object XML stored in the TempData table:
<Employee xmlns="http://schemas.datacontract.org/2004/07/Demo.Models.Domain"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" z:Id="i1">
<Address>Main Street, Edison, NJ</Address>
<ChangeTracker z:Id="i2">
<ExtendedProperties />
<ObjectsAddedToCollectionProperties />
<ObjectsRemovedFromCollectionProperties />
<OriginalValues />
<State>Added</State>
</ChangeTracker>
<EmployeeID>0</EmployeeID>
<Name>Tom</Name>
<Title>Manager</Title>
</Employee>
Summary
With Entity Framework Self-Tracking Entity, we can easily implement a temporary data saving feature. This solution is simple, elegant, and extensible.
Also, it has a very small impact on the existing application. If you really don’t want to use the Entity Framework Self-Tracking Entity, you can have a POCO style domain
entity and use DTO as the media to serialize and deserialize domain entity objects to and from XML.
Using the Code
The code is developed in Visual Studio 2010. You need to first create a database, SaveTempDataWithSTE, in SQL Server and then run the attached
SaveTempDataWithSTE.sql script to create the Employee table and TempData in it. Before running the code, remember to update
the connection string in the configuration file to your local connection string.