Wednesday, December 19, 2012

Using expression trees to get rid of "magic" strings in C#

"Magic" strings may appear in programming code in many cases. For example, if we need to pass property names to some method.

Let's say we need to make DatabaseIndexCreator class that creates database index for specified property name(or properties, if index is compound) of some model(in our example it's Person class):
 public class Person  
 {  
   public string Name { get; set; }  
   public string Surname { get; set; }  
   public bool Sex { get; set; }  
 }  

So, we end up with function that accept property names as strings:
 public class DatabaseIndexCreator<TEntity>  
 {  
   public void CreateIndex(string[] fields)  
   {  
     //creating index  
   }  
 }  

And use this class like this:
 DatabaseIndexCreator<Person> indexCreator = new DatabaseIndexCreator<Person>();  
 //creating simple index:  
 indexCreator.CreateIndex(new[] { "Name" });  
 //creating compound index:  
 indexCreator.CreateIndex(new[] { "Name", "Sex" });  

It's bad design, because you can forget to rename this "magic" strings after renaming properties of Person class.

But you can use Expression Trees - nice C# feature to get rid of this "magic" strings in your code.

Let's create class with extension method for Expression<Func<T, U>> expression:
  public static class ExpressionEx  
  {  
     public static HashSet<string> AsMemberExpressionKeys<T, U>(this Expression<Func <T, U>> index)  
     {  
       var exp = index.Body as NewExpression;  
       var keys = new HashSet<string>();  
       if (exp != null)  
       {  
         foreach (var x in exp.Arguments.OfType<MemberExpression>())  
         {  
           keys.Add(GetPropertyAlias(x));  
         }  
       }  
       else if (index.Body is MemberExpression)  
       {  
         var me = index.Body as MemberExpression;  
         keys.Add(GetPropertyAlias(me));  
       }  
       return keys;  
     }  
     private static string GetPropertyAlias(MemberExpression mex)  
     {  
       var retval = "";  
       var parentEx = mex.Expression as MemberExpression;  
       if (parentEx != null)  
       {  
         retval += GetPropertyAlias(parentEx) + ".";  
       }        
       retval += mex.Member.Name;  
       return retval;  
     }  
  }  

What benefits can we get from this tricky extension method?

Now we can refactor our DatabaseIndexCreator class like this:
 public class DatabaseIndexCreator<TEntity>  
 {  
   public void CreateIndex<TReturn>(Expression<Func<TEntity, TReturn>> exp)  
   {  
     string[] fields = exp.AsMemberExpressionKeys().ToArray();  
     //creating index(simple or compound)  
   }  
 }  

And use this class like this:
 DatabaseIndexCreator<Person> indexCreator = new DatabaseIndexCreator<Person>();  
 //creating simple index:  
 indexCreator.CreateIndex(t => t.Name);  
 //creating compound index:  
 indexCreator.CreateIndex(t => new {t.Name, t.Sex});  

As you see, there is no more "magic" strings, that is very good for our code design and our nerves.

No comments:

Post a Comment