using System.Collections.Generic;
using System.Linq.Expressions;
var listOfItems = new List<(string Key1, int Key2, string Value)>
var keysToFind = new[]{ ("foo", 1), ("bar", 2) };
var foundItems = listOfItems.AsQueryable()
.WhereCompositeKeyContainedIn(
foundItems.Should().BeEquivalentTo(("foo", 1, "A"), ("bar", 2, "D"));
System.Console.WriteLine("Success!");
sealed class ParamReplacer : ExpressionVisitor
private readonly ParameterExpression _param;
internal ParamReplacer(ParameterExpression param)
protected override Expression VisitMember(MemberExpression node)
return node.Expression.NodeType == ExpressionType.Parameter
? Expression.MakeMemberAccess(_param, node.Member)
: base.VisitMember(node);
public static class MyExpressions
public static IQueryable<TEntity> WhereCompositeKeyContainedIn<TEntity, TKey1, TKey2>(
this IQueryable<TEntity> query,
Expression<Func<TEntity, TKey1>> key1Expression,
Expression<Func<TEntity, TKey2>> key2Expression,
IEnumerable<(TKey1 Key1, TKey2 Key2)> keysToFind)
var parameter = Expression.Parameter(typeof(TEntity), "i");
var replacer = new ParamReplacer(parameter);
var key1 = replacer.Visit(key1Expression.Body);
var key2 = replacer.Visit(key2Expression.Body);
var predicate = keysToFind
Expression.Equal(key1, Expression.Constant(key.Key1)),
Expression.Equal(key2, Expression.Constant(key.Key2))))
.Aggregate(Expression.OrElse);
var lambda = Expression.Lambda<Func<TEntity, bool>>(predicate, parameter);
query = query.Where(lambda);