Episodes précédents:
Maintenant que notre arbre d'expression est prêt à être traitée, il faut s'occuper de le transformer en WIQL, et en premier lieu de transformer les appels à des propriétés de ma classe WorkItem en leurs champs WIQL correspondants. Ce travail préliminaire va nous permettre de gagner du temps par la suite lorsque nous allons “naviguer” dans l’arbre.
Je reprends la requête du premier billet:
var q = from wi in server.WorkItems()
where
(wi.Title.Contains(searchString) ||
wi.History.Contains(searchString) ||
wi.Description.Contains(searchString)) &&
wi.Field<string>(SystemField.AssignedTo) == QueryConstant.Me
orderby wi.CreatedDate descending, wi.Title
select wi;
"wi.Title" est transformé en [System.Title] dans la requête. Pour cela je passe par des définitions d'attributs qui seront identifiés par le provider. Voici la définition de la propriété Title dans le code de Fissum:
[Field(SystemField.Title)]
public string Title
{
get
{
return _workItem.Title;
}
set
{
_workItem.Title = value;
}
}
La correspondance est réalisée par l'attribut Field ("System.Title" n'est pas tel quel dans le code mais accessible via la constante "SystemField.Title"). Le mécanisme de transformation est dans la classe statique FieldUtility:
public static string ExtractFieldReferenceName(ICustomAttributeProvider methodInfo)
{
FieldAttribute[] array = methodInfo.GetCustomAttributes(typeof(FieldAttribute), false) as FieldAttribute[];
if (array == null)
{
return null;
}
return String.Format("[{0}]", array[0].ReferenceName);
}
Revenons sur la requête Linq encore une fois, il existe une autre façon d'identifier un champ avec Linq To WIQL: via la méthode Field. Cette manière est utile lorsque le champs n'est pas un champ identifié dans une propriété du workitem ou de ses classes dérivés (j'aborderai cette partie un peu plus tard). De la même façon que pour la propriété, la classe FieldUtility va nous aider:
public static string ExtractWIFieldFromMethodCall(MethodCallExpression m)
{
switch (m.Method.Name)
{
case "Field":
{
if (m.Arguments.Count != 1)
{
throw new InvalidOperationException("MethodCall");
}
ConstantExpression constant = m.Arguments[0] as ConstantExpression;
if (constant == null)
{
throw new InvalidOperationException("MethodCall");
}
return "[" + constant.Value + "]";
}
default:
return null;
}
}
Il suffira ensuite d’appeler l’une de ces 2 méthodes lors de la navigation nous serons en présence d’un champ d’un workitem. La syntaxe du WIQL est assez simple pour que ces endroits soient très facilement identifiables dans le code:
- Dans la partie gauche d’un opérateur binaire si la partie gauche n’est pas elle-même un opérateur
- Dans les clauses d’ordre.
Nous verrons cela dans la prochaine partie et comment tout s'agence avec la gestion des opérateurs dans la clause Where. Un gros problème nous attend dans la prochaine partie.