Assuming you are targeting LINQ to Objects.
Actually Join
method requires 3 expressions (funcs) - outer key selector, inner key selector and result selector. Since you want to join two enumerables of the same type, outer and inner key selectors will be one and the same.
If the count of the properties is no more than 7, we can use Tuple<>
as a key. Here is how we can build selector dynamically:
static Func<T, object> CreateSelector<T>(IEnumerable<string> propertyNames)
{
var sourceType = typeof(T);
var parameter = Expression.Parameter(sourceType, "e");
var properties = propertyNames.Select(name => Expression.PropertyOrField(parameter, name)).ToArray();
var selector = Expression.Lambda<Func<T, object>>(
Expression.Call(typeof(Tuple), "Create", properties.Select(p => p.Type).ToArray(), properties),
parameter);
return selector.Compile();
}
Then we can create a helper method that is using it (put both methods in some top level public static class of your choice):
public static IEnumerable<Tuple<T, T>> Join<T>(this IEnumerable<T> left, IEnumerable<T> right, IEnumerable<string> propertyNames)
{
var keySelector = CreateSelector<T>(propertyNames);
return left.Join(right, keySelector, keySelector, Tuple.Create);
}
and now you can use something like this:
class Product
{
public string ProductName { get; set; }
public string ProductCode { get; set; }
}
List<Product> list1 = ...;
List<Product> list2 = ...;
var result = list1.Join(list2, new [] { "ProductName", "ProductCode" });