LINQプロバイダーの計算フィールド

こんにちは、Habr!



今日は、ほんの数時間で膝の上に最近書いた小さな図書館についてお話したいと思います。 このライブラリは、メソッドをλ表現に逆コンパイルできます。



これが必要な理由-カットの下で。



イントロ



実際には、LINQでは計算フィールドを使用する必要があります。たとえば、計算フィールドFullNameを持つEmployeeクラスがあります



class Employee { public string FullName { get { return FirstName + " " + LastName; } } public string LastName { get; set; } public string FirstName { get; set; } }
      
      





そして、顧客があなたのところに来て、従業員のフルネームで検索を追加する必要があると言います。 次のクエリを長く取って書くとは思わないでしょう。



 var employees = (from employee in db.Employees where (employee.FirstName + " " + employee.LastName) == "Test User" select employee).ToList();
      
      





はい、 FullNameのような単純なフィールドでこれを行うことができますが、フィールドがそれほど単純ではない場合はどうでしょうか? たとえば、私が参加したプロジェクトの1つからの計算フィールド。



 public class WayPoint { //       public virtual bool IsValid { get { return (Account == null) || (Role == null || Account.Role == Role) && (StructuralUnit == null || Account.State.StructuralUnit == StructuralUnit); } } }
      
      





これは難しいです。 それでは始めましょう。 このような問題を解決するには何が必要ですか?



NHibernateの<式>



NHibernateを使用する場合、このフィールドを数式としてマッピングできますが、このパスはリファクタリングにあまり適していません。さらに、<formula>はsqlのみをサポートします。異なるデータベースで使用する予定のアプリケーションを作成する場合は、特に注意してください。



NHibernateでのみサポートされています。



Microsoft.Linq.Translations



これを行うには、クラスとクエリを次のように書き換える必要があります。



 class Employee { private static readonly CompiledExpression<Employee,string> fullNameExpression = DefaultTranslationOf<Employee>.Property(e => e.FullName).Is(e => e.FirstName + " " + e.LastName); public string FullName { get { return fullNameExpression.Evaluate(this); } } public string LastName { get; set; } public string FirstName { get; set; } } var employees = (from employee in db.Employees where employee.FullName == "Test User" select employee).WithTranslations().ToList()
      
      





すべてが正常で、リクエストはきれいに見えますが、プロパティの宣言はひどいです。 さらに、Evaluateは実行時にλ式をコンパイルします。これは、私の意見では、計算フィールドを指定するのと同じくらいひどいものです。



そして最後に、私が作成したもの-DelegateDecompiler



デリゲート



必要なのは、 [Computed]属性で計算フィールドをマークし、 .Decompile()メソッドを使用してリクエストを変換することです



 class Employee { [Computed] public string FullName { get { return FirstName + " " + LastName; } } public string LastName { get; set; } public string FirstName { get; set; } } var employees = (from employee in db.Employees where employee.FullName == "Test User" select employee).Decompile().ToList()
      
      





私の意見ではエレガントに(あなたは自分を賞賛しません-誰も賞賛しません)



.Decompile()が呼び出されると、デコンパイラは[Computed]属性でマークされたすべてのプロパティとメソッドを見つけて展開します。 つまり リクエストは元の例のフォームに変換されます:



 var employees = (from employee in db.Employees where (employee.FirstName + " " + employee.LastName) == "Test User" select employee).ToList();
      
      





このライブラリは、Mono.Cecilの作成者であるJean-Baptiste Evainの Mono.Reflection( GitHubNuGet )を逆コンパイラとして使用します。 Mono.Cecil自体かさばるので使用されません。



PS:当然、計算フィールド内がLINQプロバイダーによってサポートされる必要があるという事実。

PPS:このアルファ版はリリースからはほど遠い-ご自身の責任で使用してください。



参照資料



GitHubソースコード

NuGetパッケージ



All Articles