Как динамически создать дерево выражений для использования с анонимными типами - PullRequest
5 голосов
/ 30 марта 2020

Это мой первый пост здесь. Если я нарушил какие-либо рекомендации, пожалуйста, дайте мне знать, и я буду рад их исправить.

У меня есть следующие классы сущностей:

public class Book
{
public int BookID { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
}

И второй класс сущностей как таковой,

public class Library
{
public int ID  { get; set; } 
public Book Book { get; set; }
public int Count { get; set; }
}

У меня также есть эта функция для динамического генерирования лямбда-выражения на основе пользовательского ввода.

public static Expression<Func<T, bool>> GetLambdaExpression<T>(List<Operation> OperationList)
        {  
            ExpressionTree expressionTree = new ExpressionTree();
            Node Root = expressionTree.ConstructTree(OperationList);

            var Parameter = Expression.Parameter(typeof(T), "x");
            var Expression = CalculateExpression(Root);  //Returns an Expression Clause by And/Or every input in OperationList
            return Expression.Lambda<Func<T, bool>>(Expression, Parameter); //Finally creating Lambda
        }


Класс операций содержит сведения о типе операции, поле и значения. Он передается от клиента, который я использую для запросов к классам сущностей, путем сопоставления имен полей.

код работает, как и предполагалось, при использовании этого способа,

var OperationList = //Get from client
var LambdaExpression = GetLambdaExpression<Book>(OperationList);
var result = _BookContext.Books.Where(LambdaExpression);

ИЛИ

var OperationList = //Get from client
var LambdaExpression = GetLambdaExpression<Library>(OperationList);
var result = _LibraryContext.Library.Where(LambdaExpression);

Я пытаюсь объединить два класса сущностей, используя LINQ, но я не могу найти способ динамически создать лямбда-выражение для сгенерированного анонимного типа, который возвращается JOIN.

Мое объединение выглядит следующим образом:

 var result = from c in _BookContext.Books

              join d in _LibraryContext.Library

              on c.BookID equals d.ID

              select new { c , d };

Однако по понятным причинам это не сработает,

var OperationList = //Passed from client
var LambdaExpression = GetLambdaExpression<T>(OperationList);
result.Where(LambdaExpression); 

Передача 'object' или 'dynamici c 'GetLambdaExpression () не работает, потому что имена полей не определены заранее и выдает исключение.

Как я могу построить дерево выражений для анонимного типа.

Большое спасибо.

ОБНОВЛЕНИЕ

Мне удалось это исправить. Вот что я сделал:

Вместо того, чтобы сохранять результат объединения в тип Anonymous, я создал новый класс, в котором используются объекты классов Entity для выполнения объединения.

public class JoinResult
    {
        public Book Book { get; set; }
        public Library Library { get; set; }
    }

Выполните объединение и сохраните данные в JoinResult

var result = from c in _BookContext.Books

              join d in _LibraryContext.Library

              on c.BookID equals d.ID

              select new JoinResult{ Book = c , Library = d };

Наконец, вот хитрость для динамического создания лямбда-выражения для JoinResult.

Я создал параметр выражения для JoinResult, затем создал свойства выражения для свойств JoinResult.

Я использовал свойства выражений, созданные для использования в качестве параметров для передачи в новое свойство класса Entity. По сути, создание свойства в формате «x.Book.BookID».

Например, если я хочу выполнить EqualOperation на JoinResult. Вот как бы я это сделал.

public static IQueryable<T> PerformEqualOperation<T>(int Constant, int FieldName, Type Prop, IQueryable<T> resultOfJoin)
        {

            var Parameter = Expression.Parameter(typeof(T), "x"); //x
            PropertyInfo[] Properties = typeof(T).GetProperties(); //Get the properties of JoinResult

            string propertyname; 

            //Get the property name
            foreach(var property in Properties)
            {
               if(property.GetType() == Prop)
                  propertyname = property.Name;
            }

           //Creating a property that can be passed as a parameter to the property for Entity class.
           var expressionparameter = Expression.Property(Parameter, propertyname);  //x.Book
                var expressionproperty = Expression.Property(expressionparameter, FieldName);//x.Book.BookID
                var expressionclause = Expression.Equal(expressionproperty, Expression.Constant(Constant));//x.Book.BookID == Constant
var expressionlambda = Expression.Lambda<Func<T,bool>>(expressionclause, Parameter) 
                return resultOfJoin.Where(expressionlambda).AsQueryable();
        }

Надеюсь, это поможет

1 Ответ

1 голос
/ 30 марта 2020

А как насчет создания метода расширения? Например:

public static class QueryExpression
{
    public static IQueryable<T> WhereWithLambdaExpression<T>(
        this IQueryable<T> query, List<Operation> OperationList)
    {
        ExpressionTree expressionTree = new ExpressionTree();
        Node Root = expressionTree.ConstructTree(OperationList);

        var Parameter = Expression.Parameter(typeof(T), "x");
        //Returns an Expression Clause by And/Or every input in OperationList
        var Expression = CalculateExpression(Root);

        //Finally creating Lambda
        Expression<Func<T, bool>> predicate =
            Expression.Lambda<Func<T, bool>>(Expression, Parameter); 

        return query.Where(predicate);
    }
}

Затем

var query = joinResults.WhereWithLambdaExpression(OperationList);

Компилятор может вывести анонимный тип из IQueryable<T> и передать его как T в метод расширения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...