Partager via


Exemple : Modifier la requête dans la phase PreOperation

Cet exemple montre comment écrire un plug-in qui modifie une requête définie dans la phase PreOperation d’une demande RetrieveMultiple.

Le filtrage des données dans un plug-in est généralement effectué dans la phase PostOperation. Les données Entities peuvent être examinées et les tables qui ne doivent pas être renvoyées sont supprimées de la collection. Mais ce modèle présente des problèmes et le nombre d’enregistrements renvoyés dans une page peut ne pas correspondre aux tailles de pagination planifiées.

La méthode décrite par cet exemple est différente. Plutôt que de filtrer les tables après leur récupération, ce plug-in appliquera les modifications à la requête dans l’étape PreOperation avant son exécution.

Un point clé de cet exemple est que Query peut être de l’un des trois types différents dérivés de QueryBase. Pour gérer des requêtes de tout type, le code de plug-in doit détecter le type de requête et mettre en œuvre le type de filtre approprié.

Comment exécuter cet exemple

  1. Télécharger ou cloner le référentiel Exemples pour en avoir une copie locale. Cet exemple se trouve sous PowerApps-Samples-master\cds\orgsvc\C#\RetrieveMultipleAccountPreOperation.
  2. Ouvrez l’exemple de solution dans Visual Studio, accédez aux propriétés du projet, puis vérifiez que l’assembly sera signé pendant la build. Appuyez sur F6 pour générer l’assemblage d’un exemple (RetrieveMultipleAccountPreOperation.dll).
  3. Exécutez l’outil d’enregistrement de plug-in et enregistrez l’assembly dans le bac à sable et la base de données du serveur Microsoft Dataverse pour l’étape PreOperation du message RetrieveMultiple de la table Account.
  4. Utilisation d’une application ou saisie de code pour récupérer des comptes pour déclencher le plug-in. Voir Code pour tester cet exemple ci-dessous pour obtenir un exemple.
  5. Lorsque vous avec terminé vos tests, annulez l’enregistrement de l’assembly et de l’étape.

En quoi consiste cet exemple :

Lorsque le plug-in est exécuté, il garantit que des enregistrements de comptes inactifs ne sont pas retournés pour les types de requêtes les plus courants : QueryExpression et FetchExpression.

QueryByAttribute est un troisième type de requête qui peut aussi être utilisé. Il ne prend pas en charge de requêtes complexes et par conséquent il n’est pas possible d’appliquer un filtrage complexe à l’aide de cette méthode. Heureusement, ce type de requête n’est pas fréquemment utilisé. Vous souhaitez peut-être rejeter des requêtes de ce type en levant une InvalidPluginExecutionException dans la phase PreValidation.

Utiliser cet exemple

Afin de simuler le scénario décrit dans En quoi consiste cet exemple, l’exemple procède comme suit :

  1. Vérifiez que les paramètres d’entrée comprennent un paramètre appelé Query
  2. Testez le type de requête en effectuant un cast comme l’un des trois types attendu.
  3. Selon le type de requête, la requête est modifiée de la manière suivante :

FetchExpression

  1. Analysez la valeur Query contenant le FetchXml dans XDocument.
  2. Vérifiez que attribute de l’élément entity précise la table account.
  3. Examinez tous les éléments filter dans la requête pour les conditions qui testent la colonne statecode.
  4. Supprimez toutes les conditions existantes basées sur cette colonne.
  5. Ajoutez un nouveau filter à la requête qui nécessite que seuls les comptes où statecode n’est pas égal à 1 (Inactif) est renvoyé.
  6. Définissez la requête modifiée sur la valeur Query
if (fetchExpressionQuery != null)
{
    tracingService.Trace("Found FetchExpression Query");

    XDocument fetchXmlDoc = XDocument.Parse(fetchExpressionQuery.Query);
    //The required entity element
    var entityElement = fetchXmlDoc.Descendants("entity").FirstOrDefault();
    var entityName = entityElement.Attributes("name").FirstOrDefault().Value;

    //Only applying to the account entity
    if (entityName == "account")
    {
        tracingService.Trace("Query on Account confirmed");

        //Get all filter elements
        var filterElements = entityElement.Descendants("filter");

        //Find any existing statecode conditions
        var stateCodeConditions = from c in filterElements.Descendants("condition")
                                    where c.Attribute("attribute").Value.Equals("statecode")
                                    select c;

        if (stateCodeConditions.Count() > 0)
        {
            tracingService.Trace("Removing existing statecode filter conditions.");
        }
        //Remove statecode conditions
        stateCodeConditions.ToList().ForEach(x => x.Remove());


        //Add the condition you want in a new filter
        entityElement.Add(
            new XElement("filter",
                new XElement("condition",
                    new XAttribute("attribute", "statecode"),
                    new XAttribute("operator", "neq"), //not equal
                    new XAttribute("value", "1") //Inactive
                    )
                )
            );
    }


    fetchExpressionQuery.Query = fetchXmlDoc.ToString();

}

QueryExpression

  1. Vérifiez que EntityName est la table account.
  2. Effectuez une boucle viaCriteria.Filters collecte
  3. Utilisez la méthode RemoveAttributeConditions récursive pour rechercher toutes les instances ConditionExpression qui testent l’attribut de statecode et supprimez-les.
  4. Ajoutez un nouveau FilterExpression à la collection Criteria.Filters qui nécessite que seuls les comptes où statecode n’est pas égal à 1 (Inactif) est renvoyé.
if (queryExpressionQuery != null)
{
    tracingService.Trace("Found Query Expression Query");
    if (queryExpressionQuery.EntityName.Equals("account"))
    {
        tracingService.Trace("Query on Account confirmed");

        //Recursively remove any conditions referring to the statecode column
        foreach (FilterExpression fe in queryExpressionQuery.Criteria.Filters)
        {
            //Remove any existing criteria based on statecode column
            RemoveAttributeConditions(fe, "statecode", tracingService);
        }

        //Define the filter
        var stateCodeFilter = new FilterExpression();
        stateCodeFilter.AddCondition("statecode", ConditionOperator.NotEqual, 1);
        //Add it to the Criteria
        queryExpressionQuery.Criteria.AddFilter(stateCodeFilter);
    }

}

Méthode RemoveAttributeConditions

Méthode récursive qui supprime toutes les conditions applicables à une colonne spécifique nommée

/// <summary>
/// Removes any conditions using a specific named column
/// </summary>
/// <param name="filter">The filter that may have a condition using the column</param>
/// <param name="attributeName">The name of the column that should not be used in a condition</param>
/// <param name="tracingService">The tracing service to use</param>
private void RemoveAttributeConditions(FilterExpression filter, string attributeName, ITracingService tracingService)
{

    List<ConditionExpression> conditionsToRemove = new List<ConditionExpression>();

    foreach (ConditionExpression ce in filter.Conditions)
    {
        if (ce.AttributeName.Equals(attributeName))
        {
            conditionsToRemove.Add(ce);
        }
    }

    conditionsToRemove.ForEach(x =>
    {
        filter.Conditions.Remove(x);
        tracingService.Trace("Removed existing statecode filter conditions.");
    });

    foreach (FilterExpression fe in filter.Filters)
    {
        RemoveAttributeConditions(fe, attributeName, tracingService);
    }
}

QueryByAttribute

Comme QueryByAttribute ne prend pas en charge les filtres complexes, entrez uniquement un message dans le journal de suivi du plug-in.

Si vous ne souhaitez pas que ce type de requête soit utilisé, vous pouvez lever une InvalidPluginExecutionException pour empêcher l’opération, mais il est préférable de l’appliquer au cours de la phase de PreValidation.

if (queryByAttributeQuery != null)
{
    tracingService.Trace("Found Query By Attribute Query");
    //Query by attribute doesn't provide a complex query model that
    // can be manipulated
}

Code pour tester cet exemple

Le code suivant illustre 5 méthodes différentes pour effectuer la même requête qui déclenchera le plug-in.

En spécifiant des critères spécifiques, dans ce cas la valeur de la colonne address1_city, à laquelle un seul enregistrement actif doit correspondre, ces requêtes renverront uniquement cet enregistrement.

Ensuite, désactivez cet enregistrement et exécutez ce code une seconde fois. Aucun enregistrement ne sera retourné.

try
{
    string account_city_value = "ValueForTesting";

    //QueryByAttribute
    var queryByAttribute = new QueryByAttribute("account")
    {
        TopCount = 1,
        ColumnSet = new ColumnSet("accountid", "name")
    };
    queryByAttribute.AddAttributeValue("address1_city", account_city_value);
    queryByAttribute.AddOrder("name", OrderType.Descending);

    //QueryExpression
    var queryExpression = new QueryExpression("account")
    { ColumnSet = new ColumnSet("accountid", "name"), TopCount = 1 };
    queryExpression.Orders.Add(new OrderExpression("name", OrderType.Descending));
    var qeFilter = new FilterExpression(LogicalOperator.And);
    qeFilter.AddCondition(new ConditionExpression("address1_city", ConditionOperator.Equal, account_city_value));
    queryExpression.Criteria = qeFilter;

    //Fetch
    var fetchXml = $@"<fetch mapping='logical' count='1'>
                <entity name='account'>
                    <attribute name='accountid'/>
                    <attribute name='name'/>
                    <order attribute='name' descending='true' />
                    <filter>
                    <condition attribute='address1_city' operator='eq' value='{account_city_value}' />
                    </filter>
                </entity>
            </fetch>";

    var fetchExpression = new FetchExpression(fetchXml);

    //Get results:
    var queryByAttributeResults = service.RetrieveMultiple(queryByAttribute);
    var queryExpressionResults = service.RetrieveMultiple(queryExpression);
    var fetchExpressionResults = service.RetrieveMultiple(fetchExpression);

    //WebAPI
    string WebAPIAccountName = string.Empty;

    Dictionary<string, List<string>> ODataHeaders = new Dictionary<string, List<string>>() {
    {"Accept", new List<string>(){"application/json" } },
    {"OData-MaxVersion", new List<string>(){ "4.0" } },
    {"OData-Version", new List<string>(){ "4.0" } }};


    HttpResponseMessage response = service.ExecuteCrmWebRequest(HttpMethod.Get,
        $"accounts?$select=accountid,name&$top=1&$orderby=name desc&$filter=address1_city eq '{account_city_value}'",
        string.Empty,
        ODataHeaders);
    if (response.IsSuccessStatusCode)
    {
        var results = response.Content.ReadAsStringAsync().Result;
        var jsonResults = JObject.Parse(results);
        var accounts = (JArray)jsonResults.GetValue("value");
        if (accounts.Count > 0)
        {
            var account = accounts.First();
            WebAPIAccountName = account.Value<string>("name");
        }

    }

    else
    {
        Console.WriteLine(response.ReasonPhrase);
    }

    //Using Fetch with Web API
    string FetchWebAPIAccountName = string.Empty;
    HttpResponseMessage fetchResponse = service.ExecuteCrmWebRequest(HttpMethod.Get,
    $"accounts?fetchXml=" + Uri.EscapeDataString(fetchXml),
    string.Empty,
    ODataHeaders);
    if (fetchResponse.IsSuccessStatusCode)
    {

        var results = fetchResponse.Content.ReadAsStringAsync().Result;
        var jsonResults = JObject.Parse(results);
        var accounts = (JArray)jsonResults.GetValue("value");
        if (accounts.Count > 0)
        {
            var account = accounts.First();
            FetchWebAPIAccountName = account.Value<string>("name");
        }
    }

    else
    {
        Console.WriteLine(fetchResponse.ReasonPhrase);
    }

    string no_records_message = "No records returned";

    Console.WriteLine("QueryByAttribute Account Returned: {0}", queryByAttributeResults.Entities.Count > 0 ?
        queryByAttributeResults.Entities[0]["name"] : no_records_message);
    Console.WriteLine("QueryExpression Account Returned: {0}", queryExpressionResults.Entities.Count > 0 ?
        queryExpressionResults.Entities[0]["name"] : no_records_message);
    Console.WriteLine("Fetch Account Returned: {0}", fetchExpressionResults.Entities.Count > 0 ?
        fetchExpressionResults.Entities[0]["name"] : no_records_message);
    Console.WriteLine("WebAPI Account Returned: {0}", WebAPIAccountName != string.Empty ?
        WebAPIAccountName : no_records_message);
    Console.WriteLine("WebAPI Fetch Account Returned: {0}", FetchWebAPIAccountName != string.Empty ?
        FetchWebAPIAccountName : no_records_message);

}
catch (Exception ex)
{

    throw ex;
}

Voir aussi

Mettre en œuvre tous types de requêtes lors du filtrage des résultats à l’aide de PreOperation RetrieveMultiple

Notes

Pouvez-vous nous indiquer vos préférences de langue pour la documentation ? Répondez à un court questionnaire. (veuillez noter que ce questionnaire est en anglais)

Le questionnaire vous prendra environ sept minutes. Aucune donnée personnelle n’est collectée (déclaration de confidentialité).