Comment puis-je simplifier l'invocation de méthode d'extension Linq en utilisant la réflexion?

c# expression-trees generics linq reflection

Question

Contexte:

J'ai un service Web qui renvoie les lignes d'une table (nom de la table fourni en tant que paramètre) avec des ID supérieurs à un certain Id (également fournis en tant que paramètre). Nous supposons que les identifiants sont séquentiels.

J'utilise Linq to SQL pour l'interaction avec la base de données, je souhaite donc renvoyer les nouvelles lignes en tant que:

List<WhateverObject>

Comme nous ne connaissons que le nom de la table au moment de l'exécution, je ne peux pas utiliser Linq normalement, ce qui a rendu les choses beaucoup plus complexes.

Question:

Le code est ci-dessous (et ça marche). Comment puis-je le simplifier? Cela semble trop complexe.

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

Pour trouver cette solution, j'ai dû faire référence aux questions suivantes:

Réponse acceptée

Essayez quelque chose comme ça:

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


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow