Reputation: 846
I'm trying to make an extension for Linq that I can use with optional joins. I think I'm close, but I'm missing something. Can you spot it?
public static IQueryable<T> OptionalJoin<T>(this IQueryable<T> source, bool condition,
Expression<IEnumerable<T>> TInner,
Expression<Func<T, object>> outerKeySelector,
Expression<Func<T, object>> innerKeySelector,
Expression<Func<IQueryable<T>, IEnumerable<T>, object>> resultSelector)
{
return condition ? source.Join(TInner, outerKeySelector, innerKeySelector, resultSelector) : source;
}
Upvotes: 1
Views: 737
Reputation: 117027
I think you'll need something like this:
public static IQueryable<TResult> OptionalJoin<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,
bool condition,
IEnumerable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<TOuter, TInner, TResult>> joinResultSelector,
Expression<Func<TOuter, TResult>> outerResultSelector)
{
return condition
? outer.Join(inner,
outerKeySelector,
innerKeySelector,
joinResultSelector)
: outer.Select(outerResultSelector);
}
You build operators like this you need to start with the signatures of the built-in operators and keep the parameter signatures where possible. You are effectively merging Join
with Select
, so start with them:
IQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,
IEnumerable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<TOuter, TInner, TResult>> resultSelector)
public static IQueryable<TResult> Select<TSource, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> selector)
Your code had some of the signatures wrong.
Also, you could reduce down my answer to work with just T
quite easily:
public static IQueryable<T> OptionalJoin<T, K>(
this IQueryable<T> outer,
bool condition,
IEnumerable<T> inner,
Expression<Func<T, K>> outerKeySelector,
Expression<Func<T, K>> innerKeySelector,
Expression<Func<T, T, T>> joinResultSelector)
{
return condition
? outer.Join(inner,
outerKeySelector,
innerKeySelector,
joinResultSelector)
: outer;
}
I hope this helps.
Upvotes: 2
Reputation: 244767
Optional join doesn't really make much sense. Your return type is IQueryable<T>
, which is correct if you want to return the original collection. But if you want to join something into it, the return type would have to change. And you can't have a method that returns different compile-time types based on a run-time condition.
Because of that, I think what you're trying to do is impossible.
The only way I can imagine something like this could work would be if if you had two result selectors: one when condition
is true and the other when it's false. And both of them would return the same type.
Also, there are several mistakes in your code, which I tried to fix.
public static IQueryable<TResult> OptionalJoin<TSource, TInner, TKey, TResult>(
this IQueryable<TSource> source,
bool condition,
IQueryable<TInner> innerCollection,
Expression<Func<T, TKey>> outerKeySelector,
Expression<Func<T, TKey>> innerKeySelector,
Expression<Func<TSource, TInner, TResult>> trueResultSelector,
Expression<Func<TSource, TResult>> falseResultSelector)
{
return condition
? source.Join(innerCollection, outerKeySelector, innerKeySelector, trueResultSelector)
: source.Select(falseResultSelector);
}
Upvotes: 3