Hi, Habr!
Today I want to tell you about a small library that I wrote recently on my knee in just a few hours. This library can decompile methods into their λ-representation.
Why this may be needed - under the cut.
')
Intro
In life it happens that in LINQ you need to use a calculated field, for example, we have the class
Employee with a calculated field
FullNameclass Employee { public string FullName { get { return FirstName + " " + LastName; } } public string LastName { get; set; } public string FirstName { get; set; } }
And here the customer comes to you and says that we need to add a search for the full name of the employee. You do not think long take and write the following request:
var employees = (from employee in db.Employees where (employee.FirstName + " " + employee.LastName) == "Test User" select employee).ToList();
Yes, with such a simple field, how can
FullName do this, but what if the field is not so simple? Here, for example, a calculated field from one of the projects in which I participated.
public class WayPoint { // public virtual bool IsValid { get { return (Account == null) || (Role == null || Account.Role == Role) && (StructuralUnit == null || Account.State.StructuralUnit == StructuralUnit); } } }
With this harder. So let's get started. What do we have to solve such problems?
<formula> in NHibernate
If you use NHibernate, then you can wipe this field as a formula, but this way is not very friendly to refactoring, besides <formula> only supports sql, and if you are writing an application that you plan to use with different databases, then you need be especially careful.
Only poddrezhivaetsya in NHibernate.
To do this, you need to rewrite our class and query as follows:
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()
Everything is good, the query looks beautiful, but the property declaration is just awful. In addition, Evaluate compiles the λ-expression at the time of execution, which, in my opinion, is no less terrible than the task of the calculated field.
And finally, we come to my creation - DelegateDecompiler
DelegateDecompiler
All that is needed is to mark the calculated fields with the
[Computed] attribute, and convert the request using the
.Decompile () method
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()
I think gracefully (do not praise yourself - no one will praise)
When calling
.Decompile (), the decompiler will find all the properties and methods marked with the
[Computed] attribute and expand them. Those. the request will be converted to the form from the original example:
var employees = (from employee in db.Employees where (employee.FirstName + " " + employee.LastName) == "Test User" select employee).ToList();
The library uses Mono.Reflection (
GitHub ,
NuGet ) from
Jean-Baptiste Evain , the creator of
Mono.Cecil, as a decompiler .
Mono.Cecil itself
is not used because of its bulkiness.
PS: Naturally, the fact that inside a calculated field must be supported by your LINQ provider.
PPS: This is an alpha version very far from release - use at your own risk.
Links
GitHub source codePackage in NuGet