4

我想将一个 IQueryable 和一个 id 数组传递给一个基于这些 id 过滤 IQueryable 的方法。

由于 id 可以是 long 或 int,因此应该通用解决。

我想出了以下内容:

public static IEnumerable<T> GetModified<TId, T>(IQueryable<T> objects, TId[] ids) where T : class
{
        return objects.Where(j => ids.Contains((TId)j.GetType().GetProperty("Id").GetValue(j)));
}

不幸的是,我遇到了例外:

LINQ to Entities 无法识别方法“System.Object GetValue(System.Object)”方法,并且该方法无法转换为存储表达式。

4

2 回答 2

6

例外是正常的,因为通过反射获取属性显然无法转换为 SQL。

我会尝试的一件事是创建一个通用接口,该接口公开Id给定类型的属性:

public interface HasId<T> {
    T Id { get; set; }
}

现在您可以将您的实体声明为 implementation HasId<int>,例如,如果Id是 type int

下一步是修改您的方法,如下所示:

public static IEnumerable<T> GetModified<TId, T>
    (IQueryable<T> objects, TId[] ids) where T : class, HasId<TId>
{
    return objects.Where(j => ids.Contains(j.Id));
}

请注意添加的通用限制:where T : class, HasId<TId>. 这使您能够编写j.Id返回TId值的简化的 ,而不是诉诸反射。

请注意,我没有运行或测试过这段代码;这只是我看到您的问题时得到的一个想法,希望对您有所帮助。

更新:

这是另一种可能的解决方案,它不需要您声明接口或以任何方式更改您的类:

public static IEnumerable<T> GetModified<TId, T>
    (IQueryable<T> objects, TId[] ids, Expression<Func<T, TId>> idSelector) 
    where T : class
{
    return objects.Where(j => ids.Contains(idSelector(j)));
}

我在这里所做的是添加Expression<Func<T, TId>> idSelector参数,一个可以返回Id给定实例的表达式T

你会这样调用方法:

var modified = GetModified(dbObjects, yourIdArray, entity => entity.Id);

(只有第三个参数是新的;保持其他参数不变)。

同样,我还没有测试这是否有效甚至可以编译,因为我这里没有带 VS 的计算机 :(。

于 2013-09-21T08:32:44.783 回答
3

实体框架不支持某些 .NET 方法,例如GetValue()因为它不转换为 SQL(这是实际执行到 .NET 的代码。在进行反射之前IQueryable尝试调用以获取 CLR 对象:ToList

public static IEnumerable<T> GetModified<TId, T>(IQueryable<T> objects, TId[] ids) where T : class
{
    return objects.ToList().Where(j => ids.Contains((TId)j.GetType().GetProperty("Id").GetValue(j)));
}
于 2013-09-21T08:27:25.040 回答