具有generics类属性的LINQ表达式
我想将一个IQueryable和一组id传递给一个方法,该方法根据这些id过滤IQueryable。
由于id可以是long或int,因此应该通常解决。
我想出了以下内容:
public static IEnumerable GetModified(IQueryable 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)’方法,并且此方法无法转换为商店表达式。
exception是正常的,因为通过reflection获取属性显然无法转换为SQL。
我要尝试的一件事是创建一个公开接口,公开给定类型的Id
属性:
public interface HasId { T Id { get; set; } }
现在,您可以将实体声明为实现HasId
,例如,如果Id
的类型为int
。
下一步是修改你的方法,如下所示:
public static IEnumerable GetModified (IQueryable objects, TId[] ids) where T : class, HasId { return objects.Where(j => ids.Contains(j.Id)); }
请注意添加的通用限制: where T : class, HasId
。 这使您可以编写简化的j.Id
,它返回TId
值,而不是求助于reflection。
请注意,我没有运行或测试此代码; 这只是我在看到你的问题时得到的一个想法,我希望它有所帮助。
更新:
这是另一种可能的解决方案,它不要求您以任何方式声明接口或更改类:
public static IEnumerable GetModified (IQueryable objects, TId[] ids, Expression> idSelector) where T : class { return objects.Where(j => ids.Contains(idSelector(j))); }
我在这里做的是添加Expression
参数,这个表达式可以返回给定T
实例的Id
。
你会这样调用这个方法:
var modified = GetModified(dbObjects, yourIdArray, entity => entity.Id);
(只有第三个参数是新的;保留现在的其他参数)。
再次,我没有测试这是否有效甚至编译,因为我这里没有带VS的电脑:(。
entity framework不支持某些.NET方法,如GetValue()
因为它不会转换为SQL(这是实际执行到IQueryable
的代码。尝试在进行reflection之前调用ToList
获取CLR对象:
public static IEnumerable GetModified(IQueryable objects, TId[] ids) where T : class { return objects.ToList().Where(j => ids.Contains((TId)j.GetType().GetProperty("Id").GetValue(j))); }