質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

3050閲覧

ラムダ式内でリフレクションを使うには?

kikiinu

総合スコア21

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

1クリップ

投稿2018/03/06 03:07

編集2018/03/08 03:38

いつもお世話になっております。
いつも回答して頂き助かっています

回答を頂いて無事解決しました。
最終的なコードは1番下に載せています

開発環境は以下の通りです
VS2017
NETFramework,Version=v4.5.21

下記のラムダ内のn => n.Furigana部分のプロパティ名をリフレクションで対応したいのですが
できますか?

C#

1return db.Entry(_validationObject).Property(n => n.Furigana).GetValidationErrors();

下記のようにしたら
System.ArgumentException が発生しました
HResult=0x80070057
Message=The expression passed to method Property must represent a property defined on the type 'Company'.
パラメーター名:property
Source=EntityFramework
スタック トレース:
場所 System.Data.Entity.Internal.DbHelpers.ParsePropertySelector[TEntity,TProperty](Expression1 property, String methodName, String paramName) 場所 System.Data.Entity.Infrastructure.DbEntityEntry1.Property[TProperty](Expression`1 property)
場所

C#

1return db.Entry(_validationObject).Property(n => n.GetType().GetProperty("Furigana").GetValue(n)).GetValidationErrors();

ちなみに、以下のとおりにすると正しく出力されています

C#

1Debug.WriteLine(_validationObject.GetType().GetProperty("Furigana").GetValue(_validationObject));

下記が全体のコードです。
リフレクションで対応できればコード量がグッと減るんですけど・・・。

private void _dgv_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { var names = _dgv.Columns[e.ColumnIndex].Name.Split('-'); // 0 クラス名、1 プロパティ名 var t = _asm.GetType(this.GetType().Namespace + "." + names[0]); var method = t.GetMethod("Validate" + names[1]); if (method != null) { dynamic cellEx = _dgv[e.ColumnIndex, e.RowIndex]; cellEx.ErrorText = ""; var errors = (ICollection<DbValidationError>)method.Invoke(null, new object[] { _db, e.FormattedValue }); if (errors.Count != 0) { foreach (var err in errors) { cellEx.ErrorText += err.ErrorMessage + "\n"; } cellEx.ErrorText = cellEx.ErrorText.TrimEnd('\n'); } } }

C#

1 public static ICollection<DbValidationError> ValidateName(SeikouDbContext db, dynamic value) 2 { 3 _validationObject.Name = value; 4 return db.Entry(_validationObject).Property(n => n.Name).GetValidationErrors(); 5 } 6 7 public static ICollection<DbValidationError> ValidateFurigana(SeikouDbContext db, dynamic value) 8 { 9 _validationObject.Furigana = value; 10 return db.Entry(_validationObject).Property(n => n.Furigana).GetValidationErrors(); 11 } 12 13

上記のValiate~メソッドがプロパティごとにあります。

以下が解決した最終的なコードです。
Valiate~メソッドを作る必要がなくなりました

C#

1 private void _dgv_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) 2 { 3 dynamic cellEx = _dgv[e.ColumnIndex, e.RowIndex]; 4 5 var names = _dgv.Columns[e.ColumnIndex].Name.Split('-'); // 0 クラス名、1 プロパティ名 6 typeof(U).GetProperty(names[1]).SetValue(_validationObject, e.FormattedValue); 7 var errors = _db.Entry(_validationObject).Property(names[1]).GetValidationErrors(); 8 9 cellEx.ErrorText = ""; 10 foreach (var err in errors) 11 { 12 cellEx.ErrorText += err.ErrorMessage + "\n"; 13 } 14 if (errors.Count != 0) 15 { 16 cellEx.ErrorText = cellEx.ErrorText.TrimEnd('\n'); 17 e.Cancel = true; 18 _dgv.EndEdit(); 19 _dgv.InvalidateCell(cellEx); 20 } 21 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんにちは。
EF関連の部分はちょっと再現して検証する余裕がなく、ドキュメントを読みつつの回答です。

まず、EFのPropertyメソッドが取るラムダ式はFuncではなくExpressionです。
これは、実際にプロパティにアクセスする関数を渡している訳ではなく、Entryに登録したオブジェクトのどのプロパティを参照するのかのメタデータをラムダ式の形で入力できるようにしたものです。
EF側ではラムダ式の構造を解析することでユーザが指定するプロパティを特定しているのです。
よって、ラムダ式には単純な引数へのプロパティアクセス以外を記述することはできません。
質問への直接の回答としては、「この式中でリフレクションを使用することは不可能」です。
EFがなんのためにこんな面倒な設計をしているのかというと、ユーザが型安全にプロパティアクセスを記述できるようにするためです。Entryに渡したオブジェクトの型が分かっていれば、どのプロパティが存在するのかは静的に知ることができるので、それをコードに型安全に記述するためにExpressionを使用しているのです。

ところで。。
質問者さんのコードは、とっても、型の力が抜けてるので、プロパティアクセスにわざわざExpressionを取るオーバーロードを使用する理由がありません。
というわけでちょっと書いてみたのが以下です。

csharp

1static ValidationObject _validationObject; 2 3public static ICollection<DbValidationError> Validate(SeikouDbContext db, object value, string propertyName) 4{ 5 typeof(ValidationObject).GetProperty(propertyName).SetValue(_validationObject, value); 6 return db.Entry(_validationObject).Property(propertyName).GetValidationErrors(); 7}

これで動くかどうかは全く不明です。すみません。_validationObjectの情報が全く質問に書かれていなかったため、仮にValidationObjectクラスのオブジェクトであるとしました。
Propertyメソッドにはstringのみを取るオーバーロードが存在しており、プロパティ名から型情報を持たないDbPropertyEntryを手に入れることができるようです。
あとは、_validationObjectの対象プロパティにvalueをセットするためのリフレクションがあればいいです。
dynamicを使う必要がなくなったためvalueはobjectになります。
以上のやり方で、プロパティ名さえ正しく渡すことができれば、一つのメソッドで目的を達成できると思います。
_validationObjectの型が色々あるとかであれば、ジェネリックにするなりGetType()するなりで対応できると思います。

投稿2018/03/07 15:57

tamoto

総合スコア4105

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kikiinu

2018/03/08 03:31

回答ありがとうございます。 Property(n => n.Furigana)をProperty(propertyName)に直接プロパティ名を設定したら期待の動きをしました!!感動ものです。 ありがとうございます。 これでコード量がグッと減りましたし、変更にも強いものが出来上がりました!! ラムダ式にFuncとExpressionがあるの知りませんでした。 Funcだけだと思ってました。というか気にもしてなかったです。 最終的なコードを載せておきます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問