Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Save Temporary Data with Entity Framework Self-Tracking Entity

0.00/5 (No votes)
28 Dec 2011 1  
In this article, I will show you how to use Entity Framework Self-Tracking Entity to save temporary data in Enterprise Applications.

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:

  1. Temporary data is completely separated from finalized data.
  2. Easy to implement. Only one table needs to be added and no change is required to any of the current business tables or code.
  3. 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.

ER Diagram

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.

Entity Data Model

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.

Add Code Generation Item

Give the name STE.tt.

Self-Tracking Entity Generator

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.

Update Input File

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.

STEContext.tt

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.

Employee Class

Also, the class has a DataContract attribute on the class and a DataMember attribute on properties to support serialization and deserialization.

Employee Class Code

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.

  1. Create an Employee object with user entered data.
  2. Serialize the new Employee object to an XML string.
  3. Clear context.
  4. 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.

  5. Create a TempData object and store the XML of the new Employee object into the Data property.
  6. Call the Create method of the repository to attach the new TempData object with the context.
  7. 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)
    {
        // New an Employee oject with user entered data
        Employee employee = new Employee();
        employee.Name = employeeVM.Name;
        employee.Title = employeeVM.Title;
        employee.Address = employeeVM.Address;

        // Serialize the new Employee object to XML string
        string xml = DataHelper.SerializeEntity<Employee>(employee);

        // Clear context
        context.Clear();

        // New a TempData object and store XML of the new Employee object into Data property
        TempData tempData = new TempData();
        tempData.Data = xml;
        tempData.LastUpdated = DateTime.Now;

        // Call Create method of repository to attach the new TempData object with context
        tempDataRepository.Create(tempData);

        // Commit the new created TempData object into database
        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.

  1. Find the TempData object from the database.
  2. Deserialize the value of the Data property back to the Employee object.
  3. Attach the Employee object into the context.
  4. Update the Employee object with the latest data entered by the user.
  5. Delete the TempData object from the context.
  6. 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)
    {
        // Find the TempData object from database
        TempData tempData = 
          tempDataRepository.Find(td => td.TempDataID == employeeVM.LinkTempDataID);

        // Deserialize the value of data property back into Employee object
        Employee employee = DataHelper.DeserializeEntity<Employee>(tempData.Data);
        
        // Attach the Employee object into context
        employeeRepository.Attach(employee);
        
        // Update the Employee object with the latest data entered by user
        employee.Name = employeeVM.Name;
        employee.Title = employeeVM.Title;
        employee.Address = employeeVM.Address;

        // Delete TempData object from context
        tempDataRepository.Delete(tempData);

        // Commit the Employee object into database
        // and delete TempData object from database
        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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here