LINQ to Entities无法识别方法'System.Object GetValue(...)'

18

我的问题是我需要在一个泛型类中查询一个属性的值。该属性已被标记为属性。

请参阅以下代码:

 var rowKeyProperty = EFUtil.GetClassPropertyForRowKey<T>();
 var tenantKeyProperty = EFUtil.GetClassPropertyForTenantKey<T>();

 var queryResult =
                objContext.CreateObjectSet<T>().Single(l => (((int) tenantKeyProperty.GetValue(l, null)) == tenantKey) &&
                                                            (((int)rowKeyProperty.GetValue(l, null)) == KeyValue));

rowKeyProperty和tenantKeyProperty的类型为System.Reflection.PropertyInfo。

我理解为什么会出现错误。当linq查询转换为SQL时,它无法理解property.GetValue。

然而,我完全不知道如何解决这个问题。有人有任何想法如何实现吗?谢谢。


4
要使用反射属性构建 EF 兼容的查询,唯一的方法是自己构建表达式树。 - Cory Nelson
你是如何确定需要获取哪些属性的? - Servy
该属性与属性相关联。我调用一个attribute.isdefined返回具有该属性为真的属性。 - Ryan Z
4个回答

26

您需要实际构建Expression对象来表示您想要模仿的表达式,本例中您要表示的表达式是:

l => l.SomeProperty == SomeValue

因此,您需要逐步构建该组件的每个部分,从创建参数、定义等式运算符、属性访问、常量值等开始。

public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
    PropertyInfo property, TValue value)
{
    var param = Expression.Parameter(typeof(TItem));
    var body = Expression.Equal(Expression.Property(param, property),
        Expression.Constant(value));
    return Expression.Lambda<Func<TItem, bool>>(body, param);
}

一旦您拥有了所有这些,就可以使用您所拥有的数据来调用它:

var queryResult = objContext.CreateObjectSet<T>()
    .Where(PropertyEquals<T, int>(tenantKeyProperty, tenantKey))
    .Where(PropertyEquals<T, int>(rowKeyProperty, KeyValue))
    .Single();

确认在SQL分析器中生成了正确的查询。谢谢Servy。 - Ryan Z
1
当参数为DateTime时,这会出现问题。 - muttley91
@muttley91,你遇到了什么问题? - Servy
当参数为DateTime类型时,执行Expression.Property(param, property)出现错误。类型“DataAccess.WorkOrder”未定义属性“System.DateTime LastUpdate”。 - Yusril Maulidan Raji
@YusrilMaulidanRaji,你的错误提示告诉你出了什么问题。你尝试获取的对象中没有该名称和类型的属性。 - Servy
显示剩余3条评论

2

附录在这里... 参考@Servy的答案,并基于主题,其中@TomBrothers提供了一个很好的答案,您可以使用相同的逻辑来创建一个StartsWith(或类似)函数:

public static Expression<Func<TItem, bool>> PropertyStartsWith<TItem>(PropertyInfo propertyInfo, string value)
{
    var param = Expression.Parameter(typeof(TItem));

    var m = Expression.MakeMemberAccess(param, propertyInfo);
    var c = Expression.Constant(value, typeof(string));
    var mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
    var body = Expression.Call(m, mi, c);

    return Expression.Lambda<Func<TItem, bool>>(body, param);
}

在这种情况下,它强制让value变成字符串。

1
Expression.Constant(value, typeof(TValue)) 中指定类型更加准确。
public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
        string property, TValue value)
    {
        var xParameter = Expression.Parameter(typeof(TItem));
        var body = Expression.Equal(Expression.Property(xParameter, property), Expression.Constant(value, typeof(TValue)));
        return Expression.Lambda<Func<TItem, bool>>(body, xParameter);
    }

或者像这样,检查属性。更改类型

public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
        string property, TValue value)
    {
        var xParameter = Expression.Parameter(typeof(TItem));
        var type = typeof(TItem).GetProperty(property).PropertyType;
        value = ChangeType<TValue>(value);
        BinaryExpression body = Expression.Equal(Expression.Property(xParameter, property), Expression.Constant(value, type));

        return Expression.Lambda<Func<TItem, bool>>(body, xParameter);
    }

我应该查找什么?我检查所有类引用到类,寻找“..ID”条目。某处我有一个类型为“int”和“int?”。
public class BudgetLimit : BaseRecord
{
    [Required]
    public int DepartmentID { get; set; } 
    public virtual Department Department { get; set;}

    public int? ProjectID { get; set; }
    public virtual Project Project { get; set; }
 }

-1

在 LINQ 语句后面添加 .AsEnableable。 例如:objectdata.AsEnumerable() 输入链接说明


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接