2012-03-28 23 views
4

私は以下のことを実現するために、式ツリーと匿名型を使用しようとしています。は、どのように私はラムダ式と匿名型を使用したタイプのプロパティ名を取得するのですか?

のは、私はこのクラスを持っているとしましょう:

class Person 
{ 
    public string FirstName {get;set;} 
    public string MiddleName {get;set;} 
    public string LastName {get;set;} 
    public DateTime DateOfBirth {get;set;} 
} 

は今、私は次のように呼び出すことができるようにしたい:

string[] names = Foo<Person>(x=> new { x.LastName, x.DateOfBirth }); 

私は名前が2つの項目、「姓」と「dateOfBirthのを入れたいです"

私は文字列sqlを書くのではなく、コンパイル時に安全な方法で拡張しようとしています。すべてを選択するのではなく、SQLに含めるプロパティ/列のリストを指定できます。かなり大きなエンティティがあり、パフォーマンス上の理由からすべての列を選択したくない場合があります。

+1

学習教材以外にも、独自のORMユーティリティを実装する特別な理由はありますか?そこには多くのORMプロバイダがあり、しばしば家庭で栽培されているものは価値があるよりも多くの面倒を引き起こす。 – Lukazoid

+0

質問を更新しました。私は実際には、データクラスのすべてのプロパティではなく、SELECT内の指定された列をサポートするようにPetaPocoを拡張しようとしています。 – Brady

答えて

1

私は怠け者ので、このコードはパブリックプロパティを処理します。しかし、それはあなたを始めさせる良い基盤になるはずです。

public static string[] Foo<T>(Expression<Func<T, object>> func) 
{ 
    var properties = func.Body.Type.GetProperties(); 

    return typeof(T).GetProperties() 
     .Where(p => properties.Any(x => p.Name == x.Name)) 
     .Select(p => 
     { 
      var attr = (ColumnAttribute) p.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault(); 
      return (attr != null ? attr.Name : p.Name); 
     }).ToArray(); 
} 
0

コードのページが千の言葉なので、ここではMicrosoftがPrismでそれをしない方法は次のとおりです。あなたのアカウントに属性を取りたい場合は

///<summary> 
/// Provides support for extracting property information based on a property expression. 
///</summary> 
public static class PropertySupport 
{ 
    /// <summary> 
    /// Extracts the property name from a property expression. 
    /// </summary> 
    /// <typeparam name="T">The object type containing the property specified in the expression.</typeparam> 
    /// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param> 
    /// <returns>The name of the property.</returns> 
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception> 
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/> 
    ///  Not a <see cref="MemberExpression"/><br/> 
    ///  The <see cref="MemberExpression"/> does not represent a property.<br/> 
    ///  Or, the property is static. 
    /// </exception> 
    public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression) 
    { 
     if (propertyExpression == null) 
     { 
      throw new ArgumentNullException("propertyExpression"); 
     } 

     var memberExpression = propertyExpression.Body as MemberExpression; 
     if (memberExpression == null) 
     { 
      throw new ArgumentException(Resources.PropertySupport_NotMemberAccessExpression_Exception, "propertyExpression"); 
     } 

     var property = memberExpression.Member as PropertyInfo; 
     if (property == null) 
     { 
      throw new ArgumentException(Resources.PropertySupport_ExpressionNotProperty_Exception, "propertyExpression"); 
     } 

     var getMethod = property.GetGetMethod(true); 
     if (getMethod.IsStatic) 
     { 
      throw new ArgumentException(Resources.PropertySupport_StaticExpression_Exception, "propertyExpression"); 
     } 

     return memberExpression.Member.Name; 
    } 
} 

少し複雑になるだろうが、一般的な考え方ですExpression<Func<T>>を受け入れ、対象となる物件の名前を釣り上げることは同じです。

更新:通りであり、この方法は、唯一つのパラメータを受け入れます。私はそれをガイドラインとして提供しました。アイデアはもちろん、一般化することができます。

public static string[] ExtractPropertyNames<T>(
    Expression<Func<T, object>> propertyExpression) 

この方法は、Tを取り、あなたはその後、時に反映させることができます匿名型を返す式を受け入れます。あなたはobjectための第二の型パラメータを置き換えることができますが、あなたがやりたいだけの事は種類に反映されるので、それは本当にここに何もしません。

+0

これは1人のメンバー(x => x.LastName)のみをサポートしていませんか? – Brady

-1

Html.LabelFor(LabelExtensions.LabelFor<TModel,TValue>のコードは、System.Web.Mvcアセンブリから逆アセンブルする必要があります。例えば

、属性メンバー値とメンバー名を置き換えるためとしてExpressionHelper.GetExpressionText

を見て - あなたは昔ながらのリフレクションを使用する必要があります。

+0

あなたの答えに詳細を追加する必要があります。怠け者であることは、ここの周りの道ではありません。違反はありません、ちょうど冗談;-) –

3

サイズのためにこれを試してみてください:

public static string[] Foo<T, TResult>(Expression<Func<T, TResult>> func) 
{ 
    return typeof(TResult).GetProperties().Select(pi => pi.Name).ToArray(); 
} 

あなたは、ラムダからの匿名型を返すされているとして、あなたはこの匿名型のすべてのプロパティを超えることができ、ループしているとの推測された名前を使用しますプロパティ。これを使用している場合しかし、構文がよりようになります:

Foo((Person x) => new { x.LastName, x.DateOfBirth }); 

二ジェネリック引数がanoymousタイプであるためです。

+0

この回答に感謝します。私はそれがいかに簡潔であるか好きです。 – Brady

+0

@ブラディ:あなたはペタポコの延長を終えたのですか?もしそうなら、あなたはそれのために枝を作りましたか?私はあなたのソリューションに興味があります。 – Don

+0

'Foo((Person x)=> x.FirstName)'は何を返しますか? '{" Chars "、" Length "}'これらの要素を持つ文字列配列を返します。つまり、単一のプロパティであっても、匿名型を使って 'Foo'を呼び出さなければならないということです。私の答えを見てください。 –

2

のみ単一のプロパティのいずれかが選択されている場合、または複数のプロパティが選択されている場合、ここで与えられた答えが働きます。どちらも両方のために働かない。answerによるLukazoidは、複数のプロパティに対してのみ機能し、残りのプロパティは、私の答えを書いているときに単一のプロパティに対してのみ機能します。

以下のコードは両方の場合を考慮しています。つまり、単一のプロパティと複数のプロパティを選択するために使用できます。ここに健全性チェックを追加していないので、ご自身で追加してください。

string[] Foo<T>(Expression<Func<Person, T>> func) 
{ 
    if (func.Body is NewExpression) 
    { 
     // expression selects multiple properties, 
     // OR, single property but as an anonymous object 

     // extract property names right from the expression itself 
     return (func.Body as NewExpression).Members.Select(m => m.Name).ToArray(); 

     // Or, simply using reflection, as shown by Lukazoid 
     // return typeof(T).GetProperties().Select(p => p.Name).ToArray(); 
    } 
    else 
    { 
     // expression selects only a single property of Person, 
     // and not as an anonymous object. 
     return new string[] { (func.Body as MemberExpression).Member.Name }; 
    }   
} 

以上簡潔に、三項演算子を使用すると、それはすべてがちょうどこのようになります。

string[] Foo<T>(Expression<Func<Person, T>> func) 
{ 
    return (func.Body as NewExpression) != null 
     ? typeof(T).GetProperties().Select(p => p.Name).ToArray() 
     : new string[] { (func.Body as MemberExpression).Member.Name }; 
} 

ダウンロードLinkPadファイル:LinkPad
オンラインそれを参照してください:Repl.it

指摘してお気軽にどうぞ私が逃したかもしれないもの。

関連する問題