title: Get [DisplayName] attribute of a property in strongly-typed way
body: <p>Good day!</p>
  
  <p>I've such method to get <code>[DisplayName]</code> attribute value of a property (which is attached directly or using <code>[MetadataType]</code> attribute). I use it in rare cases where I need to get <code>[DisplayName]</code> in controller code.</p>
  
  <pre><code>public static class MetaDataHelper
  {
      public static string GetDisplayName(Type dataType, string fieldName)
      {       
          // First look into attributes on a type and it's parents
          DisplayNameAttribute attr;
          attr = (DisplayNameAttribute)dataType.GetProperty(fieldName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
  
          // Look for [MetadataType] attribute in type hierarchy
          // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
          if (attr == null)
          {
              MetadataTypeAttribute metadataType = (MetadataTypeAttribute)dataType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
              if (metadataType != null)
              {
                  var property = metadataType.MetadataClassType.GetProperty(fieldName);
                  if (property != null)
                  {
                      attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
                  }
              }
          }
          return (attr != null) ? attr.DisplayName : String.Empty;
      }
  }
  </code></pre>
  
  <p>It works, but it has two drawbacks:</p>
  
  <ul>
  <li>It requires field name as string</li>
  <li>It doesn't work if I want to get property of a property</li>
  </ul>
  
  <p>Is it possible to overcome both problems using lambdas, something like we have in ASP.NET MVC:</p>
  
  <pre><code>Html.LabelFor(m =&gt; m.Property.Can.Be.Very.Complex.But.Strongly.Typed);  
  </code></pre>
  
  <p><strong>Update</strong></p>
  
  <p>Here is an updated and checked version from <em>BuildStarted</em> solution. It is modified to use <code>DisplayName</code> attribute (you can modify back to <code>Display</code> attribute if you use it). And fixed minor bugs to get attribute of nested properties.</p>
  
  <pre><code>public static string GetDisplayName&lt;TModel&gt;(Expression&lt;Func&lt;TModel, object&gt;&gt; expression)
  {
      Type type = typeof(TModel);
  
      string propertyName = null;
      string[] properties = null;
      IEnumerable&lt;string&gt; propertyList;
      //unless it's a root property the expression NodeType will always be Convert
      switch (expression.Body.NodeType)
      {
          case ExpressionType.Convert:
          case ExpressionType.ConvertChecked:
              var ue = expression.Body as UnaryExpression;
              propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
              break;
          default:
              propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
              break;
      }
  
      //the propert name is what we're after
      propertyName = propertyList.Last();
      //list of properties - the last property name
      properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties
  
      foreach (string property in properties)
      {
          PropertyInfo propertyInfo = type.GetProperty(property);
          type = propertyInfo.PropertyType;
      }
  
      DisplayNameAttribute attr;
      attr = (DisplayNameAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
  
      // Look for [MetadataType] attribute in type hierarchy
      // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
      if (attr == null)
      {
          MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
          if (metadataType != null)
          {
              var property = metadataType.MetadataClassType.GetProperty(propertyName);
              if (property != null)
              {
                  attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
              }
          }
      }
      return (attr != null) ? attr.DisplayName : String.Empty;
  }
  </code></pre>