【发布时间】:2018-12-27 11:58:10
【问题描述】:
我的存储库返回从公共基类派生的实体
class BaseClass
{
public int Id { get; set; }
}
class MyClass : BaseClass
{
public string Name { get; set; }
}
class MyOtherClass : BaseClass
{
...
}
在这样的函数中:
IQueryable<T> GetEntities<T>() where T : BaseClass
我添加了一种方法,使用 Expression<Func<T,bool>> 将特定实体的其他过滤器注册为 lambda,如下所示:
RegisterFilter<MyClass>(t => t.Name == "Test" );
只要在类型参数中使用MyClass 调用GetEntities,就会应用它。
问题
如何在运行时动态创建一个表达式,将类型转换包装在过滤器周围?
在我的特定情况下,GetEntities 使用 BaseClass 作为类型参数和事件艰难调用 IQueryable<MyClass> 我知道需要应用 MyClass 的过滤器我没有想办法做到这一点:
IQueryable<BaseClass> src =
(new List<MyClass>
{
new MyClass { Id = 1, Name = "asdf" },
new MyClass { Id = 2, Name = "Test" }
})
.AsQueryable();
Expression<Func<MyClass, bool>> filter = o => o.Name == "Test";
// does not work (of course)
src.Where(filter);
尝试失败
显然我可以在调用 Where 之前将集合转换回来,但我在运行时尝试这样做没有奏效:
// HACK: don't look
var genericCast = typeof(Queryable).GetMethod("Cast").MakeGenericMethod(entityType);
var genericWhere = typeof(Queryable).GetMethods().Single(mi => mi.Name == "Where" && mi.GetParameters()[1].ParameterType.GenericTypeArguments[0].Name == "Func`2");
q = (IQueryable<T>)genericCast.Invoke(q, new object[] { genericWhere.Invoke(q, new object[] { filterExp }) });
System.InvalidOperationException: '不能对 ContainsGenericParameters 为 true 的类型或方法执行后期绑定操作。'
因为这也很丑陋,所以我尝试将过滤器包装成这样:
LambdaExpression filterExp = (LambdaExpression)filter;
var call = filterExp.Compile();
Expression<Func<T, bool>> wrap = o => (bool)call.DynamicInvoke(Convert.ChangeType(o, entityType));
这可行,但会阻止将过滤器生成到存储表达式中,因此它将在内存中完成,这也是不可取的。
我觉得这个问题的解决方案应该是微不足道的,但我似乎无法正确解决,因此非常感谢任何帮助。
【问题讨论】:
-
你为什么不能
Expression<Func<BaseClass, bool>> o => o is MyClass && (MyClass)o).Name == "Test";
标签: c# generics lambda iqueryable