You could use Reflection.Emit
to create the return type dynamically, take a look here - How to create LINQ Expression Tree with anonymous type in it.
To match your example:
private Type Join(Type outerType, Type innerType, string[] outerKey, string[] innerKey)
{
var outerKeySelector = GetKeySelector(outerType, outerKey);
var innerKeySelector = GetKeySelector(innerType, innerKey);
Dictionary<string, Type> dynamicFields = new Dictionary<string, Type>
{
{ outerType.Name, outerType },
{ innerType.Name, innerType }
};
Dictionary<string, ParameterExpression> parameters = new Dictionary<string, ParameterExpression>
{
{ outerType.Name, Expression.Parameter(outerType) },
{ innerType.Name, Expression.Parameter(innerType) }
};
Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(dynamicFields);
var resultSelector = Expression.Lambda(
Expression.MemberInit(
Expression.New(
dynamicType.GetConstructor(Type.EmptyTypes)),
dynamicType.GetFields().Select(f => Expression.Bind(f, parameters[f.Name]))),
parameters.Values)
.Compile();
DataSource = typeof(Enumerable)
.GetMethods().Where(m => m.Name == "Join" && m.GetParameters().Length == 5).First()
.MakeGenericMethod(outerType, innerType, typeof(string), typeof(object))
.Invoke(null, new object[] { DataSource, DataSources[innerType.AssemblyQualifiedName], outerKeySelector, innerKeySelector, resultSelector }) as IEnumerable;
return dynamicType;
}