2009-05-31 7 views
0

J'utilise Entity Framework et je développé cette méthode d'extension:Essayer de développer une nouvelle méthode d'extension

public static IQueryable<TResult> Like<TResult>(this IQueryable<TResult> query, Expression<Func<TResult, string>> field, string value) 
{ 
    var expression = Expression.Lambda<Func<TResult, bool>>(
     Expression.Call(field.Body, typeof(string).GetMethod("Contains"), 
     Expression.Constant(value)), field.Parameters); 

    return query.Where(expression); 
} 

ce code fonctionne correctement si je l'utilise comme ceci:

var result = from e in context.es.Like(r => r.Field, "xxx") 
      select e 

maintenant J'ai besoin d'appeler cette méthode d'extension par programme:

public static IQueryable<TSource> SearchInText<TSource>(this IQueryable<TSource> source, string textToFind) 
{ 
    // Collect fields 
    PropertyInfo[] propertiesInfo = source.ElementType.GetProperties(); 
    List<string> fields = new List<string>(); 
    foreach (PropertyInfo propertyInfo in propertiesInfo) 
    { 
     if (
      (propertyInfo.PropertyType == typeof(string)) || 
      (propertyInfo.PropertyType == typeof(int)) || 
      (propertyInfo.PropertyType == typeof(long)) || 
      (propertyInfo.PropertyType == typeof(byte)) || 
      (propertyInfo.PropertyType == typeof(short)) 
      ) 
     { 
      fields.Add(propertyInfo.Name); 
     } 
    } 

    ParameterExpression parameter = Expression.Parameter(typeof(TSource), source.ElementType.Name); 
    Expression expression = Expression.Lambda(Expression.Property(parameter, typeof(TSource).GetProperty(fields[0])), parameter); 
    Expression<Func<TSource, string>> field = Expression.Lambda<Func<TSource, string>>(expression, parameter); 

    return source.Like(field, textToFind); 
} 

Maintenant, ce code ne fonctionne pas! J'ai besoin de comprendre comment déclarer le "champ" des méthodes étendues Like.

Expression<Func<TSource, string>> field = Expression.Lambda<Func<TSource, string>>(expression, parameter); 

Lors de l'exécution, je reçois cette erreur: Impossibile utilizzare un'espressione di tipo 'System.Func`2 [TestMdf.Equipment, System.String]' par un tipo restituito 'System.String'

+0

Je suis un peu confus par votre deuxième méthode d'extension. Vous parcourez tous les PropertyInfo sur l'élément, les ajoutez à un ensemble de champs, puis choisissez le premier. Cela semble vraiment vague et non ciblé ... très susceptible de retourner des résultats aléatoires. Avant que je fournisse n'importe quelle sorte de réponse ... est-ce que c'est ce que tu veux? Ou avez-vous vraiment besoin de choisir une propriété spécifique à rechercher ... ou avez-vous besoin de faire défiler toutes les propriétés et d'appeler .Like() pour chacune d'entre elles et agréger les résultats? – jrista

+0

Salut, vous avez raison sur la boucle ... La version finale de cette méthodes étendues générera plusieurs clause Where. Dans l'exemple, vous avez les champs F1, F2, F3 qui sont des nombres ou des chaînes Je voudrais rechercher dans le texte comme ceci: '' + F1 + '' + F2 + '' + F3 + '' LIKE ' % Mais sera plus compliqué à nouveau ... Je voudrais ajouter un peu de moteur de recherche donc: '' + F1 + '' + F2 + '' + F3 + '' LIKE '% % ' ET' '+ F1 +' '+ F2 +' '+ F3 +' 'LIKE'% % ' ET' '+ F1 +' '+ F2 +' '+ F3 +' 'N'EST PAS'% % ' et gérer les phrases aussi. –

Répondre

0

Ceci est ma première version:

public static IQueryable<TSource> SearchInText<TSource>(this IQueryable<TSource> source, string textToFind) 
{ 
    if (textToFind.Trim() == "") 
    { 
     return source; 
    } 
    string[] textToFindList = textToFind.Replace("'", "''").Split(' '); 

    // Collect fields 
    PropertyInfo[] propertiesInfo = source.ElementType.GetProperties(); 
    List<string> fieldList = new List<string>(); 
    foreach (PropertyInfo propertyInfo in propertiesInfo) 
    { 
     if (
      (propertyInfo.PropertyType == typeof(string)) || 
      (propertyInfo.PropertyType == typeof(int)) || 
      (propertyInfo.PropertyType == typeof(long)) || 
      (propertyInfo.PropertyType == typeof(byte)) || 
      (propertyInfo.PropertyType == typeof(short)) 
      ) 
     { 
      fieldList.Add(propertyInfo.Name); 
     } 
    } 

    ParameterExpression parameter = Expression.Parameter(typeof(TSource), source.ElementType.Name); 
    MethodInfo concatMethod = typeof(String).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }); 

    var spaceExpression = Expression.Constant(" "); 
    var concatenatedField = BinaryExpression.Add(spaceExpression, Expression.MakeMemberAccess(parameter, typeof(TSource).GetProperty(fieldList[0])), concatMethod); 

    for (int i = 1; i < fieldList.Count; i++) 
    { 
     concatenatedField = BinaryExpression.Add(concatenatedField, spaceExpression, concatMethod); 
     concatenatedField = BinaryExpression.Add(concatenatedField, Expression.MakeMemberAccess(parameter, typeof(TSource).GetProperty(fieldList[i])), concatMethod); 
    } 

    concatenatedField = BinaryExpression.Add(concatenatedField, spaceExpression, concatMethod); 
    var fieldsExpression = Expression.Call(concatenatedField, "ToUpper", null, null); 

    var clauseExpression = Expression.Call(
     fieldsExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), 
     Expression.Constant(textToFindList[0].ToUpper()) 
     ); 

    if (textToFindList.Length == 1) 
    { 
     return source.Where(Expression.Lambda<Func<TSource, bool>>(clauseExpression, parameter)); 
    } 

    BinaryExpression expression = Expression.And(Expression.Call(
      fieldsExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), 
      Expression.Constant(textToFindList[1].ToUpper()) 
      ), clauseExpression); 
    for (int i = 2; i < textToFindList.Length; i++) 
    { 
     expression = Expression.And(Expression.Call(
      fieldsExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), 
      Expression.Constant(textToFindList[i].ToUpper()) 
      ), expression); 
    } 

    return source.Where(Expression.Lambda<Func<TSource, bool>>(expression, parameter)); 

} 

je modifierai pour gérer certaines règles comme "phrase" + et - opérateur.

0

Je suppose que votre deuxième extrait de code est juste un exemple tronqué - si vous l'avez vraiment fait, les résultats seraient imprévisibles, car vous prenez la première propriété renvoyée par réflexion, qui peut changer entre les exécutions de votre programme. Vous obtiendrez de meilleures réponses si vous dites "Cela a fonctionné" suivi d'une description de ce qui s'est passé, et "Cela n'a pas fonctionné" suivi d'une description de la façon dont vous pourriez dire que cela n'a pas fonctionné (compilateur erreur? erreur d'exécution? message d'exception?)

Tout d'abord, connaissez-vous Dynamic Linq? Il vous permet de retarder les décisions sur la façon dont une requête doit être structurée jusqu'à l'exécution et peut résoudre nombre de vos problèmes pour vous.

Mais en supposant que cela est un exercice d'apprentissage ...

Votre Like méthode d'extension prend une expression (qui l'appelant doit l'habitude d'écrire comme un lambda, comme c'est le point de l'ensemble de ces choses). Cette expression convertira un "enregistrement" à partir d'un ensemble de résultats de requête et renverra une valeur de chaîne (en la sélectionnant vraisemblablement parmi les données stockées dans l'enregistrement). La méthode prend également une chaîne de valeur.

Mais il construit ensuite (à la main) son propre prédicat qui appelle la méthode Contains sur le corps du lambda field.

Je suppose que cela devrait fonctionner, car le résultat de cette lambda est une chaîne. Cependant, je ne vois pas pourquoi vous faites cela à la dure. Quel est le problème avec:

var result = from e in context.es 
      where e.Field.Contains("xxx")) 
      select e 
+0

Salut, je voudrais rechercher dans tous les champs de sorte que vous devez composer les champs comme ceci '' + F1 + '' + F2 ... J'ai besoin d'une méthodes étendues juste parce que je ne connais pas la liste des champs. Vous avez raison, mon second extrait de code n'est qu'un exemple tronqué. Je pense que cette méthode étendue peut être très utile, donc j'espère pouvoir trouver de l'aide pour la compléter. –

+0

Mes premières méthodes étendues est le seul moyen que j'ai trouvé pour appeler dinamiquement les méthodes de Contains. J'ai besoin de l'appeler dinamiquement parce que je dois construire le champ pour comparer. Quelque chose comme ("" + F1 + "" + F2 + "") .Contains ("" + + "") –

+0

Je dois comprendre comment je dois construire le paramètre pour appeler la méthode Like. Si vous écrivez dans votre champ de code. Comme ("texte") c'est simple. Mais je ne sais pas comment je peux l'appeler dinamiquement. A propos du "ne fonctionne pas": il compile mais je reçois cette erreur sur l'exécution: "Impossibile utilizzare un'espressione de tipo" System.Func'2 [TestMdf.Equipment, System.String] 'par un système de restituito' .String '" –

0

Maintenant, je trouve une solution partielle à mon problème:

public static IQueryable<TSource> SearchInText<TSource>(this IQueryable<TSource> source, string textToFind) 
{ 
    // Collect fields 
    PropertyInfo[] propertiesInfo = source.ElementType.GetProperties(); 
    List<string> fields = new List<string>(); 
    foreach (PropertyInfo propertyInfo in propertiesInfo) 
    { 
     if (
      (propertyInfo.PropertyType == typeof(string)) || 
      (propertyInfo.PropertyType == typeof(int)) || 
      (propertyInfo.PropertyType == typeof(long)) || 
      (propertyInfo.PropertyType == typeof(byte)) || 
      (propertyInfo.PropertyType == typeof(short)) 
      ) 
     { 
      fields.Add(propertyInfo.Name); 
     } 
    } 

    ParameterExpression parameter = Expression.Parameter(typeof(TSource),  source.ElementType.Name); 

    var property = typeof(TSource).GetProperty(fields[0]); 
    var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     var constantValue = Expression.Constant(textToFind); 
    var equality = Expression.Call(Expression.Call(Expression.Property(parameter,  property), "ToUpper", null, null), typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(textToFind.ToUpper())); 

    return source.Where(Expression.Lambda<Func<TSource, bool>>(equality, parameter)); 
} 

Maintenant, l'étape suivante consiste à concaténer tous la liste des champs:

" " + fields[0] + " " + ... fields[n] 

Quelques idées?

+0

Concaténation de champ? Vous voulez probablement utiliser un StringBuilder. –