具有泛型类属性的LINQ表达式

时间:2013-09-21 08:21:32

标签: c# linq generics lambda

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

2 个答案:

答案 0 :(得分:6)

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

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

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

现在,您可以将实体声明为实施HasId<int>,例如,如果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<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的电脑:(。

答案 1 :(得分:3)

实体框架不支持某些.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)));
}