Sfondo:
Ho un servizio web che restituisce le righe in una tabella (nome tabella fornito come parametro) con Id maggiore di un determinato Id (fornito anche come parametro). Assumiamo che gli ID siano sequenziali.
Sto usando Linq to SQL per l'interazione con il database, quindi voglio restituire le nuove righe come:
List<WhateverObject>
Poiché conosciamo solo il nome della tabella in fase di runtime, non posso usare Linq in modo normale, il che ha reso le cose molto più complesse.
Domanda:
Il codice è sotto (e funziona). Come posso semplificarlo? Sembra troppo complesso.
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;
}
Per elaborare questa soluzione ho dovuto fare riferimento alle seguenti domande:
Prova qualcosa del genere:
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();
}