如何利用反射简化Linq扩展方法的调用?

7

背景:

我有一个 Web 服务,它会返回表中(表名由参数提供)大于某个 Id(也由参数提供)的行。我们假设这些 Id 是连续的。

我正在使用 Linq to SQL 进行数据库交互,因此我希望将新行返回为:

List<WhateverObject>

因为我们只在运行时知道表名,所以我无法像正常情况下使用Linq,这使得事情变得更加复杂。
问题:
以下是代码(它可以工作)。如何简化它?它看起来过于复杂。
private object GetUpdateList(string tableName, int Id, DataClassesDataContext db)
{
    PropertyInfo pi = db.GetType().GetProperty(tableName);

    var table = pi.GetValue(db, null);

    // Get type of object within the table.
    Type genericType = table.GetType().GetGenericArguments()[0];

    // The Where method lives on the Enumerable type in System.Linq
    var whereMethods = typeof(System.Linq.Enumerable)
        .GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(mi => mi.Name == "Where");

    // There are actually 2 where methods - we want the one with 2 parameters
    MethodInfo whereMethod = null;
    foreach (var methodInfo in whereMethods)
    {
        var paramType = methodInfo.GetParameters()[1].ParameterType;
        if (paramType.GetGenericArguments().Count() == 2)
        {
            // we are looking for  Func<TSource, bool>, the other has 3
            whereMethod = methodInfo;
            break;
        }
    }

    Func<object, bool> IdEquals = BuildEqFuncFor("Id", Id);

    whereMethod = whereMethod.MakeGenericMethod(genericType);
    var result = whereMethod.Invoke(table, new object[] { table, IdEquals });

    MethodInfo toListMethod = typeof(System.Linq.Enumerable).GetMethod("ToList").MakeGenericMethod(genericType);
    return toListMethod.Invoke(result, new object[] { result });
}

// Build lambda expression for use in Linq
private static Func<object, bool> BuildEqFuncFor(string prop, object val)
{
    // We know we are comparing integers here so cast them.
    // There is probably a more general solution.
    return t => (int)t.GetType().InvokeMember(prop, BindingFlags.GetProperty, null, t, null) > (int)val;
}

为了得出这个解决方案,我不得不参考以下问题:
请注意,以上内容保留了HTML标签。

不是为了推卸责任,但你是否处于无法重构的情况下?这似乎对于一些简单的事情来说需要付出很多工作。它需要如此通用吗? - Bryan Crosby
只需将其强类型化?传递表名和ID。从数据库中选择它并返回请求的行。返回一个 IEnumerable<WhateverObject>,然后就完成了。 - Bryan Crosby
问题是我在编译时不知道表名。我需要它返回当前30个表中的任何一个(未来可能会增加)。如果它是动态的,我可以添加表格,这段代码仍然可以工作。 - Ryan Kirkman
1个回答

5
尝试像这样做:

试试以下代码:

private IList GetUpdateList(string tableName, int id, DataClassesDataContext db)
{
    System.Reflection.PropertyInfo pi = db.GetType().GetProperty(tableName);

    var table = pi.GetValue(db, null);

    // Get type of object within the table.
    Type genericType = table.GetType().GetGenericArguments()[0];

    var param = Expression.Parameter(genericType, "x");
    var predicateExpr = Expression.Lambda(
        Expression.GreaterThan(
            Expression.Property(param, "Id"),
            Expression.Constant(id)),
        param);

    return this
        .GetType()
        .GetMethod("GetUpdateListGeneric")
        .MakeGenericMethod(genericType)
        .Invoke(this, new[] { table, predicateExpr }) as IList;
}

private IList<T> GetUpdateListGeneric<T>(
    Table<T> table, 
    Expression<Func<T, bool>> predicate) where T : class
{
    return table.Where(predicate).ToList();
}

太棒了!我只需要编辑一些东西(返回类型),并将Expression.Parameter先存入一个变量中,然后它就可以工作了。 - Ryan Kirkman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接