Implementing refreshing data on save

In this recipe, we are going to look at how to get your application on a change to get the most current data it can, so that when you are modifying objects, you get the most current one back.

Getting ready

We will be using the Nuget Package Manager to install the Entity Framework 4.1 assemblies.

The package installer can be found at http://nuget.codeplex.com/.

We will also be using a database for connecting to the data and updating it.

Open the Improving Refreshing Data on Save solution in the included source code examples.

How to do it...

  1. We start be writing out a test that will allow us to define the scope of our problem. In this case, we want our context to refresh any object that it commits after the commit is final. We define it with the following code:
    using System;
    using System.Linq;
    using BusinessLogic;
    using DataAccess;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace Test
    {
      [TestClass]
      public class QueryTests
      {
        [TestMethod]
        public void ShouldRefreshDataOnSave()
        {
          var blogContext = new BlogContext();
          Database.SetInitializer(new Initializer());
          blogContext.Database.Initialize(true);
    
          var repo = new BlogRepository(new BlogContext());
          var repo2 = new BlogRepository(new BlogContext());
    
          var blog = repo.Set<Blog>().FirstOrDefault();
    
          var blog2 = repo2.Set<Blog>().FirstOrDefault();
    
          blog.Title = "Something new";
          blog2.Rating = 3;
    
          repo2.SaveChanges();
          repo.SaveChanges();
    
          Assert.AreEqual(blog.Rating, blog2.Rating);
    
        }
    
      }
    
    }
  2. We then add our Blog object as a new C# file named Blog.cs to our BusinessLogic project, so that we have an example object to connect to, with the following code:
    using System;
    
    namespace BusinessLogic
    {
      public class Blog
      {
        public int Id { get; set; }
        public DateTime Creationdate { get; set; }
        public string ShortDescription { get; set; }
        public string Title { get; set; }
        public double Rating { get; set; }
        public bool IsDeleted { get; set; }
      }
    }
  3. We then add our mapping as a new C# file named BlogMapping to the DataAccess project with the following code:
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.ModelConfiguration;
    using BusinessLogic;
    
    namespace DataAccess.Mappings
    {
      public class BlogMapping : EntityTypeConfiguration<Blog>
      {
        public BlogMapping()
        {
          this.ToTable("Blogs");
          this.HasKey(x => x.Id);
          this.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
    
          this.Property(x => x.Title).IsRequired().HasMaxLength(250);
    
          this.Property(x => x.Creationdate).HasColumnName("CreationDate").IsRequired();
    
          this.Property(x => x.ShortDescription).HasColumnType("Text").IsMaxLength().IsOptional().HasColumnName("Description");
          this.Property(x => x.IsDeleted).HasColumnName("DeletedFlag");
        }
    
      }
    }
  4. The next step is for us to add the mapping to the BlogContext and make the context change the commit behavior with the following code:
    using System.Data;
    using System.Data.Entity;
    using System.Linq;
    using DataAccess.Mappings;
    
    namespace DataAccess
    {
      public class BlogContext : DbContext, IUnitOfWork
      {
        public void Add<T>(T entity) where T : class
        {
          this.Set<T>().Add(entity);
        }
    
        public void Commit()
        {
          var itemsToBeSaved = this.ChangeTracker.Entries().Where(x => x.State == EntityState.Modified).ToList();
    
          SaveChanges();
          foreach (var dbEntityEntry in itemsToBeSaved)
          {
            dbEntityEntry.Reload();
          }
        }
    
        public IQueryable<T> Find<T>() where T : class
        {
          return this.Set<T>();
        }
    
        public void Refresh()
        {
          this.ChangeTracker.Entries().ToList().ForEach(x => x.Rload());
        }
        public void Remove<T>(T entity) where T : class
        {
          this.Set<T>().Remove(entity);
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
          modelBuilder.Configurations.Add(new BlogMapping());
          base.OnModelCreating(modelBuilder);
        }
      }
    }
  5. All of our tests pass.

How it works...

We start with a test that explicitly defines our goals. In this case, we want the data on a save operation to be the most current version possible. We define a test where we select an object back from two contexts. We update the objects differently, and call a save operation on both. The second object should get saved, and then updated.

We then create our Blog object that we want to update, and the mapping that will tie it into the database context. This will give us the ability to interact with the database, and make sure that the object is truly refreshed.

We then get to the SaveChange method in the context that will allow us to save the objects as we would normally do, but we also want to store the change tracking entries from before we save, so that we know which objects to refresh. This allows us to leverage the change tracker to reload the object from the database.

There's more...

The change tracker in Entity Framework code first is complex and worth understanding a bit more.

EntityState

The EntityState is what the change tracker uses to know if it cares about the object and how it has been modified. When we add, delete, or modify a tracked object, the state is changed to reflect this operation. We are given access to this by passing in the EntityKey, or the entity itself to the Entry method, and getting back the object that represents the entity to the change tracker.

There is a secondary purpose to the change tracker, and that is to know which objects to ignore. When we need the change tracker to ignore an object, we have the ability to detach it, and then, at a later time, reattach it.

EntityKey

EntityKey is an immutable object that uniquely identifies the entity in the context. This is used for updates, edits, change tracking, and constraint checking. This is the main identifier for the object. It is derived from how the object is mapped to a database table.

DbEntityEntry

This is the object which will hold the original values from the selection, the current values, and the state information that the Entity Framework needs to operate on. This gives a powerful API for managing the state of the object in reference to how the framework sees it.

See also

Chapter 7, Using Concurrent and Parallel Processing

  • Handling data retrieval in highly-threaded environments
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset