Category Archives: Data
Using the EntityFramework SoftDelete Interceptor

In my previous post I introduced the Isg.EntityFramework.Interceptors I created. In this post, I will demonstrate the usage of another package that builds on the first: Isg.EntityFramework.Interceptors.SoftDelete

The entire SoftDelete package consists of two classes. The first is an interface:

   1:      public interface ISoftDelete
   2:      {
   3:          bool IsDeleted { get; set; }
   4:      }

The second is the SoftDeleteChangeInterceptor class.

If your domain class implements ISoftDelete, the SoftDeleteChangeInterceptor will catch the delete operation, set the IsDeleted field to true, and repurpose the operation to an Update.

Here is a unit test that demonstrates this behavior.

   1:          [Test]
   2:          public void Delete()
   3:          {
   4:              var name = Guid.NewGuid().ToString();
   5:   
   6:              using (var db = new CustomerDbContext())
   7:              {
   8:                  var customer = new Customer {IsDeleted = false, Name = name};
   9:                  db.Customers.Add(customer);
  10:                  db.SaveChanges();
  11:              }
  12:   
  13:              using (var db = new CustomerDbContext())
  14:              {
  15:                  var customer = db.Customers.SingleOrDefault(i => i.Name == name);
  16:                  db.Customers.Remove(customer);
  17:                  db.SaveChanges();
  18:              }
  19:   
  20:              using (var db = new CustomerDbContext())
  21:              {
  22:                  var customer = db.Customers.SingleOrDefault(i => i.Name == name);
  23:                  Assert.That(customer, Is.Not.Null);
  24:                  Assert.That(customer.IsDeleted, Is.True);
  25:              }
  26:          }

 

Happy Coding!

Entity Framework Interceptors

I’ve recently published two NuGet packages that may be of interest to Entity Framework Code First users. The first is Isg.EntityFramework.Interceptors which provides an interface for intercepting operations during a call to DbContext.SaveChanges() and taking some action before or after. The 2nd package is Isg.EntityFramework.Interceptors.SoftDelete which provides a concrete implementation of an interceptor.

Using the interceptors library involves three steps.

1) Implement an interceptor.

2) Register your interceptors.

3) Subclass InterceptorDbContext

Implementing an Interceptor

Implementing an interceptor is very easy.  The IInterceptor has two methods—Before and After—each of which refer to the SaveChanges() method of DbContext.

 1:     public interface IInterceptor
 2:     {
 3:         void Before(InterceptionContext context);
 4:         void After(InterceptionContext context);
 5:     }

 

The InterceptionContext consists of information your interceptor may need. You get the InterceptorDbContext that is in the process of being saved, the ObjectContext, ObjectStateManager, DbChangeTracker, and list of DbEntityEntry objects that are participating in the operation.

A TypeInterceptor implementation is provided that filters for entities of a certain type. A ChangeInterceptor implementation is provided that filters entities being Added, Modified, or Deleted. The ChangeInterceptor provides Before and After methods that can be overridden for each of those operations.

Register Your Interceptors

Interceptor registration is easy. In the case of the SoftDeleteInterceptor, registration looks like this:

 1: InterceptorProvider.SetInterceptorProvider(
 2:     new DefaultInterceptorProvider(
 3:         new SoftDeleteChangeInterceptor()));

 

InterceptorProvider is a global cache of Interceptor instances that relies on a provider to resolve the instances. In this case, I am manually providing the SoftDeleteInterceptor, but you can plug in a dependency injection framework or any other source of providers that you prefer. If you do this at application startup, any instance of InterceptorDbContext will automatically pull in the Interceptors and apply them during the SaveChanges operation.

Subclass InterceptorDbContext

This one really is as easy as it sounds. Instead of inheriting directly from DbContext, inherit from InterceptorDbContext. For example:

 1:     public class CustomerDbContext : InterceptorDbContext
 2:     {
 3:         public DbSet<Invoice> Invoices { get; set; }
 4:         public DbSet<Customer> Customers { get; set; }
 5: 
 6:         protected override void OnModelCreating(DbModelBuilder modelBuilder)
 7:         {
 8:             base.OnModelCreating(modelBuilder);
 9: 
 10:             modelBuilder.Entity<Invoice>()
 11:                .HasKey(i => i.InvoiceId)
 12:                ;
 13:             modelBuilder.Entity<Invoice>()
 14:                 .HasRequired(invoice => invoice.Customer)
 15:                 .WithMany(customer => customer.Invoices)
 16:                 .HasForeignKey(i => i.CustomerId)
 17:                 ;
 18: 
 19:             modelBuilder.Entity<Customer>()
 20:                .HasKey(customer => customer.CustomerId)
 21:                ;
 22:         }
 23:     }

InterceptorDbContext will build the InterceptionContext object and pass it to your interceptors Before and After the SaveChanges method is called.

Your Input Matters

The current version of the libraries are 0.2.1. If there are other commonly used interceptor implementations that should be implemented I’ll be happy to create packages for those. If you have other feature requests for the package I’d love to hear them.

Implementing EF4 Change Interceptors

Interceptors are a great way to handle some repetitive and predictable data management tasks. NHibernate has good support for Interceptors both at the change and query levels. I wondered how hard it would be to write interceptors for the new EF4 CTP and was surprised at how easy it actually was… well, for the change interceptors anyway. It looks like query interceptors would require a complete reimplementation of the Linq provider—not something I feel like undertaking right now.

On to the Code!

This is the first interface we’ll use to create a class that can respond to changes in the EF4 data context.

  1. namespace Yodelay.Data.Entity
  2. {
  3.     /// <summary>
  4.     /// Interface to support taking some action in response
  5.     /// to some activity taking place on an ObjectStateEntry item.
  6.     /// </summary>
  7.     public interface IInterceptor
  8.     {
  9.         void Before(ObjectStateEntry item);
  10.         void After(ObjectStateEntry item);
  11.     }
  12. }

 

We’ll also use this interface to add some conditional execution support.

  1. namespace Yodelay.Data.Entity
  2. {
  3.     /// <summary>
  4.     /// Adds conditional execution to an IInterceptor.
  5.     /// </summary>
  6.     public interface IConditionalInterceptor : IInterceptor
  7.     {
  8.         bool IsTargetEntity(ObjectStateEntry item);        
  9.     }
  10. }

 

The first interceptor I want to write is one that manages four audit columns automatically. First I need an interface that provides the audit columns:

  1. public interface IAuditEntity
  2. {
  3.     DateTime InsertDateTime { get; set; }
  4.     DateTime UpdateDateTime { get; set; }
  5.     string InsertUser { get; set; }
  6.     string UpdateUser { get; set; }
  7. }

The EF4 DbContext class provides an override for SaveChanges() that I can use to start handling the events. I decided to subclass DbContext and add the interception capability to the new class. I snipped the constructors for brevity, but all of the constructors from the base class are bubbled.

  1. public class DataContext : DbContext
  2. {
  3.     private readonly List<IInterceptor> _interceptors = new List<IInterceptor>();
  4.     public List<IInterceptor> Interceptors
  5.     {
  6.         get { return this._interceptors; }
  7.     }
  8.  
  9.     private void InterceptBefore(ObjectStateEntry item)
  10.     {
  11.         this.Interceptors.ForEach(intercept => intercept.Before(item));
  12.     }
  13.  
  14.     private void InterceptAfter(ObjectStateEntry item)
  15.     {
  16.         this.Interceptors.ForEach(intercept => intercept.After(item));
  17.     }
  18.  
  19.     public override int SaveChanges()
  20.     {
  21.         const EntityState entitiesToTrack = EntityState.Added |
  22.                                             EntityState.Modified |
  23.                                             EntityState.Deleted;
  24.         var elementsToSave =
  25.             this.ObjectContext
  26.                 .ObjectStateManager
  27.                 .GetObjectStateEntries(entitiesToTrack)
  28.                 .ToList();
  29.  
  30.         elementsToSave.ForEach(InterceptBefore);
  31.  
  32.         var result = base.SaveChanges();
  33.         elementsToSave.ForEach(InterceptAfter);
  34.         return result;
  35.     }

 

I only want the AuditChangeInterceptor to fire if the object implements the IAuditEntity interface. I could have directly implemented IConditionalInterceptor, but I decided to extract the object-type criteria into a super-class.

  1. public abstract class TypeInterceptor : IConditionalInterceptor
  2. {
  3.     private readonly System.Type _targetType;
  4.     public Type TargetType { get { return _targetType; }}
  5.  
  6.     protected TypeInterceptor(System.Type targetType)
  7.     {
  8.         this._targetType = targetType;
  9.     }
  10.  
  11.     public virtual bool IsTargetEntity(ObjectStateEntry item)
  12.     {
  13.         return item.State != EntityState.Detached &&
  14.             this.TargetType.IsInstanceOfType(item.Entity);
  15.     }
  16.  
  17.     public void Before(ObjectStateEntry item)
  18.     {
  19.         if (this.IsTargetEntity(item))
  20.             this.OnBefore(item);
  21.     }
  22.  
  23.     protected abstract void OnBefore(ObjectStateEntry item);
  24.  
  25.     public void After(ObjectStateEntry item)
  26.     {
  27.         if (this.IsTargetEntity(item))
  28.             this.OnAfter(item);
  29.     }
  30.  
  31.     protected abstract void OnAfter(ObjectStateEntry item);
  32. }

 

I also decided that the super-class should provide obvious method-overrides for BeforeInsert, AfterInsert, BeforeUpdate, etc.. For that I created a generic class that sub-classes TypeInterceptor and provides friendlier methods to work with.

  1. public class ChangeInterceptor<T> : TypeInterceptor
  2. {
  3.     #region Overrides of Interceptor
  4.  
  5.     protected override void OnBefore(ObjectStateEntry item)
  6.     {
  7.         T tItem = (T) item.Entity;
  8.         switch(item.State)
  9.         {
  10.             case EntityState.Added:
  11.                 this.OnBeforeInsert(item.ObjectStateManager, tItem);
  12.                 break;
  13.             case EntityState.Deleted:
  14.                 this.OnBeforeDelete(item.ObjectStateManager, tItem);
  15.                 break;
  16.             case EntityState.Modified:
  17.                 this.OnBeforeUpdate(item.ObjectStateManager, tItem);
  18.                 break;
  19.         }
  20.     }
  21.  
  22.     protected override void  OnAfter(ObjectStateEntry item)
  23.     {
  24.         T tItem = (T)item.Entity;
  25.         switch (item.State)
  26.         {
  27.             case EntityState.Added:
  28.                 this.OnAfterInsert(item.ObjectStateManager, tItem);
  29.                 break;
  30.             case EntityState.Deleted:
  31.                 this.OnAfterDelete(item.ObjectStateManager, tItem);
  32.                 break;
  33.             case EntityState.Modified:
  34.                 this.OnAfterUpdate(item.ObjectStateManager, tItem);
  35.                 break;
  36.         }        
  37.     }
  38.  
  39.     #endregion
  40.  
  41.     public virtual void OnBeforeInsert(ObjectStateManager manager, T item)
  42.     {
  43.         return;
  44.     }
  45.  
  46.     public virtual void OnAfterInsert(ObjectStateManager manager, T item)
  47.     {
  48.         return;
  49.     }
  50.  
  51.     public virtual void OnBeforeUpdate(ObjectStateManager manager, T item)
  52.     {
  53.         return;
  54.     }
  55.  
  56.     public virtual void OnAfterUpdate(ObjectStateManager manager, T item)
  57.     {
  58.         return;
  59.     }
  60.  
  61.     public virtual void OnBeforeDelete(ObjectStateManager manager, T item)
  62.     {
  63.         return;
  64.     }
  65.  
  66.     public virtual void OnAfterDelete(ObjectStateManager manager, T item)
  67.     {
  68.         return;
  69.     }
  70.  
  71.     public ChangeInterceptor() : base(typeof(T))
  72.     {
  73.         
  74.     }
  75. }

 

Finally, I created subclassed ChangeInterceptor<IAuditEntity>.

  1. public class AuditChangeInterceptor : ChangeInterceptor<IAuditEntity>
  2. {
  3.     public override void OnBeforeInsert(ObjectStateManager manager, IAuditEntity item)
  4.     {
  5.         base.OnBeforeInsert(manager, item);
  6.  
  7.         item.InsertDateTime = DateTime.Now;
  8.         item.InsertUser = System.Threading.Thread.CurrentPrincipal.Identity.Name;
  9.         item.UpdateDateTime = DateTime.Now;
  10.         item.UpdateUser = System.Threading.Thread.CurrentPrincipal.Identity.Name;
  11.     }
  12.  
  13.     public override void OnBeforeUpdate(ObjectStateManager manager, IAuditEntity item)
  14.     {
  15.         base.OnBeforeUpdate(manager, item);
  16.         item.UpdateDateTime = DateTime.Now;
  17.         item.UpdateUser = System.Threading.Thread.CurrentPrincipal.Identity.Name;
  18.     }
  19. }

 

I plugged this into my app, and it worked on the first go.

Another common scenario I encounter is “soft-deletes.” A “soft-delete” is a virtual record deletion that does not actual remove the record from the database. Instead it sets an IsDeleted flag on the record, and the record is then excluded from other queries. The problem with soft-deletes is that developers and report writers always have to remember to add the “IsDeleted == false” criteria to every query in the system that touches the affected records. It would be great to replace the standard delete functionality with a soft-delete, and to modify the IQueryable to return only records for which “IsDeleted == false.” Unfortunately, I was unable to find a clean way to add query-interceptors to the data model to keep deleted records from being returned. However, I was able to get the basic soft-delete ChangeInterceptor to work. Here is that code.

 

  1. public interface ISoftDelete
  2. {
  3.     bool IsDeleted { get; set; }
  4. }

 

  1. public class SoftDeleteChangeInterceptor : ChangeInterceptor<ISoftDelete>
  2. {
  3.     public override void OnBeforeInsert(ObjectStateManager manager, ISoftDelete item)
  4.     {
  5.         base.OnBeforeInsert(manager, item);
  6.         item.IsDeleted = false;
  7.     }
  8.  
  9.     public override void OnBeforeDelete(ObjectStateManager manager, ISoftDelete item)
  10.     {
  11.         if (item.IsDeleted)
  12.             throw new InvalidOperationException("Item is already deleted.");
  13.         
  14.         base.OnBeforeDelete(manager, item);
  15.         item.IsDeleted = true;
  16.         manager.ChangeObjectState(item, EntityState.Modified);
  17.     }
  18. }

 

Here’s the complete diagram of the code:

image

EF4 has come a long way with respect to supporting extensibility. It still needs query-interceptors to be feature-parable with other ORM tools such as NHibernate, but I suspect that it is just a matter of time before the MS developers get around to adding that functionality. For now, you can use the interceptor model I’ve demo’ed here to add functionality to your data models. Perhaps you could use them to add logging, validation, or security checks to your models. What can you come up with?

Understanding the Fluent Configuration API for One-To-Many Relationships in “Code-First” EF4

Configuring ORM’s through fluent api calls is relatively new to me. For the last three years or so I’ve been using EF1 and Linq-To-Sql as my data modeling tools of choice. My first exposure to a code-first ORM tool came in June when I started working with Fluent NHibernate. As interesting as that has been, I hadn’t really be faced with the issue of proper configuration because I’ve had someone on our team that does it easily. This weekend I started working on a sample project using the EF4 CTP, and the biggest stumbling block has been modeling the relationships.

The context code is a SCRUM process management app I’m writing. Here’s the model:

Model

The relationship I tried to model was the one between Project and Story via the Backlog property. Since “Project” is the first entity I needed to write, my natural inclination was to model the relationship to “Story” first.

  1. public class DataContext : DbContext
  2. {
  3.     public DbSet<Project> Projects { get; set; }
  4.     public DbSet<Resource> Resources { get; set; }
  5.     public DbSet<Sprint> Sprints { get; set; }
  6.     public DbSet<Story> Stories { get; set; }
  7.     public DbSet<StoryStatus> StoryStatuses { get; set; }
  8.     public DbSet<Task> Tasks { get; set; }
  9.  
  10.     protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder builder)
  11.     {
  12.         base.OnModelCreating(builder);
  13.  
  14.         builder
  15.             .Entity<Project>()
  16.             .HasMany(e => e.BackLog);

I knew that “Story” would be slightly more complex because it has two properties that map back to “Project.” These are the “Project” property ,and the “ProjectId” property. Some of the EF4 samples I found refer to a “Relationship” extension method that I was unable to find in the API, so I was fairly confused. I finally figured out what I needed to do by reading this post from Scott Hanselman, though he doesn’t specifically highlight the question I was trying to answer.

This is the mapping code I created for “Story:”

  1. builder.Entity<Story>()
  2.     .HasRequired(s => s.Project)
  3.     .HasConstraint((story, project) => story.ProjectId == project.Id);

and this is the code I’m using to create a new Story for a project:

  1. [HttpPost]
  2. public ActionResult Create(Story model)
  3. {
  4.     using (var context = new DataContext())
  5.     {
  6.         var project = context.Projects.Single(p => p.Id == model.ProjectId);
  7.         project.BackLog.Add(model);
  8.         context.SaveChanges();
  9.         return RedirectToAction("Backlog", "Story", new {id = project.Id});
  10.     }
  11. }

 

When I tried to save the story, the data context threw various exceptions. I thought I could avoid the problem by rewriting the code so that I was just adding the story directly to the “Stories” table on the DataContext (which I think will ultimately be the right thing as it saves an unnecessary database call), but that would have been hacking around the problem and not really understanding what was wrong with what I was doing. It just took some staring at Scott Hanselman’s code sample for awhile to realize what was wrong with my approach. Before I explain it, let me show you the mapping code that works.

  1. protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder builder)
  2. {
  3.     base.OnModelCreating(builder);
  4.  
  5.     builder
  6.         .Entity<Project>()
  7.         .Property(p => p.Title)
  8.         .IsRequired();
  9.  
  10.     // api no longer has Relationship() extension method.
  11.     builder.Entity<Story>()
  12.         .HasRequired(s => s.Project)
  13.         .WithMany(p => p.BackLog)
  14.         .HasConstraint((story, project) => story.ProjectId == project.Id);
  15.  
  16. }

Notice what’s missing? I completely yanked the mapping of “Project->Stories” from the “Project” model’s perspective. Instead, I map the one-to-many relationship from the child-entity’s perspective, i.e., “Story.” Here’s how to read the mapping.

  1. // api no longer has Relationship() extension method.
  2. builder.Entity<Story>() // The entity story
  3.     .HasRequired(s => s.Project) // has a required property called "Project" of type "Project"
  4.     .WithMany(p => p.BackLog) // and "Project" has a reference back to "Story" through it's "Backlog" collection property
  5.     .HasConstraint((story, project) => story.ProjectId == project.Id); // and Story.ProjectId is the same as Story.Project.Id
  6. ;

The key here is understanding that one-to-many relationships must be modeled from the perspective of the child-entity.

Previous Page