如何使用Linq to Objects连接不同的数据源

时间:2014-01-02 13:30:07

标签: linq reflection linq-to-objects

我需要在运行时使用linq到对象连接不同的数据源,但我的数据源类型是未知的。所以,我不能直接使用Join。 我有一个包含我的数据源的字典,这就是我得到了多远:

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!!!
}

我需要的最后一件事是:每个连接的类型必须是结果对象的属性。示例:new {Type1 = some_Obj_Of_Type1,Type2 = some_Obj_Of_Type2}

编辑: 我添加了一个DataSource属性,它表示我的合并集合

1 个答案:

答案 0 :(得分:1)

您可以使用Reflection.Emit动态创建返回类型,请查看此处 - How to create LINQ Expression Tree with anonymous type in it

符合您的示例:

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; 
}