Computed fields for any LINQ provider
Hello, Habr!
Today I want to talk about a small library that I recently wrote on my knee in just a few hours. This library can decompile methods into their λ representation.
Why this might be needed - under the cut.
In life, it happens that in LINQ you need to use a computed field, for example, we have an Employee class with a computed field FullName
And here the customer comes to you and says that we need to add a search by the full name of the employee. You take a short time to think and write the following query:
Yes, with a field as simple as FullName , you can do this, but what if the field is not so simple? For example, a calculated field from one of the projects in which I participated.
This is harder. So let's get started. What do we have to solve such problems?
If you use NHibernate, you can map this field as a formula, but this path is not very refactoring friendly, and besidessupports only sql, and if you are writing an application that you plan to use with different databases, then you need to be especially careful.
Only supported in NHibernate.
To do this, we need to rewrite our class and query as follows:
Everything is fine, the request looks beautiful, but the declaration of the property is just awful. In addition, Evaluate compiles the λ-expression at the time of execution, which, in my opinion, is no less terrible than specifying a calculated field.
And finally, we come to my creation - DelegateDecompiler
All that is needed is to mark the calculated fields with the [Computed] attribute , and convert the request using the .Decompile () method
In my opinion it’s elegant (you won’t praise it yourself - no one will praise it)
When you call .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:
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 due to its bulkiness.
PS: Naturally, the fact that inside the calculated field should be supported by your LINQ provider.
PPS: This alpha version is very far from release - use at your own risk.
Source Code on GitHub
Package in NuGet
Today I want to talk about a small library that I recently wrote on my knee in just a few hours. This library can decompile methods into their λ representation.
Why this might be needed - under the cut.
Intro
In life, it happens that in LINQ you need to use a computed field, for example, we have an Employee class with a computed field FullName
class 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 by the full name of the employee. You take a short time to think and write the following query:
var employees = (from employee in db.Employees
where (employee.FirstName + " " + employee.LastName) == "Test User"
select employee).ToList();
Yes, with a field as simple as FullName , you can do this, but what if the field is not so simple? 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);
}
}
}
This is harder. So let's get started. What do we have to solve such problems?
at NHibernate
If you use NHibernate, you can map this field as a formula, but this path is not very refactoring friendly, and besides
Only supported in NHibernate.
Microsoft.Linq.Translations
To do this, we need to rewrite our class and query as follows:
class Employee
{
private static readonly CompiledExpression fullNameExpression
= DefaultTranslationOf.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 fine, the request looks beautiful, but the declaration of the property is just awful. In addition, Evaluate compiles the λ-expression at the time of execution, which, in my opinion, is no less terrible than specifying a 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()
In my opinion it’s elegant (you won’t praise it yourself - no one will praise it)
When you call .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 due to its bulkiness.
PS: Naturally, the fact that inside the calculated field should be supported by your LINQ provider.
PPS: This alpha version is very far from release - use at your own risk.
References
Source Code on GitHub
Package in NuGet