【发布时间】:2020-03-30 08:48:25
【问题描述】:
这是我在这里的第一篇文章。如果我违反了任何准则,请告诉我,我很乐意纠正它们。
我有以下实体类:
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; }
}
我还有这个函数可以根据用户输入动态生成 lambda 表达式。
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
}
Operation 类包含有关操作类型、字段和值的详细信息。它从客户端传递过来,我通过映射字段名称对实体类使用查询。
以这种方式使用时,代码会按预期工作,
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 返回的生成的匿名类型动态创建 Lambda 表达式。
我的加入看起来像,
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' 或 'dynamic' 传递给 GetLambdaExpression() 不起作用,因为字段名称未预定义并且会引发异常。
如何为匿名类型构建表达式树。
非常感谢。
更新
我设法修复它。这是我所做的:
我没有将连接的结果存储到匿名类型中,而是创建了一个新类,其中包含用于执行连接的实体类的对象。
public class JoinResult
{
public Book Book { get; set; }
public Library Library { get; set; }
}
执行join并将数据存入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 动态创建 Lambda 表达式的技巧。
我为 JoinResult 创建了一个表达式参数,然后为 JoinResult 的属性创建了表达式属性。
我使用创建的表达式属性用作要传递给实体类的新属性的参数。本质上,以“x.Book.BookID”的格式创建一个属性。
例如,如果我想对 JoinResult 执行 EqualOperation。这是我将如何做到的。
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();
}
希望对你有帮助
【问题讨论】:
-
我认为在这里使用匿名类型不一定是个好主意,但是您可以通过将
GetLambdaExpression设为IQueryable<T>的扩展方法来伪造它。 -
有一些方法可以创建匿名类型的 lambda 表达式并传递给
Enumerable.Where,但您将无法在代码中将返回结果用作IEnumerable<anonymous type>。相反,您将获得IEnumerable<object>。如果您需要访问属性,可以尝试使用ValueTuple,您现有的代码应该可以工作。 -
这是您的第一个问题 - 尊重。您的操作必须包含有关所请求类型的任何信息。也许您可以将您的 linq 更改为
from c in _BookContext.Books.Where(GetLambdaExpression<Book>(OperationList)) join d in _LibraryContext.Library.Where(GetLambdaExpression<Library>(OperationList)) on ...我还没有测试过 - 这只是一个提示。仅当您的GetLambdaExpression-method 可以提取所请求类型的有趣部分时,这才有效。 -
@weichch 我不太确定,但 OP 正在使用实体框架,据我所知它不支持
ValueTuple。 -
@SebastianSchumann 谢谢!我考虑过分离书本和图书馆的查询并单独构建表达式,但如果我有一个像这样的表达式,我认为分离查询没有帮助:(Book.ID == 'x ' && 库.ID== 'y') || (Book.Author == "author" || Library.Book == "Book")我不确定如何创建一个条件,将 "Library.ID=='y'" 输入为 && 与 Book。 ID=='x'。即使 Library.ID 条件为假,单独构建它们也会返回 Book.ID。有什么想法吗?
标签: c# entity-framework linq lambda expression-trees