use*_*738 5 c# linq linq-to-entities entity-framework
尝试将属性值检索为对象而不是各自的类型时,我遇到了一些麻烦.以下代码抛出此异常:
Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
Run Code Online (Sandbox Code Playgroud)
选择字符串时此代码可以正常工作,但选择DateTimes,Integers或Nullable类型时则不行.
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedOn { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
using (var ctx = new MyContext())
{
// Property selector: select DateTime as Object
Expression<Func<Customer, object>> selector = cust => cust.CreatedOn;
// Get set to query
IQueryable<Customer> customers = ctx.Set<Customer>();
// Apply selector to set. This throws:
// 'Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.'
IList<object> customerNames = customers.Select(selector).Distinct().ToList();
}
}
}
public class MyContext : DbContext
{
}
Run Code Online (Sandbox Code Playgroud)
最终目标是通用过滤,以从任何对象的属性中选择不同的值.
我知道您希望使用内联Expression声明以方便的方式选择属性(无需解析表示属性路径的点分隔字符串并使用反射)。然而,这样做需要Expression显式声明,并且我们必须使用显式类型。不幸的是,该object类型不能用作表达式的返回类型,因为稍后它无法转换为数据库中支持的类型之一。
我认为这里有一个解决方法。我们的想法是,我们将把 转换Expression<T,object>为另一个Expression<T,returnType>,其中returnType是属性的实际返回类型(由selector)返回。然而,总是要求在设计时应该知道明确的含义Select类型。所以这是不可能的。我们没有办法直接打电话。相反,我们必须使用反射来调用. 返回结果预计为一个,然后您可以调用该结果来获取您想要的对象列表。Expression<T,returnType>returnTypeSelectSelectIEnumerable<object>ToList()
现在您可以使用此扩展方法IQueryable<T>:
public static class QExtension
{
public static IEnumerable<object> Select<T>(this IQueryable<T> source,
Expression<Func<T, object>> exp) where T : class
{
var u = exp.Body as UnaryExpression;
if(u == null) throw new ArgumentException("exp Body should be a UnaryExpression.");
//convert the Func<T,object> to Func<T, actualReturnType>
var funcType = typeof(Func<,>).MakeGenericType(source.ElementType, u.Operand.Type);
//except the funcType, the new converted lambda expression
//is almost the same with the input lambda expression.
var le = Expression.Lambda(funcType, u.Operand, exp.Parameters);
//try getting the Select method of the static class Queryable.
var sl = Expression.Call(typeof(Queryable), "Select",
new[] { source.ElementType, u.Operand.Type },
Expression.Constant(source), le).Method;
//finally invoke the Select method and get the result
//in which each element type should be the return property type
//(returned by selector)
return ((IEnumerable)sl.Invoke(null, new object[] { source, le })).Cast<object>();
}
}
Run Code Online (Sandbox Code Playgroud)
用法:(与您的代码完全相同)
Expression<Func<Customer, object>> selector = cust => cust.CreatedOn;
IQueryable<Customer> customers = ctx.Set<Customer>();
IList<object> customerNames = customers.Select(selector).Distinct().ToList();
Run Code Online (Sandbox Code Playgroud)
起初我尝试访问 theexp.Body.Type并认为它是内部表达式的实际返回类型。然而,不知怎的,它总是System.Object除了特殊情况string(当属性访问的返回类型是string)。这意味着有关内部表达式的实际返回类型的信息完全丢失(或至少非常小心地隐藏)。这种设计相当奇怪,完全令人无法接受。我不明白他们为什么这样做。有关表达式实际返回类型的信息应该很容易访问。
| 归档时间: |
|
| 查看次数: |
1092 次 |
| 最近记录: |