C# 在嵌套对象上的IQueryable上动态创建lambda搜索
我试图在嵌套对象上构建一个动态搜索,稍后将发送到EF和SQLServer。到目前为止,我能够搜索第一个对象的所有属性。下面是一个非常简化的版本: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
公共类用户
{
公共字符串名称{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)
{
表达式=λ;
}
其他的
{
表达式=表达式。或(λ);
}
}
}