2017-03-10 1 views
1

Contexte: l'utilisateur doit être en mesure de choisir aussi efficacement que possible un DB-Table/Model/Class et filtrer/trier/afficher toutes les propriétés publiques de celui-ci.Charger l'accesseur Func par réflexion dans le dictionnaire

Les noms peuvent être interrogés par l'API de réflexion, mais je me demandais, si ces accès peuvent être stockés et devenir plus efficace de cette façon?

Cet exemple montre comment cela peut être fait, mais à chaque accès, il va interroger la réflexion-api dans la fonction.

public class TestClass // the Model or Table 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public static void Main(string[] args) 
{ 
    var testClasses = new TestClass[] { 
     new TestClass { Id = 1 , Name = "1" } , 
     new TestClass { Id = 2 , Name = "2" } , 
     new TestClass { Id = 3 , Name = "3" } , 
    }; 

    var propertyInfos = typeof(TestClass).GetProperties(); 
    var map = new Dictionary<string,Func<object,object>>(); // Func<object,object> -> Func takes an instance of the class and return a public property 

    // load the map once 
    foreach(var propertyInfo in propertyInfos) 
    { 
     Func<object,object> func = x => propertyInfo.GetValue(x); 

     map.Add(propertyInfo.Name , func); 
    } 

    // get the names by user-input 
    var names = propertyInfos.Select(x => x.Name).ToArray(); 

    // load the properties by name 
    foreach(var testClass in testClasses) 
    { 
     Console.WriteLine($"{testClass.Id} - {testClass.Name}"); 

     foreach(var name in names) 
     { 
      var func = map[ name ]; 
      var value = func(testClass); // this is 'bad' as it uses reflection every invokation 

      Console.WriteLine($"\t{name} = {value}"); 
     } 
    } 
} 

Ma question serait la suivante: peut-il Dictionnaire

var map = new Dictionary<string,Func<object,object>> { 
    { "Id"  , x => (x as TestClass).Id } , 
    { "Name" , x => (x as TestClass).Name } , 
}; 

automatiquement créé en fournissant seulement le type (et sans utiliser la réflexion sur chaque invokation)?

+0

Notez que si vous faites ce que vous suggérez, vous allez filtrer les lignes C# -side ... Vous ne produirez pas une requête SQL qui utilise un 'WHERE'. Votre code C# recevra toutes les lignes de la table, puis les filtrera :-( – xanatos

+0

En général, comment le faire "correctement" dépend de la façon dont vous faites les requêtes à la base de données ... Si vous utilisez un * Ado.NET * ('DbConnection' ou' SqlConnection' avec 'DbCommand' ou' SqlCommand') et écrire des requêtes avec des chaînes puis il y a un ensemble de solutions.Si vous utilisez Entity Framework, Linq-to-SQL, NHibernate, ??? – xanatos

+0

Bon point Malheureusement, je dois charger l'enregistrement en mémoire, car je dois les pré-traiter avant le filtrage (et je ne peux pas changer la représentation de la table sous-jacente ni stocker les versions prétraitées dans une autre table). même possible, comme une «procédure stockée pour C# Funcs» – nonsensation

Répondre

2

Vous pourriez gagner quelque chose en supprimant la réflexion de chaque appel et de le faire qu'une seule fois:

var par = Expression.Parameter(typeof(object), "row"); 

// load the map once 
foreach (var propertyInfo in propertyInfos) 
{ 
    Func<object, object> func = Expression.Lambda<Func<object, object>>(Expression.Convert(Expression.Property(Expression.Convert(par, propertyInfo.DeclaringType), propertyInfo), typeof(object)), par).Compile(); 
    map.Add(propertyInfo.Name, func); 
} 

créer un varient petit arbre d'expression qui jette l'objet de paramètre du type « correct » (TestClass dans ce cas), appelle le getter de la propriété et convertit le résultat en object.