Use Lambda Expressions for Strongly-Typed Goodness, and Serialize it too


With my venture into MVC2 recently, I’ve fallen in love with the strongly-typed helpers and am attempting to provide mechanisms similar to those in many different areas of my code so I can cease the use of hardcoded/constant strings all over the place and get better compile-time error checking.

This post is to show you how you can use Lambda Expressions to perform these sorts of things in an N-Tier system in a way where you can even serialize these expressions. I’m only handling a very basic scenario here but the tools I use are VERY flexible and can handle, out-of-the-box, much more complex scenarios.

So the setting where I’m using this in is within MVC where a grid is displaying paged data and I’d like to filter and sort the data. For this post, let’s look at the dynamic sorting options. Not only do I want to sort the data, but I also want to support multi-column sorting. However the UI implements this, I don’t really care but I want to expose a strongly-typed way for it to do so, if it chooses (i.e. as the default so future coders are less likely to create bugs that sneak into production).

In order to do this, I’ve created a fairly simple class to store these sorting options. I share both on the UI side and the server side of WCF as this is acceptable in my system. However, I do so in a way that COULD be consumed by any other technology while losing the strongly-typed functionality but not losing all functionality. Here is my initial version of this class:

    public class SortOption<TModel, TProperty> where TModel : class
    {
        public SortOption(Expression<Func<TModel, TProperty>> property, bool isAscending = true, int priority = 0)
        {
            Property = property;
            IsAscending = isAscending;
            Priority = priority;
        }
 
        public SortOption()
            : this(null)
        {
        }
 
        public Expression<Func<TModel, TProperty>> Property { get; set; }
 
        public bool IsAscending { get; set; }
 
        public int Priority { get; set; }
    }

Pretty simple, right? This actually is a VERY simple class with the exception of that pesky Property object with the nasty Expression<Func<TModel, TProperty>> signature. In case you didn’t know, that nasty signature is how we would write code such as foo.Property = (x => x.FirstName); in order to say that we want the “FirstName” property off of whatever object we instantiated foo with as TModel (whether it’s a Person class or a Pet class or whatever). Because of that nasty Expression signature, there’s no way this is going to serialize nicely, or at all. Lots of Googling all over, everybody tells you that you simply cannot serialize Lambda Expressions, don’t try!

Luckily for us, they are only partially correct. It depends on what you want to do with that Lambda Expression that determines if you really can serialize it or not. All we’re using it for is to have strongly-typed code at development time. At run-time, it does nothing for us and in actuality is a slight performance hindrance. But this is the age of sacrificing performance for developer productivity, so this is okay (at least for some). So with the help of some Dynamic LINQ Libraries (haha!!), we can actually serialize these expressions that we’re wanting to use in this scenario! ScottGu has a nice post introducing these and I encourage you to read it. What we are using, as ScottGu mentions, is the code in the Dynamic.cs file in the “\LinqSamples\DynamicQuery” project.

Take a looksee through the Dynamic.cs file and you’ll see a static DynamicExpression class with some ParseLambda functions in there – these are the things that we care about mostly for this. As you can see, these functions are quite powerful and flexible.

Once we make some slight modifications to our SortOption<TModel, TProperty> object to implement ISerializable, we can leverage one of those ParseLambda functions to perform the hard part of our custom serialization of this class. Below is a newer version of this class, all with nice comments, to do exactly what we’re wanting to do:

    /// <summary>
    /// This defines a framework to pass, across serialized tiers, sorting logic to be performed.
    /// </summary>
    /// <typeparam name="TModel">This is the object type that you are filtering.</typeparam>
    /// <typeparam name="TProperty">This is the property on the object that you are filtering.</typeparam>
    [Serializable]
    public class SortOption<TModel, TProperty> : ISerializable where TModel : class
    {
        /// <summary>
        /// Convenience constructor.
        /// </summary>
        /// <param name="property">The property to sort.</param>
        /// <param name="isAscending">Indicates if the sorting should be ascending or descending</param>
        /// <param name="priority">Indicates the sorting priority where 0 is a higher priority than 10.</param>
        public SortOption(Expression<Func<TModel, TProperty>> property, bool isAscending = true, int priority = 0)
        {
            Property = property;
            IsAscending = isAscending;
            Priority = priority;
        }
 
        /// <summary>
        /// Default Constructor.
        /// </summary>
        public SortOption()
            : this(null)
        {
        }
 
        /// <summary>
        /// This is the field on the object to filter.
        /// </summary>
        public Expression<Func<TModel, TProperty>> Property { get; set; }
 
        /// <summary>
        /// This indicates if the sorting should be ascending or descending.
        /// </summary>
        public bool IsAscending { get; set; }
 
        /// <summary>
        /// This indicates the sorting priority where 0 is a higher priority than 10.
        /// </summary>
        public int Priority { get; set; }
 
        #region Implementation of ISerializable
 
        /// <summary>
        /// This is the constructor called when deserializing a SortOption.
        /// </summary>
        protected SortOption(SerializationInfo info, StreamingContext context)
        {
            IsAscending = info.GetBoolean("IsAscending");
            Priority = info.GetInt32("Priority");
 
            // We just persisted this by the PropertyName. So let's rebuild the Lambda Expression from that.
            Property = DynamicExpression.ParseLambda<TModel, TProperty>(info.GetString("Property"), default(TModel), default(TProperty));
        }
 
        /// <summary>
        /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
        /// </summary>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param>
        /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            // Just stick the property name in there. We'll rebuild the expression based on that on the other end.
            info.AddValue("Property", Property.MemberWithoutInstance());
            info.AddValue("IsAscending", IsAscending);
            info.AddValue("Priority", Priority);
        }
 
        #endregion
    }

With this simple custom serialization implementation, we can now have strongly-typed, serializable Lambda expressions and hopefully prevent sneaky bugs getting in there. Granted, when we serialize/deserialize this, we’re convert it to simple bug-tolerant strings, but this is the ugly plumbing work that you tend to not change that much. And also, when you break it, it tends to be pretty obvious and not very sneaky. So that’s okay. And from another perspective, good luck serializing anything if you can’t convert it into a string of some sort!



SOA-based Architecture in progress


I’ve been tasked at coming up with a standard architecture for us to apply to our future projects. Yeah, I know, there’s no blanket answer for everything, but given the requirements I expect, I think a single architecture can handle 95% of what we’ll be doing and we can deviate/improve as necessary for the other 5%.

Ultimately I don’t yet know what this is going to look like but this is the direction I’m leaning in:
(more…)


Jaxidian Update is proudly powered by WordPress and themed by Mukkamu