Let A
be a class whith some property Hello
. I would like to filter collections of A
instances by this property in many places. Thus I'd make somewhere a static member of type Expression<Func<A, bool>>
which denotes the filtering predicate and I use it in all places where I do a filtering. (This predicate would be transformed by an ORM to some concrete DB-specific expression.)
Next. There exists a class B
with property of type A
. I would like to filter collection of B
instances by the same logic as was used in the first case (by property Hello
of class A
).
Question. What is the most correct way to implement this and reduce code duplication?
My suggestion. Add three things: 1) interface IWithA
with property of type A
, 2) class WithA<T>
implementing this interface IWithA
and providing property of type T
, 3) some static property of type Expression<Func<IWithA, bool>>
implementing the filtering logic. Demo code is following.
public static void Main() {
var listOfAs = new List<A>().AsQueryable();
var query0 = listOfAs
.Select(a => new WithA<A> {
A = a,
Smth = a,
})
.Where(Filter);
var listOfBs = new List<B>().AsQueryable();
var query1 = listOfBs
.Select(b => new WithA<B> {
A = b.A,
Smth = b,
})
.Where(Filter);
}
private class A {
public int Hello { get; set; }
}
private class B {
public A A { get; set; }
}
private interface IWithA {
A A { get; set; }
}
private class WithA<T> : IWithA {
public A A { get; set; }
public T Smth { get; set; }
}
private static readonly Expression<Func<IWithA, bool>> Filter = a => a.A.Hello > 0;
The problem with this approach: 1) one must always make Select(x => new WithA<X> { ... })
, 2) the ORM may not support this.
About the answer. I am satisfied with the accepted answer (by Ivan Stoev). I think it is the best possible approach. Also it is helpful to look at the suggestion from Mihail Stancescu (see it in comments to the question). Still I do not understand the answer from user853710; may be it is useful also.
I would create and use a helper function that converts the original Expression<A, bool>
to Expression<B, bool>
using the System.Linq.Expressions
like this
public static class ExpressionUtils
{
public static Expression<Func<TTarget, bool>> ConvertTo<TSource, TTarget>(this Expression<Func<TSource, bool>> source, Expression<Func<TTarget, TSource>> sourceSelector)
{
var body = new ParameterExpressionReplacer { source = source.Parameters[0], target = sourceSelector.Body }.Visit(source.Body);
var lambda = Expression.Lambda<Func<TTarget, bool>>(body, sourceSelector.Parameters);
return lambda;
}
class ParameterExpressionReplacer : ExpressionVisitor
{
public ParameterExpression source;
public Expression target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == source ? target : base.VisitParameter(node);
}
}
}
Sample usage
Expression<Func<A, bool>> filterA = item => item.Hello == 2; // The original logic
var filterB = filterA.ConvertTo((B b) => b.A);
This approach doesn't require any changes to your entity model. Of course you can cache the filters in a static properties of the respective classes if you wish, but the principle is still write the logic in one place and then just use convert in other places.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With