C# 在嵌套对象上的IQueryable上动态创建lambda搜索

C# 在嵌套对象上的IQueryable上动态创建lambda搜索,c#,entity-framework,reflection,lambda,C#,Entity Framework,Reflection,Lambda,我试图在嵌套对象上构建一个动态搜索,稍后将发送到EF和SQLServer。到目前为止,我能够搜索第一个对象的所有属性。下面是一个非常简化的版本: 公共类用户 { 公共字符串名称{get;set;} 公共广播地址{get;set;} } 公共课堂演讲 { 公共字符串City{get;set;} } 公共类MyClass其中tenty:类{ 公共IQueryable applySearch(IQueryable OriginallList、string propName、string valueTo

我试图在嵌套对象上构建一个动态搜索,稍后将发送到EF和SQLServer。到目前为止,我能够搜索第一个对象的所有属性。下面是一个非常简化的版本:

公共类用户
{
公共字符串名称{get;set;}
公共广播地址{get;set;}
}
公共课堂演讲
{
公共字符串City{get;set;}
}
公共类MyClass其中tenty:类{
公共IQueryable applySearch(IQueryable OriginallList、string propName、string valueToSearch){
ParameterExpression ParameterExpression=表达式参数(typeof(tenty),“p”);
PropertyInfo PropertyInfo=typeof(tenty).GetProperty(propName);
MemberExpression member=Expression.MakeMemberAccess(parameterExpression,propertyInfo);
lambda=Expression.lambda(Expression.Equal(成员,Expression.Constant(valueToSearch)),parameterExpression);
返回原始列表。其中(表达式);
}
}
propName=“Name”
一切正常时,但当
propName=“Address.City”
时,
propertyInfo
为空,我在
成员
分配行上收到此错误:

System.ArgumentNullException:值不能为null

我能够使用以下解决方案获得嵌套属性的
propertyInfo

PropertyInfo PropertyInfo=GetPropertyRecursive(typeof(tenty),propName);
...
私有属性信息GetPropertyRecursive(类型baseType,字符串propertyName)
{
string[]parts=propertyName.Split('.');
返回(零件长度>1)
?GetPropertyRecursive(baseType.GetProperty(parts[0])。PropertyType,parts.Skip(1)。聚合((a,i)=>a+“+i))
:baseType.GetProperty(propertyName);
}
但是,我在
成员
分配中得到了这个错误:

System.ArgumentException:未为类型“User”定义属性“System.String City”

这应该指向
地址
而不是
用户
,但我不知道我在这里是否正确,我的意思是,我应该现在更改
参数表达式


如何对嵌套对象进行动态搜索,以便将其转换为lambda表达式并稍后发送到SQL?

可能值得一看Linqkit的谓词生成器

我还要看一看实体SQL

您可能正在用正在编写的代码重新发明轮子


另外,我应该就SQLServer计划缓存发表评论,除非您别无选择,否则我不会动态构建查询。最好创建一个查询来处理SQL Server可以缓存计划的所有情况,如果每次执行查询时SQL Server的计划缓存中没有命中任何计划,则查询的运行速度会慢得多

提前警告-我不是构建表达式,只是检查它的结构

当我需要动态创建表达式时,我发现检查表达式并复制其结构非常有用:

Expression<Func<User, string>> getCity = user => user.Address.City;
getCity.Body
是一个成员访问-它访问表达式
user.Address
的成员
City
。从技术上讲,这是一个内部类,所以我们甚至不能对其进行强制转换,但这没关系。
最后,让我们看一下内心的表达:

((MemberExpression)getCity.Body).Expression
{user.Address}
    CanReduce: false
    DebugView: "$user.Address"
    Expression: {user}
    Member: {ConsoleApplication1.Address Address}
    NodeType: MemberAccess
    Type: {Name = "Address" FullName = "ConsoleApplication1.Address"}
var addressProperty = typeof (User).GetProperty("Address");
var cityProperty = typeof(Address).GetProperty("City");
var userParameter = Expression.Parameter(typeof (User), "user");
var getCityFromUserParameter = Expression.Property(Expression.Property(userParameter, addressProperty), cityProperty);
var lambdaGetCity = Expression.Lambda<Func<User, string>>(getCityFromUserParameter, userParameter);
那只是
user.Address

现在我们可以构建一个相同的表达式:

((MemberExpression)getCity.Body).Expression
{user.Address}
    CanReduce: false
    DebugView: "$user.Address"
    Expression: {user}
    Member: {ConsoleApplication1.Address Address}
    NodeType: MemberAccess
    Type: {Name = "Address" FullName = "ConsoleApplication1.Address"}
var addressProperty = typeof (User).GetProperty("Address");
var cityProperty = typeof(Address).GetProperty("City");
var userParameter = Expression.Parameter(typeof (User), "user");
var getCityFromUserParameter = Expression.Property(Expression.Property(userParameter, addressProperty), cityProperty);
var lambdaGetCity = Expression.Lambda<Func<User, string>>(getCityFromUserParameter, userParameter);
var addressProperty=typeof(User).GetProperty(“地址”);
var cityProperty=typeof(Address).GetProperty(“城市”);
var userParameter=Expression.Parameter(typeof(User),“User”);
var getCityFromUserParameter=Expression.Property(Expression.Property(userParameter,addressProperty),cityProperty);
var lambdaGetCity=Expression.Lambda(getCityFromUserParameter,userParameter);
Expression.MakeMemberAccess
也可以工作,而不是
Expression.Property


显然,您需要在循环中更动态地构建表达式,但结构是相同的。

在Kobi的建议和大量的尝试和错误之后,我终于实现了这一点。这使用了。这是:

公共类MyClass其中tenty:class
{
公共IQueryable ApplySearch(IQueryable原始列表,字符串值搜索,字符串[]列搜索)
{
表达式=null;
foreach(columnsToSearch中的变量propName)
{
表达式lambda=null;
ParameterExpression ParameterExpression=表达式参数(typeof(tenty),“p”);
字符串[]nestedProperties=propName.Split('.');
表达式成员=参数表达式;
foreach(nestedProperties中的字符串属性)
{
member=Expression.PropertyOrField(member,prop);
}
var canConvert=CanConvertToType(valueToSearch,member.Type.FullName);
如果(canConvert)
{
var value=ConvertToType(valueToSearch,member.Type.FullName);
if(member.Type.Name==“String”)
{
恒常压力常数=表达式常数(值);
MethodInfo mi=typeof(string).GetMethod(“StartsWith”,新类型[]{typeof(string)});
表达式调用=表达式调用(成员,mi,常量);
lambda=Expression.lambda(调用,参数Expression);
}
其他的
{
lambda=Expression.lambda(Expression.Equal(成员,Expression.Constant(值)),parameterExpression);
}
}
如果(λ!=null)
{
if(表达式==null)
{
表达式=λ;
}
其他的
{
表达式=表达式。或(λ);
}
}
}