title: Get the property, as a string, from an Expression<Func<TModel,TProperty>>
body: <p>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 <code>Expression&lt;Func&lt;TModel,TProperty&gt;&gt;</code> and are used as such: <code>SortOption.Field = (p =&gt; p.FirstName);</code>. I've gotten this working perfectly for this simple case.</p>
  
  <p>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(<code>SortOption.Field = (p =&gt; p.Address.State.Abbreviation);</code>). This code has some very different assumptions in the need to support deeply-nested properties.</p>
  
  <p>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 <em>good</em> 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?</p>
  
  <p>Requirements:</p>
  
  <ul>
  <li>Given the expression <code>p =&gt; p.FirstName</code> I need a string of <code>"FirstName"</code>.</li>
  <li>Given the expression <code>p =&gt; p.Address.State.Abbreviation</code> I need a string of <code>"Address.State.Abbreviation"</code></li>
  </ul>
  
  <hr>
  
  <p>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 <code>DynamicExpression.ParseLambda</code> comes from the <a href="http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx" rel="noreferrer">Dynamic LINQ</a> stuff and <code>Property.PropertyToString()</code> is what this question is about.</p>
  
  <pre><code>/// &lt;summary&gt;
  /// This defines a framework to pass, across serialized tiers, sorting logic to be performed.
  /// &lt;/summary&gt;
  /// &lt;typeparam name="TModel"&gt;This is the object type that you are filtering.&lt;/typeparam&gt;
  /// &lt;typeparam name="TProperty"&gt;This is the property on the object that you are filtering.&lt;/typeparam&gt;
  [Serializable]
  public class SortOption&lt;TModel, TProperty&gt; : ISerializable where TModel : class
  {
      /// &lt;summary&gt;
      /// Convenience constructor.
      /// &lt;/summary&gt;
      /// &lt;param name="property"&gt;The property to sort.&lt;/param&gt;
      /// &lt;param name="isAscending"&gt;Indicates if the sorting should be ascending or descending&lt;/param&gt;
      /// &lt;param name="priority"&gt;Indicates the sorting priority where 0 is a higher priority than 10.&lt;/param&gt;
      public SortOption(Expression&lt;Func&lt;TModel, TProperty&gt;&gt; property, bool isAscending = true, int priority = 0)
      {
          Property = property;
          IsAscending = isAscending;
          Priority = priority;
      }
  
      /// &lt;summary&gt;
      /// Default Constructor.
      /// &lt;/summary&gt;
      public SortOption()
          : this(null)
      {
      }
  
      /// &lt;summary&gt;
      /// This is the field on the object to filter.
      /// &lt;/summary&gt;
      public Expression&lt;Func&lt;TModel, TProperty&gt;&gt; Property { get; set; }
  
      /// &lt;summary&gt;
      /// This indicates if the sorting should be ascending or descending.
      /// &lt;/summary&gt;
      public bool IsAscending { get; set; }
  
      /// &lt;summary&gt;
      /// This indicates the sorting priority where 0 is a higher priority than 10.
      /// &lt;/summary&gt;
      public int Priority { get; set; }
  
      #region Implementation of ISerializable
  
      /// &lt;summary&gt;
      /// This is the constructor called when deserializing a SortOption.
      /// &lt;/summary&gt;
      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&lt;TModel, TProperty&gt;(info.GetString("Property"), default(TModel), default(TProperty));
      }
  
      /// &lt;summary&gt;
      /// Populates a &lt;see cref="T:System.Runtime.Serialization.SerializationInfo"/&gt; with the data needed to serialize the target object.
      /// &lt;/summary&gt;
      /// &lt;param name="info"&gt;The &lt;see cref="T:System.Runtime.Serialization.SerializationInfo"/&gt; to populate with data. &lt;/param&gt;
      /// &lt;param name="context"&gt;The destination (see &lt;see cref="T:System.Runtime.Serialization.StreamingContext"/&gt;) for this serialization. &lt;/param&gt;
      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
  }
  </code></pre>