【问题标题】:LINQ expression with generic class properties具有泛型类属性的 LINQ 表达式
【发布时间】:2013-09-21 08:21:32
【问题描述】:

我想将一个 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)' 方法,并且该方法无法转换为存储表达式。

【问题讨论】:

    标签: c# linq generics lambda


    【解决方案1】:

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

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

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

    现在您可以将您的实体声明为实现HasId&lt;int&gt;,例如,如果Id 的类型为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&lt;TId&gt;。这使您可以编写简化的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&lt;Func&lt;T, TId&gt;&gt; idSelector 参数,该表达式可以返回T 的给定实例的Id

    你可以这样调用方法:

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

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

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

    【讨论】:

    • 亲爱的先生,这个答案很棒!我还没有尝试过第二种方法,但第一种方法很有效。
    • 第二种方法不能按预期工作。错误:“idSelector”是一个变量,但用作方法。
    • 我问了另一个与第二种方法相关的问题。如果您有兴趣,请参阅stackoverflow.com/questions/18933314/…
    【解决方案2】:

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

    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)));
    }
    

    【讨论】:

    • 这行得通,但效率很低,因为所有对象都是从数据库中提取的,对吧?
    • 没错。但是由于您需要反射,LINQ to Entites 无法将您的反射代码转换为 SQL。我想你可以试试objects.Cast&lt;TId&gt;().Where(a =&gt; ids.Contains(a.Id))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多