I have class X
that implements an interface IX
. I also have a repository class dedicated for X, which uses lambda expresions as parameters:
public interface IX
{
}
public class X : IX
{
....
}
public class XRepository : IRepository<X>
{
public IEnumerable<X> Filter(Func<X, bool> filterFunc)
{
...
}
}
I need to make the repository class work with the interface IX
, therefore I add IRepository<IX>
to the interfaces being implemented:
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>.
}
}
I must convert the Func<IX, bool>
to Func<X, bool>
, but since the code is written in C# 3.0 using .NET 3.5, I cannot benefit from Type covariance, which was introduced in 4.0.
A simple solution could be to use Func<X, bool> newFunc = x => filterFunc(x);
, where filterFunc
is of type Func<IX, bool>
. This would compile and one might expect it to run fine, but I assume it will not. The problem is that I am using 3rd party framework for the filter implementation, namely FluentNhibernate. I know it uses expression trees to strip the passed into the lambda expression member access condition (like x => x.Name == "John"
) in order to build native SQL query (like WHERE Name = 'John'
). The above solution would produce a Func<X, bool>
that is not such expression and I fear it will fail to translate. So I need to create the same lambda expression but with the compatible type. Knowing that X implements IX, it is obvious that any code inside a Func<IX, bool>
will work for objects of type X
. It is not obvious for me, however, how can I perform this conversion.
I assume this can be done using expression trees. I also fear my performance will suffer greatly. Even if I decide to have another solution to my scenario, I will still appreciate the suggested way to translate one lambda into a similar another.
Edit:
To clarify more about the issue I am experiencing, I wrote the following test, simulating the real-life scenarion I am facing:
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();
}
so, at the ToList()
method I receive the following exception
Unable to cast object of type 'NHibernate.Hql.Ast.HqlParameter' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'.
This confirms my assumption that Flunet NHiberante cannot correctly handle the compatibleFilter argument.
So what I want is a way to convert the Func to Func or as suggested by John Skeet, an Expression<Func<IX, bool>>
to an Expression<Func<X, bool>>
which have the same body (y => y.Name = "John"
).
Edit 2:
Finally I made it happen! The correct way is not to use Func<X, bool>
, but 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);
This produces the correct SQL query.IX, bool