我想在多个列上实现过滤器,但我不想写 为每个列一个新的查询。所以我实现了一个GetDistinctProperty函数,如下所示:
public ActionResult GetDistinctProperty(string propertyName)
{
var selector = CreateExpression<TDomain>(propertyName);
var query = this.InventoryService.GetAll(Deal);
var results = query.Select(selector).Distinct().ToList();
return Json(results, JsonRequestBehavior.AllowGet);
}
private static Expression<Func<T, object>> CreateExpression<T>(string propertyName)
{
// Specify the type we are accessing the member from
var param = Expression.Parameter(typeof(T), "x");
Expression body = param;
// Loop through members in specified property name
foreach (var member in propertyName.Split('.'))
{
// Access each individual property
body = Expression.Property(body, member);
}
var conversion = Expression.Convert(body, typeof(object));
// Create a lambda of this MemberExpression
return Expression.Lambda<Func<T, object>>(conversion, param);
}
我们以拥有propertyName SiteIdentifier为例。
选择器将我作为值
{x => Convert(x.SiteIdentifier)}
当我想看到结果时,它会给我以下错误:
Unable to cast the type 'System.String' to type 'System.Object'.
LINQ to Entities only supports casting EDM primitive or enumeration types.
当我尝试选择如下:
var results = query.Select(x=>x.SiteIdentifier).Distinct().ToList();
它有效。
任何想法?
答案 0 :(得分:0)
在静态类型语言中使用Linq时,没有理由将属性名称作为字符串传递。泛型和代表(Func)被介绍使这种逻辑过时了。
您只需传递表达式而不是传递属性名称:
"rooms": {
"$roomId": {
".write": "auth != null && auth.uid.contains($roomId)"
}
用法:
public ActionResult GetDistinctProperty(Expression<Func<TDomain, TProp> selector)
{
var query = this.InventoryService.GetAll(Deal);
var results = query.Select(selector).Distinct().ToList();
return Json(results, JsonRequestBehavior.AllowGet);
}
答案 1 :(得分:0)
问题在于虽然IQueryable<T>
接口是协变的,但值类型不支持协方差,因此IQueryable<int>
不能被视为IQueryable<object>
。另一方面,EF不喜欢将值类型转换为object
。
因此,为了使其工作,您需要求助于非通用IQueryable
接口。不幸的是,几乎所有Queryable
扩展方法都围绕IQueryable<T>
构建,因此您必须手动编写相应的调用。
例如,为了按名称(路径)选择属性,你需要这样的东西:
public static partial class QueryableExtensions()
{
public static IQueryable SelectProperty(this IQueryable source, string path)
{
var parameter = Expression.Parameter(source.ElementType, "x");
var property = path.Split(',')
.Aggregate((Expression)parameter, Expression.PropertyOrField);
var selector = Expression.Lambda(property, parameter);
var selectCall = Expression.Call(
typeof(Queryable), "Select", new[] { parameter.Type, property.Type },
source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery(selectCall);
}
}
但是,您需要一个适用于Distinct
的{{1}}方法:
IQueryable
现在,您已拥有实施相关方法的所有必要部分。但是还有另一个重要的细节。为了能够创建public static partial class QueryableExtensions()
{
public static IQueryable Distinct(this IQueryable source)
{
var distinctCall = Expression.Call(
typeof(Queryable), "Distinct", new[] { source.ElementType },
source.Expression);
return source.Provider.CreateQuery(distinctCall);
}
}
,您需要致电List<object>
。但是如果使用Cast<object>
扩展方法,您将从EF获得相同的不支持的异常。因此,您需要明确调用IQueryable.Cast
:
IEnumerable.Cast