Posts
208
Comments
1144
Trackbacks
51
Testing EF4 Code First Optimistic Concurrency with Fluent Assertions

Entity Framework 4 Code First CTP4 was released in July and the API for developing without any edmx continues to mature and get better. Code First allows you to define your entity framework configuration with a Fluent API similar to Fluent NHibernate. Although the API for EF Code First is very user friendly, you should still have integration tests to verify that you’ve configured your entities correctly. This post will walk through configuring EF4 Code First optimistic concurrency.

Suppose I have a POCO Task class that looks like this:

   1:  public class Task
   2:  {
   3:      public int Id { get; set; }
   4:      public string Name { get; set; }
   5:      public DateTime? DueDate { get; set; }
   6:      public int Status { get; set; }
   7:      public string Notes { get; set; }
   8:      public byte[] Timestamp { get; set; }
   9:  }

Notice on line #8 I have a byte array that I’ve named Timestamp. I’m going to map this to a SQL Server timestamp column. First I’ll create my DbContext. DbContext is a new type in EF Code Only that simplifies ObjectContext to streamline on the most frequently used methods.

   1:  class TasksDbContext : DbContext
   2:  {
   3:      public TasksDbContext(DbModel dbModel) : base(dbModel)
   4:      {
   5:      }
   6:   
   7:      public DbSet<Task> Tasks { get; set; }
   8:  }

Notice the constructor of my database context takes a DbModel. There are a couple of different ways I can create this. The first is to configure everything using the model builder like this:

   1:  var modelBuilder = new ModelBuilder();
   2:  modelBuilder.Entity<Task>().Property(m => m.Timestamp).IsConcurrencyToken().HasStoreType("timestamp").IsComputed();
   3:  var model = modelBuilder.CreateModel();
   4:  var context = new TasksDbContext(model);

The key is line #2 above where I fluently configure the timestamp property of my Tasks class to be 1) identified as a concurrency token, 2) specified as a SQL Server timestamp data type, and 3) a computed column so that EF knows that the user will not be inserting values into this field but rather, the value will be computed on the SQL Server itself. Although it’s syntactically convenient to be able to do all this inline with the ModelBuilder, as your apps get bigger, that can get ugly. In this case, it’s better to separate your configuration for each entity in EntityConfiguration classes like this:

   1:  class TaskConfiguration : EntityConfiguration<Task>
   2:  {
   3:      public TaskConfiguration()
   4:      {
   5:          this.Property(m => m.Timestamp).IsConcurrencyToken().HasStoreType("timestamp").IsComputed();
   6:      }
   7:  }

I’ve only added one line of configuration code here but, when I need to specify additional mapping/configuration details about the Task entity, I’ll do it here.  Now I can simply reference that entity configuration with the model builder. Hence, I’ll create a simple context factory class:

   1:  class ContextFactory
   2:  {
   3:      private static DbModel dbModel = InitializeModel();
   4:   
   5:      private static DbModel InitializeModel()
   6:      {
   7:          var modelBuilder = new ModelBuilder();
   8:          modelBuilder.Configurations.Add(new TaskConfiguration());
   9:          return modelBuilder.CreateModel();
  10:      }
  11:   
  12:      public static TasksDbContext CreateContext()
  13:      {
  14:          return new TasksDbContext(dbModel);
  15:      }
  16:  }

Now anytime I need a database context, I can just call my factory method. Before we get to integration testing our data access code, let’s wrap up our EF code in a simple repository so our consumers don’t need any knowledge of EF.

   1:  public class TaskRepository : IRepository<Task>
   2:  {
   3:      public void Add(Task item)
   4:      {
   5:          using (var context = ContextFactory.CreateContext())
   6:          {
   7:              context.Tasks.Add(item);
   8:              context.SaveChanges();
   9:          }
  10:      }
  11:   
  12:      public void Update(Task item)
  13:      {
  14:          using (var context = ContextFactory.CreateContext())
  15:          {
  16:              context.Tasks.Attach(item);
  17:              context.ChangeObjectState(item, System.Data.EntityState.Modified);
  18:              context.SaveChanges();
  19:          }
  20:      }
  21:   
  22:      public Task FindById(int id)
  23:      {
  24:          using (var context = ContextFactory.CreateContext())
  25:          {
  26:              return context.Tasks.FirstOrDefault(x => x.Id == id);
  27:          }
  28:      }
  29:      // Remove() and FindAll() methods omitted for brevity
  30:  }

A couple things of note: first, I’m simply using my ContextFactory to create the context anytime I need it. Second, notice line #17 above calls a method on the database context called ChangeObjectState(). I added this method to TasksDBContext in order to get at the ObjectStateManager in the ObjectContext. This allows me to mark an entity as modified (i.e., a UPDATE should occur) that came in from another tier and was created in another context somewhere. The entire method looks like this:

   1:  public void ChangeObjectState<T>(T entity, EntityState entityState)
   2:  {
   3:      this.ObjectContext.ObjectStateManager.ChangeObjectState(entity, entityState);
   4:  }

In EF4 DbContext, the ObjectContext is a protected property. Hence, if you want to get at it, you have to do so in the context itself (i.e., I could not have referred to it from my repository directly).

At this point, we’re ready to execute our integration tests. To do this, I’m going to use MSTest with Fluent Assertions. Fluent Assertions is a CodePlex project that enables you to use a fluent-style API to more natural specify your expected outcome. The API for MSTest has been basically stagnant for the last several years and this adds functionality onto it so you can use MSTest and still get the benefit of fluent assertions. Additionally, Fluent Assertions enables us to get away from the clumsy [ExpectedException] attribute to a more deterministic xUnit-style ShouldThrow() API.

Our unit test will have two different repositories (and therefore 2 different contexts) get the same task. Then the first repository will save. When the second repository saves, we’ll expect an OptimisticConcurrencyException:

   1:  [TestMethod]
   2:  public void Save_with_wrong_timestamp_should_fail()
   3:  {
   4:      // arrange
   5:      var taskId = 1;
   6:   
   7:      var repository1 = new TaskRepository();
   8:      var task1 = repository1.FindById(taskId);
   9:   
  10:      var repository2 = new TaskRepository();
  11:      var task2 = repository2.FindById(taskId);
  12:   
  13:      task1.Notes = "modified note";
  14:      repository1.Update(task1);
  15:      task1.Notes.Should().Be("modified note");
  16:   
  17:      task2.Notes = "modified note 2";
  18:   
  19:      // act
  20:      Action action = () => repository2.Update(task2);
  21:   
  22:      // assert
  23:      action.ShouldThrow<OptimisticConcurrencyException>();
  24:  }

Notice line #15 above – it shows a simple example of the Should().Be() syntax that Fluent Assertions gives you for MSTest. Next look at line #20 and #23. Here is see we are deterministically asserting that that specific line of code *will* throw the expected exception. This is much better than the [ExpectedException] attribute as that could give false positives given that *any* line of code in your unit test could potentially cause an exception.

As a final step, let’s check out what happens behind the scenes when this test runs by using the Entity Framework Profiler. This tool is not free but it’s simply the best tool I’ve seen for profiling EF where people typically resort to using the SQL Profiler. If you’re doing any serious EF work, it’s *definitely* worth the money. To use the EF Prof, just add the required assembly per the documentation (HibernatingRhinos.Profiler.Appender.v4.0.dll) and then add the 1-line code to the beginning of your method: EntityFrameworkProfiler.Initialize();

 

efprofoptcon

 

There are a couple of interesting things to notice in our EF Prof output. First, the WHERE clause on line #6-7 above shows what we’d expect with Optimistic Concurrency – that is, it looks at both the Id *and* the concurrency token column.  The second interesting thing is that EF prof is showing a red alert bubble. When we click on this we see, “Transaction disposed without explicit rollback/commit”. So EF Prof not only shows you the complete profiling results but also suggests ways to improve your code.

Certainly data access code is nothing new. However, with EF4 Code First, integration testing with Fluent Assertions, and debugging with EF Prof, I must say, it’s more fun than it’s been in a while. I highly recommend checking all these out.

posted on Monday, October 11, 2010 10:01 PM Print
Comments
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Ali
10/15/2010 2:03 PM
What is ModelBuilder. Is this included in EF code first CTP release or you code it yourself. if so Could you share it with us
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Ali
10/15/2010 2:23 PM
Got it. I was using CTP3 instead of CTP4

thanks for the post
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Ali
10/15/2010 2:31 PM
Another question came up. You are testing in a non stateless environment. How would you do that say in an MVC application where you retrieve an entity and disconnect. Modify it and then update. Not sure how you would achieve that and get OptimisticConcurrencyException.
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Steve
10/16/2010 9:50 AM
@Ali - When you say "you are testing in a non stateless environment" - I don't understand what you mean by that. The above example with the Repository is specifically *intended* to be used in a stateless application such as an MVC app.

Action #1: User1 asks for Contact 218 and it's displayed on their screen. It's an MVC stateless app. The repository's FindById() method as shown above is invoked to get it to display on the screen. Contact 218 has a timestamp of 1234 stored in the database. This timestamp is stored as part of the view model.

Action #2: User2 also asks for Contact 218 and it's displayed on their screen. Timestamp is also 1234 because User1 has not saved yet.

Action #3: User1 saves Contact 218 to database. This is a stateless MVC app so the repository must use EF Attach() method. When this save happens, DB assigns new timestamp (call it 5678).

Action #4: User2 saves Contact 218. The timestamp on this entity is 1234 but the timestamp now currently in the database is 5678. At this point, EF will (correctly) throw an OptimisticConcurrencyException.
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Sam
3/17/2011 6:46 AM
What's the equivalent code for RC1?
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Steve
3/17/2011 6:49 AM
@Sam - Looks like for RC1, instead of:
.Property(m => m.Timestamp).IsConcurrencyToken().HasStoreType("timestamp").IsComputed();

you can get the same results with this:
.Property(m => m.Timestamp).IsRowVersion();
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Sam
3/17/2011 7:39 AM
Hi Steve,
I'm working on MVC 3 VideoStore tutorial on the asp.net website http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part1-cs. I guess the code you suggested is equivalent to

[Timestamp]
[ConcurrencyCheck] [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] RowVersion { get; set; }

But I cant get the controller to do the concurrency check.

var movie = db.Movies.Find(model.ID);
db.Movies.Attach(movie);
db.Entry(movie).State = EntityState.Modified;
TryUpdateModel(movie);
db.SaveChanges();

got any idea why? thanks
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Steve
3/17/2011 7:44 AM
@Sam - Hmm, hard to tell with the limited snippet of code. Plus, I'm not sure what you mean when you say it won't do concurrency check - do you mean it throws exception or there is no exception but no concurrency check being done? The one thing that stands out from glancing at the code is: why are you calling TryUpdateModel *after* attaching rather than before? Plus, is there really a need to retrieve it from the database first before saving - I'd just attach the entity that was posted.
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Sam
3/17/2011 8:01 AM
Steven,
I cant thank you enough for pointing out a stupid mistake I made, it works now, here is a method

[HttpPost]
public ActionResult Edit(Movie model)
{
try
{
var movie = db.Movies.Find(model.ID);
TryUpdateModel(movie);
db.Movies.Attach(movie);
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException e)
{
ModelState.AddModelError("", "Optimistic Concurrency Exception");
}
catch (Exception e)
{
ModelState.AddModelError("", "Unexpexted Error " + e.ToString());
}

return View(model);
}
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Brad
8/29/2011 11:57 AM
Appreciate any suggestions. I receive the following error when attempting to edit a code first generated db - "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."

My class is simple.

public class City
{
[Required]
public virtual bool Active { get; set; }

[DisplayName("ID")]
public virtual int Id { get; set; }

[Required]
[StringLength(256)]
public virtual string Name { get; set; }

[Timestamp]
[ConcurrencyCheck]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] RowVersion { get; set; }
}

and my MVC controller has the following code

[HttpPost]
public ActionResult Edit(City city)
{
if (ModelState.IsValid)
{
try
{
db.Cities.Attach(city);
db.Entry(city).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch(DBConcurrencyException e)
{
ModelState.AddModelError("", "Optimistic concurrency exception.");
}
catch(Exception e)
{
ModelState.AddModelError("", e.Message);
}
}
return View(city);
}
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Brad
8/29/2011 12:42 PM
Found solution at: http://thedatafarm.com/blog/data-access/round-tripping-a-timestamp-field-with-ef4-1-code-first-and-mvc-3/

Added a hidden form field for the timestamp and it works great.
Gravatar
# re: Testing EF4 Code First Optimistic Concurrency with Fluent Assertions
Ab
8/1/2012 11:58 PM
Nicely explained. Thanks you very much. In EF4.3 the following configuration is not supported:
this.Property(m => m.Timestamp).IsConcurrencyToken().HasStoreType("timestamp").IsComputed();

Will any one of the following two options work:
1) this.Property(t => t.TimeStamp).IsConcurrencyToken().HasColumnName("timestamp").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
2) this.Property(t => t.TimeStamp).IsConcurrencyToken().HasColumnName("timestamp").IsRowVersion()

Thanks.
--A

Post Comment

Title *
Name *
Email
Comment *  
Verification

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud