【发布时间】:2012-03-11 20:18:00
【问题描述】:
我有实现interface IX 的class X。我还有一个专门用于 X 的存储库类,它使用 lambda 表达式作为参数:
public interface IX
{
}
public class X : IX
{
....
}
public class XRepository : IRepository<X>
{
public IEnumerable<X> Filter(Func<X, bool> filterFunc)
{
...
}
}
我需要使存储库类与接口IX 一起工作,因此我将IRepository<IX> 添加到正在实现的接口中:
public class XRepository : IRepository<X>, IRepository<IX>
{
public IEnumerable<X> Filter(Func<X, bool> filterFunc)
{
...
}
public IEnumerable<IX> Filter(Func<IX, bool> filterFunc)
{
// I need to call the same filter method as above, but
// in order to do so I must convert the Func<IX, bool> to Func<X, bool>.
}
}
我必须将 Func<IX, bool> 转换为 Func<X, bool>,但由于代码是使用 .NET 3.5 用 C# 3.0 编写的,因此我无法从 4.0 中引入的类型协方差中受益。
一个简单的解决方案是使用Func<X, bool> newFunc = x => filterFunc(x);,其中filterFunc 的类型为Func<IX, bool>。这将编译并且人们可能期望它运行良好,但我认为它不会。问题是我使用 3rd 方框架来实现过滤器,即 FluentNhibernate。我知道它使用表达式树将传递到 lambda 表达式成员访问条件(如 x => x.Name == "John")中,以构建原生 SQL 查询(如 WHERE Name = 'John')。上面的解决方案会产生一个Func<X, bool> 不是这样的表达,我担心它会翻译失败。所以我需要创建相同的 lambda 表达式但具有兼容的类型。知道 X 实现了 IX,很明显 Func<IX, bool> 中的任何代码都适用于 X 类型的对象。但是,对我来说并不明显,我该如何执行这种转换。
我认为这可以使用表达式树来完成。我也担心我的表现会受到很大影响。即使我决定为我的场景提供另一种解决方案,我仍然会欣赏将一个 lambda 转换为类似的另一个的建议方法。
编辑:
为了更清楚地了解我遇到的问题,我编写了以下测试,模拟了我所面临的现实场景:
Func<IX, bool> filter = y => y.Name == "John";
Func<X, bool> compatibleFilter = y => filter(y);
...
// Inside the Filter(Func<X, bool> filter method)
using(var session = nhibernateSessionFactory.OpenSession())
{
IEnumerable<X> xx = session.Query<X>().Where(z => compatibleFilter(z)).ToList();
}
所以,在 ToList() 方法中,我收到以下异常
Unable to cast object of type 'NHibernate.Hql.Ast.HqlParameter' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'.
这证实了我的假设,即 Flunet NHiberante 无法正确处理 compatibleFilter 参数。
所以我想要一种将 Func 转换为 Func 的方法,或者按照 John Skeet 的建议,将 Expression<Func<IX, bool>> 转换为具有相同主体 (y => y.Name = "John") 的 Expression<Func<X, bool>>。
编辑 2:
我终于做到了!正确的做法是不要使用Func<X, bool>,而是使用Expression<Func<X, bool>>。
Expression<Func<IX, bool>> filter = y => y.Name == "John Skeet";
Expression<Func<X, bool>> compatibleFilter = Expression.Lambda<Func<X, bool>>(
filter.Body,
filter.Parameters);
这会产生正确的 SQL 查询。IX, bool
【问题讨论】:
-
你的意思是 .NET 3.5 吗?没有 C# 3.5 这样的东西。
-
是的,我的意思是 .NET 3.5,但我在某些地方看到过 C# 3.0,我认为我可以将它应用于 3.5。我纠正了自己
-
我不知道这种差异,感谢您的链接。从现在开始,我将尝试使用正确的术语
标签: c# nhibernate .net-3.5 fluent-nhibernate