我不习惯使用表达式函数,但是我的问题是: 我将属性名称作为字符串获取,然后需要将其转换为适当的表达式。
目前我正在做这样的事情:
if (string.Equals(propertyString, "customerNo", StringComparison.InvariantCultureIgnoreCase))
{
return _repo.DoSomething(x=>x.CustomerNo);
}
if (string.Equals(propertyString, "customerName", StringComparison.InvariantCultureIgnoreCase))
{
return _repo.DoSomething(x => x.CustomerName);
}
使用回购功能是这样的:
public IEnumerable<ICustomer> DoSomething(Expression<Func<IObjectWithProperties, object>> express)
{
//Do stuff
}
我想做的是像这样使用反射:
var type = typeof(IObjectWithProperties);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if(string.Equals(property.Name,propertyString,StringComparison.InvariantCultureIgnoreCase))
return _repo.DoSomething(x => x.PROPERTY);
}
但是我找不到从propertyinfo生成表达式func的方法
编辑:Mong Zhu的回答,我可以使用该属性创建一个表达式。
我需要此表达式的原因是,我试图在iqueryable中动态设置orderby。
public IEnumerable<Customer> List(Expression<Func<IObjectWithProperties, object>> sortColumn)
{
using (var context = _contextFactory.CreateReadOnly())
{
return context.Customers.OrderBy(sortColumn).ToList();
}
}
使用这样的答案:
public Customer Test(string sortColumn){
var type = typeof(IObjectWithProperties);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (string.Equals(property.Name, sortColumn, StringComparison.InvariantCultureIgnoreCase))
{
Expression<Func<IObjectWithProperties, object>> exp = u =>
(
u.GetType().InvokeMember(property.Name, BindingFlags.GetProperty, null, u, null)
);
return _customerRepository.List(exp);
}
}
}
我得到一个错误:
System.InvalidOperationException:从作用域”引用的类型为'IObjectWithProperties'的变量'u',但未定义
编辑:
客户返回类型继承了IObjectWithProperties:
public class Customer: IObjectWithProperties
{
//properties
}
答案 0 :(得分:1)
好吧,在深入研究之后,我在this answer中找到了一个适用于EF的可行解决方案。
必须稍作修改
private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
return Expression.Lambda<Func<T, object>>(property, parameter);
}
呼叫看起来像这样:
var type = typeof(IObjectWithProperties);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (string.Equals(property.Name, propertyString, StringComparison.InvariantCultureIgnoreCase))
{
var result = DoSomething(ToLambda<IObjectWithProperties>(property.Name));
}
}
我将假设Customer
是实现接口IObjectWithProperties
和现有数据库表的扩展的部分类。因此,您的orderby方法应如下所示:
public IEnumerable<Customer> DoSomething(Expression<Func<IObjectWithProperties, object>> sortColumn)
{
using (var context = _contextFactory.CreateReadOnly())
{
return context.Customers.OrderBy(sortColumn).Cast<Customer>().ToList();
}
}
您在这里需要做的重要事情是调用Compile()
,它将被转换为sql语句并发送到服务器进行查询。
由于您将接口用作Func
的输入参数,因此编译器
似乎无法推断出您的局部类实现了此接口。
因此,必须进行进一步的显式Cast<Customer>()
调用才能建立正确的返回类型。
我希望这是可以理解的,并且可以帮助您解决第二个问题
此解决方案还将OrderBy
子句转换为SQL。
免责声明:
不幸的是,它可以与string
属性一起使用,但是到目前为止,不适用于Int32
。
我仍在努力找出原因。
编辑:
与此同时,我在this answer的David Specht中找到了另一种解决方案
此扩展类确实可以用作复制粘贴,并且可以在任何一种类型上使用。这是您需要的重要代码:
public static class IQueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "OrderBy", propertyName, comparer);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer);
}
/// <summary>
/// Builds the Queryable functions using a TSource property name.
/// </summary>
public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName,
IComparer<object> comparer = null)
{
var param = Expression.Parameter(typeof(T), "x");
var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField);
return comparer != null
? (IOrderedQueryable<T>)query.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
methodName,
new[] { typeof(T), body.Type },
query.Expression,
Expression.Lambda(body, param),
Expression.Constant(comparer)
)
)
: (IOrderedQueryable<T>)query.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
methodName,
new[] { typeof(T), body.Type },
query.Expression,
Expression.Lambda(body, param)
)
);
}
}
您的订购方法将如下所示:
public IEnumerable<Customer> DoSomething(string propertyName)
{
using (var context = _contextFactory.CreateReadOnly())
{
return context.Customers.OrderBy(propertyName).ToList();
}
}