2012-01-28 5 views
3

J'ai besoin d'un filtrage en mémoire avec Dynamic Linq. Mes objets ont seulement indexeur:Linq dynamique: existe-t-il un moyen d'accéder aux données d'objet par index?

public object this[int index] { } 

L'accès à mes données sont comme: objet [0], objet [1], ...

Donc ma question est comme ceci:

// get FilterText from user at runtime 
// eg. filterText can be: [0] > 100 and [1] = "wpf" 
collection.AsQueryable().where(filterText); 

Y at-il un moyen de le faire?

Répondre

2

J'ai quelques nouvelles pour vous.

C'est en fait possible ... MAIS! (Oui, il y a un mais) Je suppose que vous utilisez la bibliothèque Linq dynamique. Vous devez ensuite utiliser le mot-clé "it" pour pouvoir spécifier l'objet courant auquel vous souhaitez appliquer votre opération d'indexation. Cependant, le type de données de retour de votre indexeur est objet, vous ne pourrez donc pas écrire [0]> 100 et [1] = "wpf" en raison d'une discordance de type de données. De plus, même si vous dérivez de DynamicObject et ajoutez les propriétés à l'exécution, ces propriétés ne seront pas résolues à l'exécution par la bibliothèque linq dynamique dans son état actuel. Vous obtiendriez juste un champ ou une propriété n'existe pas dans le type xxx.

Il existe plusieurs solutions à ce problème, dont certaines peuvent être considérées comme une solution.

  • Une solution laide, si vous avez un nombre limité de types de données, par exemple n (où n < le nombre de types .NET), vous pouvez utiliser n indexeurs avec mise en correspondance des paramètres et obtenir le type de données que vous vouloir. Pour exemple, si vous avez la plupart ints et quelques cordes:

    it[0] > 100 AND it[1, "dummy"] = "wpf" //The dummy parameter allows you to specify return type. 
    
  • Une autre solution laid, dynamique Linq a un support pour l'utilisation du ToString() et Convert-méthodes, donc, vous pouvez par exemple écrire le même requête comme ci-dessus:

    Convert.ToDouble(it[0]) > 100 AND it[1].ToString() = "wpf". 
    
  • une troisième solution laide, vous pouvez utiliser une convention où vous enveloppez déclarations d'une manière qui vous indique comment convertir les données. Pour exemple, vous pouvez écrire:

    it[0] > 100 AND it{1} = "wpf" 
    

    et remplacer "il [" avec "Convert.ToDouble (il [" et ainsi de suite ...

Si je suis bon, je pense que vous ne pouvez pas non plus utiliser un indexeur générique avec la bibliothèque, et Convert.ChangeType ne vous convient pas dans ce cas puisque le type de retour est toujours object.Peut-être que moi ou quelqu'un d'autre réécrira la bibliothèque un certain temps pour supporter ce genre de choses, mais je n'ai pas le temps de le faire dans un proche avenir (quelques semaines).

Eh bien, je suis désolé, mais je dois être quelque part dans 15 min, donc nous devrons prendre les meilleures solutions plus tard j'espère!

me téléporter à la réunion

MISE À JOUR:

Je pense que je l'ai trouvé une solution à votre problème (et mon)!

Dans la bibliothèque DLINQ, vous pouvez ajouter un membre dans l'interface (s) IxxxSignatures pour le type avec lequel vous souhaitez pouvoir travailler.

Alors, j'ai ajouté (par exemple) dans les IEqualitySignatures:

void F(Object x, Int32 y); 

Modifiés le (dans ce cas) ParseComparison méthode dans le bloc autre comme celui-ci.

left = Expression.Convert(left, right.Type); 

Et, croyez-le ou non, cela a fonctionné :)

Je ne l'ai pas testé toutes sortes d'opérations, puisque je ne l'ai pas ajouté les signatures pour les autres types et opérations, mais il devrait être tout à fait simple à faire!

MISE À JOUR

mis à jour certaines choses mineures ci-dessus ..

je suis expérimenter un peu plus sur ce sujet et alors qu'il pourrait ne pas être la plus jolie solution soit, vous pourriez faire quelque chose comme ça (DataObject est juste un DynamicObject avec un indexeur):

[TestMethod] 
    public void DynamicTest() 
    { 
     List<DataObject> dataObjects = new List<DataObject>(); 

     dynamic firstObject = new DataObject(); 
     dynamic secondObject = new DataObject(); 

     firstObject.dblProp = 10.0; 
     firstObject.intProp = 8; 
     firstObject.strProp = "Hello"; 

     secondObject.dblProp = 8.0; 
     secondObject.intProp = 8; 
     secondObject.strProp = "World"; 

     dataObjects.Add(firstObject); 
     dataObjects.Add(secondObject); 

     /* Notice the different types */ 
     string newQuery = FormatQuery("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'"); 

     var result = dataObjects.Where(newQuery); 

     Assert.AreEqual(result.Count(), 1); 
     Assert.AreEqual(result.First(), firstObject); 
    } 

Et une sorte de méthode de format comme (faites semblant écrit une méthode Ive complète): 01

public string FormatQuery(string query) 
    { 
     query = query.Replace('\'', '\"'); 

     string[] operators = new string[] { "<", ">", "!=", "<=", ">=", "<>", "=" }; 

     string[] parts = query.Split(); 

     for (int i = 0; i < parts.Length; i++) 
     { 
      if (operators.Contains(parts[i])) 
      { 
       parts[i - 1] = "it[\"" + parts[i - 1] + "\"]"; 
      } 
     } 

     return String.Join(" ", parts); 
    } 

Nous pourrions bien sûr avoir une méthode d'extension à la place.

Ou ... On pourrait mettre la méthode dans le DLINQ lib en utilisant quelque chose comme (pas dire une bonne idée cependant):

if (typeof(T).GetInterface("IDynamicMetaObjectProvider", true) != null) 
    whereClause = whereClause.FormatQuery(); 

Et, vérifiez si le type implémente une chaîne indexeur bien sûr , quelque chose comme (en ignorant IndexerName attribut ici):

if (t.GetType().GetProperty("Item") != null) 

qui permettrait à un utilisateur "normal" d'écrire:

data.Where("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'") 

Eh bien, ce n'est peut-être pas bon d'avoir ce genre de choses, mais vous avez compris. Tu aurais pu! :)

+0

vraiment? Je ne pense pas que vous ayez besoin de faire des choses aussi laides, voyez ma réponse. – Krizz

+0

Oui, je sais. J'essayais juste de faire le point que le type est objet et donc certaines opérations ne s'appliquent pas sans conversion. Ce serait bien d'avoir une conversion implicite. Peut-être que DLINQ pourrait regarder la valeur du côté droit et convertir le côté gauche (je sais, il fait déjà une partie de cela, mais les libs pourraient toujours faire plus pour vous: P). – Johan

+0

Merci pour cette excellente réponse. Je comprends maintenant le contexte et ce qui est possible. Mais je ne suis pas vraiment satisfait :( J'ai essayé ce jour à – Schorsch

1

Vous utilisez DynamicLinq http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx ou vous construisez vos arbres d'expression en utilisant la réflexion. Beaucoup de gens préfèrent la bibliothèque Dynamic Linq pour cela; Je ne l'ai jamais utilisé moi-même, j'ai pris l'approche de la réflexion.

+0

Vous ne savez pas qui vous a donné -1. C'est une réponse parfaitement valide. +1 – EBarr

+0

Merci, @EBarr. Je me demandais pourquoi était downvoted moi même si c'est correct. – Icarus

+1

Merci à votre commentaire. J'utilise la bibliothèque DLinq. Je l'ai étendu maintenant pour le traduire [n]. Mais le problème est la conversion de type. Voir ma réponse aux autres déclarations. – Schorsch

1

Vous avez juste besoin de préfixer votre indexeur avec it qui est équivalent en langage DynamicLinq à this en C#.Par conséquent, vous avez juste besoin de it[1] == "wpf".

Cependant, il y a plus de complication du fait que votre indexeur renvoie object. Pour les types de classe (y compris string), tout va bien, DLinq fera la promotion de tout au besoin. Toutefois, pour les types de valeur tels que int s, vous devrez effectuer Int32(it[0]) > 10.

+0

Non ce n'est pas l'ensemble complet de funktionality que j'ai avec lui [1] .ToString(). Dans ce cas, je peux aussi utiliser "=,>, <". Entourer ou ajouter une méthode n'est pas le problème. Le problème est d'obtenir le bon type pour ajouter cette methotd. Voir mon commentaire à la première réponse. – Schorsch

+0

@Schorsch désolé, je ne peux pas comprendre ce que vous voulez dire. Pouvez-vous s'il vous plaît expliquer? "Le problème est d'obtenir le bon type pour ajouter cette methotd." - Pour obtenir le type correct de quoi? ajouter quelle méthode? – Krizz

+0

@Schorsch, j'ai mis à jour ma réponse.J'espère que c'est utile pour vous! – Johan

Questions connexes