Question

I need to join different datasources in runtime using linq to objects, but my datasources types are unknown. So, I can´t use Join directly. I have a Dictionary containing my datasources, and that is how far I got:

public virtual IEnumerable DataSource { get; set; }

public virtual void MergeDataSources()
{
    var outerType = System.Type.GetType(DataSources.Keys.First());
    DataSource = Cast(DataSources[DataSources.Keys.First()], outerType);

    foreach (var typeName in DataSources.Keys.Skip(1))
        outerType = Join(outerType, System.Type.GetType(typeName), new[] { "CodigoCliente" }, new[] { "CodigoCliente" });

}

private IEnumerable Cast(IEnumerable datasource, Type type)
{
    return typeof(Enumerable)
                .GetMethod("Cast")
                .MakeGenericMethod(type)
                .Invoke(null, new object[] { datasource }) as IEnumerable;
}

private Type Join(Type outerType, Type innerType, string[] outerKey, string[] innerKey)
{
    var outerKeySelector = GetKeySelector(outerType, outerKey);
    var innerKeySelector = GetKeySelector(innerType, innerKey);

    var resultSelector = Expression.Lambda(
                            null, // <<<<<<------- MISSING!!!
                            Expression.Parameter(outerType),
                            Expression.Parameter(innerType));

    DataSource = typeof(Enumerable)
                    .GetMethod("Join")
                    .MakeGenericMethod(outerType, innerType, typeof(string), typeof(IEnumerable))
                    .Invoke(null, new object[] { DataSource, DataSources[innerType.AssemblyQualifiedName], outerKeySelector, innerKeySelector, resultSelector }) as IEnumerable;

    return null; // <<<<<<------- MISSING!!!
}

The last thing that I need is: each type joined must be a property of resulting object. Example: new { Type1 = some_Obj_Of_Type1, Type2 = some_Obj_Of_Type2 }

EDIT: I added a DataSource property, that represents my merged collection

Was it helpful?

Solution

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; 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top