Geeks With Blogs
Dane Morgridge Programmer, Geek, ASPInsider
A blog about code and data access

I am a huge fan of code reuse, as any good developer would be, but there it the tendency at times to write "near-duplicated" code.  Near-duplicated code is when you have a function, like a database save or delete, that is rewritten multiple times, but with small, but seemingly important differences.  There are many times where those "seemingly important" differences can be easily abstracted away. 

For example, I can write a Save method in the Entity Framework as follows:


   1: public Person SavePerson(Person person)
   2: {
   3:     using (EFDemoEntities db = new EFDemoEntities())
   4:     {
   5:         Person entity = db.Person.Where(p => p.PersonId == person.PersonId).FirstOrDefault();
   6:         if (entity == null)
   7:         {
   8:             db.AddObject("Person", person);
   9:         }
  10:         else
  11:         {
  12:             db.ApplyPropertyChanges("Person", person);
  13:         }
  14:         db.SaveChanges();
  15:         db.Refresh(System.Data.Objects.RefreshMode.StoreWins, person);
  16:         return person;
  17:     }
  18: }


Here I have a method that will take a Person entity and save it.  If first does a lookup to see if the person already exists on line 5.  If it does, the entity will be loaded into the context and line 12 will apply the changes from "person" to "entity".  This is done "magically" because of the way Entity Framework stores key data within the object context.  If the entity doesn't exist, it will be added on line 8 and then the save changes call on line 14 will commit it to the database.  I have this method also returning the entity passed in because there are quite a few times where I need to have the ability to pull back an inserted entity and start using it right away. 

I can easily write several methods to do the same thing for each entity type that I will be saving, or I can build a reusable method that I only have to write once.  By making a couple of small changes, I have a method that will work with any entity in the model:


   1: public object Save(string entitySetName, object entity)
   2: {
   3:     using (EFDemoEntities db = new EFDemoEntities())
   4:     {
   5:         System.Data.EntityKey key = db.CreateEntityKey(entitySetName, entity);
   6:         if (int.Parse(key.EntityKeyValues.First().Value.ToString()) > 0)
   7:         {
   8:             object obj = db.GetObjectByKey(key);
   9:             db.ApplyPropertyChanges(entitySetName, entity);
  10:         }
  11:         else
  12:         {
  13:             db.AddObject(entitySetName, entity);
  14:         }
  15:         db.SaveChanges();
  16:         db.Refresh(System.Data.Objects.RefreshMode.StoreWins, entity);
  17:         return entity;
  18:     }
  19: }


The "Save" method does the same thing as the "SavePerson" method, except that it can be used for any entity associated with the EFDemoEntities context.  There are a few differences between the two methods that make this possible:

Line 1 is changed to also take in the EntitySetName which was hard coded as a string in the first method. This is passed to the ApplyPropertyChanges and AddObject methods instead of hard coding a string.

Line 5 is creating an entity key from the passed in entity.  This has to be done because the key will sometimes be null depending on where the entity came from.  The EntityKey is used to fetch the current object in the database for comparison. which is done in line 8.  This function is built to expect that there will be a single key on the entity and that it will be an int.  This if statement would need to be modified to check for different keys depending on your model.

So as you see we have a method now that can be called when ever we need to save an entity object instead of having to write a new save method for each entity that we need to be able to save.  While this method may not be useable in every scenario, it should cover most of your saving needs.  You could also abstract this out a level further and pass in the context as a parameter so that the same method would work across multiple data contexts.

Note:  The code above is written against v1 of the Entity Framework.  To use this with Entity Framework 4, you will need to change line 9 from ApplyPropertyChanges to ApplyCurrentValues.  With that change, I have been using this method with Entity Framework 4 POCO classes so this method ports easily between versions.

This same concept can easily be applied to deletes as well.

Posted on Wednesday, December 2, 2009 11:06 PM | Back to top

Copyright © Dane Morgridge | Powered by: