- PVSM.RU - https://www.pvsm.ru -
Привет!
Сегодня я хочу рассказать, о маленькой библиотеки, которую я написал недавно на коленке всего за несколько часов. Эта библиотека может декомпилировать методы в их λ-представление.
Зачем это может понадобиться — под катом.
В жизни случается, что в LINQ нужно использовать вычисляемое поле, к примеру у нас есть класс Employee с вычисляемым полем FullName
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 так можно поступить, но что делать, если поле не такое простое? Вот к примеру, вычисляемое поле из одного из проектов, в котором я учавствовал.
public class WayPoint
{
// все остальное опущено в целях наглядности
public virtual bool IsValid
{
get
{
return (Account == null) ||
(Role == null || Account.Role == Role) &&
(StructuralUnit == null || Account.State.StructuralUnit == StructuralUnit);
}
}
}
С этим сложнее. Итак, приступим. Что же у нас есть для решения таких задач?
Если вы используете NHibernate, то можете замапить данное поле как формулу, но этот путь не очень дружелюбен к рефакторингу, к тому же <formula> поддерживает только sql, и если вы пишете приложение, которое планируется использовать с разными базами данных, то здесь вам нужно быть особенно осторожными.
Поддреживается только в NHibernate.
Для этого необходимо переписать наш класс и запрос следующим образом:
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 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.Reflection (GitHub [2], NuGet [3]) от Jean-Baptiste Evain [4] — создателя Mono.Cecil. Сама Mono.Cecil не используется из-за ее громоздкости.
PS: Естественно, то что внутри вычисляемого поля должно поддерживаться вашим LINQ-провайдером.
PPS: Это альфа-версия очень далекая от релиза — используйте на свой страх и риск.
Исходный код на GitHub [5]
Пакет в NuGet [6]
Автор: alexanderzaytsev
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/17716
Ссылки в тексте:
[1] Microsoft.Linq.Translations: http://damieng.com/blog/2009/06/24/client-side-properties-and-any-remote-linq-provider
[2] GitHub: https://github.com/jbevain/mono.reflection/
[3] NuGet: https://nuget.org/packages/Mono.Reflection
[4] Jean-Baptiste Evain: http://evain.net
[5] Исходный код на GitHub: https://github.com/hazzik/DelegateDecompiler
[6] Пакет в NuGet: https://nuget.org/packages/DelegateDecompiler
Нажмите здесь для печати.