Get the property, as a string, from an Expression<Func<TModel,TProperty>>

Posted by Jaxidian on Stack Overflow See other posts from Stack Overflow or by Jaxidian
Published on 2010-05-07T14:57:52Z Indexed on 2010/05/07 15:18 UTC
Read the original article Hit count: 246

Filed under:
|

I use some strongly-typed expressions that get serialized to allow my UI code to have strongly-typed sorting and searching expressions. These are of type Expression<Func<TModel,TProperty>> and are used as such: SortOption.Field = (p => p.FirstName);. I've gotten this working perfectly for this simple case.

The code that I'm using for parsing the "FirstName" property out of there is actually reusing some existing functionality in a third-party product that we use and it works great, until we start working with deeply-nested properties(SortOption.Field = (p => p.Address.State.Abbreviation);). This code has some very different assumptions in the need to support deeply-nested properties.

As for what this code does, I don't really understand it and rather than changing that code, I figured I should just write from scratch this functionality. However, I don't know of a good way to do this. I suspect we can do something better than doing a ToString() and performing string parsing. So what's a good way to do this to handle the trivial and deeply-nested cases?

Requirements:

  • Given the expression p => p.FirstName I need a string of "FirstName".
  • Given the expression p => p.Address.State.Abbreviation I need a string of "Address.State.Abbreviation"

While it's not important for an answer to my question, I suspect my serialization/deserialization code could be useful to somebody else who finds this question in the future, so it is below. Again, this code is not important to the question - I just thought it might help somebody. Note that DynamicExpression.ParseLambda comes from the Dynamic LINQ stuff and Property.PropertyToString() is what this question is about.

/// <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.PropertyToString());
        info.AddValue("IsAscending", IsAscending);
        info.AddValue("Priority", Priority);
    }

    #endregion
}

© Stack Overflow or respective owner

Related posts about c#

Related posts about lambda-expressions