Share via


Récupérer et détecter les modifications apportées aux métadonnées

Les classes de l’espace de noms Microsoft.Xrm.Sdk.Metadata.Query et les classes RetrieveMetadataChangesRequest et RetrieveMetadataChangesResponse vous permettent de créer des requêtes de métadonnées efficaces et de collecter les modifications apportées aux métadonnées à mesure qu’elles se produisent dans le temps.

Tous les exemples de code référencés dans ce document se trouvent dans Exemple : Rechercher des métadonnées et détecter les modifications.

L'article technique Rechercher des métadonnées à l'aide de JavaScript fournit une bibliothèque JavaScript pour utiliser les objets et messages dans le code côté client.

Stratégies d’utilisation des métadonnées

Les métadonnées vous permettent de créer des applications qui s’adaptent au fil des modifications du modèle de données Dynamics 365 Customer Engagement (on-premises). Les métadonnées sont importantes pour les types d’application suivants :

  • Interface utilisateur des applications clientes

  • Outils d’intégration devant mapper les données Dynamics 365 Customer Engagement (on-premises) aux systèmes externes

  • Outils de développement

    Les classes de l’espace de noms Microsoft.Xrm.Sdk.Metadata.Query vous permettent d’implémenter des conceptions qui existent quelque part entre une requête légère et un cache de métadonnées persistant.

Requête légère

Un exemple de requête légère est une interface utilisateur de ressource Web personnalisée qui fournit un contrôle de sélection pour afficher les options actuelles dans un attribut Groupe d’options Dynamics 365 Customer Engagement (liste déroulante). Vous ne souhaitez pas coder en dur ces options, car vous devrez mettre à jour ce code si les options disponibles sont modifiées. À la place, vous pouvez créer une requête pour récupérer uniquement ces valeurs d’options et étiquettes à partir des métadonnées.

Il n’est pas nécessaire de mettre en cache ces données, car vous pouvez utiliser les classes Microsoft.Xrm.Sdk.Metadata.Query pour les récupérer directement à partir du cache d’application Dynamics 365 Customer Engagement.

Cache de métadonnées persistant

Lorsque vous disposez d’une application qui doit pouvoir s’exécuter lorsqu’elle est déconnectée de Dynamics 365 Server, ou qui est sensible à une limitation de la bande passante réseau entre le client et le serveur, telle qu’une application mobile, vous pouvez implémenter un cache de métadonnées persistant.

Avec un cache de métadonnées persistant, votre application devra rechercher toutes les métadonnées nécessaires lors de sa première connexion. Vous enregistrez ensuite ces données dans l’application. La prochaine fois que l’application se connectera au serveur, vous pourrez récupérer uniquement la différence depuis la dernière requête, ce qui doit représenter beaucoup moins de données à transmettre, puis fusionner les modifications dans votre cache de métadonnées pendant le chargement de votre application.

La fréquence de recherche des modifications de métadonnées dépend de la volatilité attendue des métadonnées de votre application et de la durée d’exécution de votre application. Il n’existe aucun événement disponible vous permettant de détecter quand des modifications sont apportées aux métadonnées. La durée d’enregistrement des modifications de métadonnées supprimées est limitée et une demande de modifications qui se produit au-delà de cette limite nécessite une réinitialisation complète du cache des métadonnées. Pour plus d’informations, voir Expiration des métadonnées supprimées.

Si aucune modification n’est apportée, la requête doit répondre rapidement et aucune donnée ne sera retournée. Toutefois, si des modifications sont apportées, en particulier si des éléments de métadonnées supprimées doivent être supprimés de votre cache, vous pouvez vous attendre à ce que la requête mette un peu plus de temps à s'exécuter. Informations complémentaires : Performances lors de la récupération des métadonnées supprimées

Récupérer uniquement les métadonnées nécessaires

Les métadonnées sont souvent récupérées ou synchronisées au démarrage d’une application et peuvent affecter le temps de chargement de l’application. Cela est particulièrement vrai pour les applications mobiles qui récupèrent des métadonnées pour la première fois. Récupérer uniquement les métadonnées nécessaires est très important pour créer une application qui s’exécute correctement.

La classe EntityQueryExpression fournit une structure cohérente avec la classe QueryExpression que vous utilisez pour créer des requêtes complexes pour récupérer les données d’entité. Contrairement aux classes RetrieveAllEntitiesRequest, RetrieveEntityRequest, RetrieveAttributeRequest ou RetrieveRelationshipRequest, RetrieveMetadataChangesRequest contient un paramètre Query qui accepte une instance EntityQueryExpression que vous pouvez utiliser pour définir des critères spécifiques pour les données à retourner, en plus des propriétés souhaitées. Vous pouvez utiliser RetrieveMetadataChangesRequest pour retourner l’ensemble complet de métadonnées que vous obtenez à l’aide de RetrieveAllEntitiesRequest, ou simplement une étiquette pour un attribut spécifique.

Spécifier vos critères de filtre

La propriété EntityQueryExpression.Criteria accepte une MetadataFilterExpression qui contient une collection d’objets MetadataConditionExpression permettant de définir des conditions de filtrage des propriétés d’entité en fonction de leur valeur. Ces conditions utilisent MetadataConditionOperator qui autorise les opérateurs suivants :

L’exemple suivant illustre MetadataFilterExpression qui retourne un ensemble d’entités sans intersection appartenant à l’utilisateur non incluses dans une liste d’entités à exclure :




     // An array SchemaName values for non-intersect, user-owned entities that should not be returned.
     String[] excludedEntities = {
"WorkflowLog",
"Template",
"CustomerOpportunityRole",
"Import",
"UserQueryVisualization",
"UserEntityInstanceData",
"ImportLog",
"RecurrenceRule",
"QuoteClose",
"UserForm",
"SharePointDocumentLocation",
"Queue",
"DuplicateRule",
"OpportunityClose",
"Workflow",
"RecurringAppointmentMaster",
"CustomerRelationship",
"Annotation",
"SharePointSite",
"ImportData",
"ImportFile",
"OrderClose",
"Contract",
"BulkOperation",
"CampaignResponse",
"Connection",
"Report",
"CampaignActivity",
"UserEntityUISettings",
"IncidentResolution",
"GoalRollupQuery",
"MailMergeTemplate",
"Campaign",
"PostFollow",
"ImportMap",
"Goal",
"AsyncOperation",
"ProcessSession",
"UserQuery",
"ActivityPointer",
"List",
"ServiceAppointment"};

     //A filter expression to limit entities returned to non-intersect, user-owned entities not found in the list of excluded entities.
     MetadataFilterExpression EntityFilter = new MetadataFilterExpression(LogicalOperator.And);
     EntityFilter.Conditions.Add(new MetadataConditionExpression("IsIntersect", MetadataConditionOperator.Equals, false));
     EntityFilter.Conditions.Add(new MetadataConditionExpression("OwnershipType", MetadataConditionOperator.Equals, OwnershipTypes.UserOwned));
     EntityFilter.Conditions.Add(new MetadataConditionExpression("SchemaName", MetadataConditionOperator.NotIn, excludedEntities));
     MetadataConditionExpression isVisibileInMobileTrue = new MetadataConditionExpression("IsVisibleInMobile", MetadataConditionOperator.Equals, true);
     EntityFilter.Conditions.Add(isVisibileInMobileTrue);

Spécifier les propriétés souhaitées

La propriété Properties accepte MetadataPropertiesExpression. Vous pouvez définir MetadataPropertiesExpression .AllProperties sur true si vous souhaitez renvoyer toutes les propriétés ou vous pouvez fournir une collection de chaînes à la propriété MetadataPropertiesExpression .PropertyNames pour définir les propriétés que vous souhaitez inclure dans les résultats.

Les objets fortement tapés retournés incluront toutes les propriétés, mais seules celles que vous demandez contiendront des données. Toutes les propriétés auront une valeur null, à quelques exceptions près : chaque élément de métadonnées inclura les valeurs MetadataId,LogicalName et HasChanged si elles existent pour cet élément. Il n’est pas nécessaire de les spécifier dans les propriétés Properties que vous demandez.

Si vous n’utilisez pas du code géré et analysez réellement la valeur responseXML retournée de XMLHttpRequest, vous obtiendrez des éléments pour chaque propriété, mais seules celles que vous demandez contiendront des données. Le code XML suivant indique le code xml des métadonnées d’entité de contact qui sera retourné lorsque IsVisibleInMobile est la seule propriété demandée.

<a:EntityMetadata>  
 <c:MetadataId>608861bc-50a4-4c5f-a02c-21fe1943e2cf</c:MetadataId>  
 <c:HasChanged i:nil="true"/>  
 <c:ActivityTypeMask i:nil="true"/>  
 <c:Attributes i:nil="true"/>  
 <c:AutoRouteToOwnerQueue i:nil="true"/>  
 <c:CanBeInManyToMany i:nil="true"/>  
 <c:CanBePrimaryEntityInRelationship i:nil="true"/>  
 <c:CanBeRelatedEntityInRelationship i:nil="true"/>  
 <c:CanCreateAttributes i:nil="true"/>  
 <c:CanCreateCharts i:nil="true"/>  
 <c:CanCreateForms i:nil="true"/>  
 <c:CanCreateViews i:nil="true"/>  
 <c:CanModifyAdditionalSettings i:nil="true"/>  
 <c:CanTriggerWorkflow i:nil="true"/>  
 <c:Description i:nil="true"/>  
 <c:DisplayCollectionName i:nil="true"/>  
 <c:DisplayName i:nil="true"/>  
 <c:IconLargeName i:nil="true"/>  
 <c:IconMediumName i:nil="true"/>  
 <c:IconSmallName i:nil="true"/>  
 <c:IsActivity i:nil="true"/>  
 <c:IsActivityParty i:nil="true"/>  
 <c:IsAuditEnabled i:nil="true"/>  
 <c:IsAvailableOffline i:nil="true"/>  
 <c:IsChildEntity i:nil="true"/>  
 <c:IsConnectionsEnabled i:nil="true"/>  
 <c:IsCustomEntity i:nil="true"/>  
 <c:IsCustomizable i:nil="true"/>  
 <c:IsDocumentManagementEnabled i:nil="true"/>  
 <c:IsDuplicateDetectionEnabled i:nil="true"/>  
 <c:IsEnabledForCharts i:nil="true"/>  
 <c:IsImportable i:nil="true"/>  
 <c:IsIntersect i:nil="true"/>  
 <c:IsMailMergeEnabled i:nil="true"/>  
 <c:IsManaged i:nil="true"/>  
 <c:IsMappable i:nil="true"/>  
 <c:IsReadingPaneEnabled i:nil="true"/>  
 <c:IsRenameable i:nil="true"/>  
 <c:IsValidForAdvancedFind i:nil="true"/>  
 <c:IsValidForQueue i:nil="true"/>  
 <c:IsVisibleInMobile>  
  <a:CanBeChanged>false</a:CanBeChanged>  
  <a:ManagedPropertyLogicalName>canmodifymobilevisibility</a:ManagedPropertyLogicalName>  
  <a:Value>false</a:Value>  
 </c:IsVisibleInMobile>  
 <c:LogicalName>contact</c:LogicalName>  
 <c:ManyToManyRelationships i:nil="true"/>  
 <c:ManyToOneRelationships i:nil="true"/>  
 <c:ObjectTypeCode i:nil="true"/>  
 <c:OneToManyRelationships i:nil="true"/>  
 <c:OwnershipType i:nil="true"/>  
 <c:PrimaryIdAttribute i:nil="true"/>  
 <c:PrimaryNameAttribute i:nil="true"/>  
 <c:Privileges i:nil="true"/>  
 <c:RecurrenceBaseEntityLogicalName i:nil="true"/>  
 <c:ReportViewName i:nil="true"/>  
 <c:SchemaName i:nil="true"/>  
</a:EntityMetadata>  
  

Dans une prochaine version, d’autres améliorations peuvent être obtenues en ne retournant pas les éléments avec des valeurs null pour les propriétés qui n’ont pas été demandées. Si vous écrivez du code pour analyser ce code XML, vous devez vous attendre à ce que le code XML retourné pour la même requête soit réduit au seul code XML suivant.

<a:EntityMetadata>  
 <c:MetadataId>608861bc-50a4-4c5f-a02c-21fe1943e2cf</c:MetadataId>  
 <c:IsVisibleInMobile>  
  <a:CanBeChanged>false</a:CanBeChanged>  
  <a:ManagedPropertyLogicalName>canmodifymobilevisibility</a:ManagedPropertyLogicalName>  
  <a:Value>false</a:Value>  
 </c:IsVisibleInMobile>  
 <c:LogicalName>contact</c:LogicalName>  
</a:EntityMetadata>  

Les métadonnées sont retournées en l’état dans une structure hiérarchique à l’aide de RetrieveAllEntitiesRequest. Pour accéder à un attribut ou une relation spécifique, vous devez créer une requête qui retourne l’entité dont il fait partie. Si vous souhaitez récupérer des données concernant un attribut spécifique, vous devez inclure la propriété EntityMetadata.Attributes dans votre EntityQueryExpression.Properties. Pour que les relations d’entité soient retournées, vous devez inclure une ou plusieurs des propriétés suivantes EntityMetadata : ManyToManyRelationships, ManyToOneRelationships ou OneToManyRelationships.

L’exemple suivant retourne la propriété Attributes pour les entités demandées :



//A properties expression to limit the properties to be included with entities
MetadataPropertiesExpression EntityProperties = new MetadataPropertiesExpression()
{
 AllProperties = false
};
EntityProperties.PropertyNames.AddRange(new string[] { "Attributes" });

Récupérer les métadonnées d’attribut

La propriété EntityQueryExpression.AttributeQuery accepte une AttributeQueryExpression qui définit Criteria et Properties pour les attributs à renvoyer pour les entités qui correspondent aux propriétés EntityQueryExpressionCriteria et Properties.

Le tableau suivant répertorie les propriétés AttributeMetadata qui ne peuvent pas être utilisées dans MetadataFilterExpression

L’exemple suivant limite les attributs retournés à ceux avec OptionSet et retourne uniquement les propriétés OptionSet et AttributeType pour ces attributs :



//A condition expresson to return optionset attributes
MetadataConditionExpression[] optionsetAttributeTypes = new MetadataConditionExpression[] { 
new MetadataConditionExpression("AttributeType", MetadataConditionOperator.Equals, AttributeTypeCode.Picklist),
new MetadataConditionExpression("AttributeType", MetadataConditionOperator.Equals, AttributeTypeCode.State),
new MetadataConditionExpression("AttributeType", MetadataConditionOperator.Equals, AttributeTypeCode.Status),
new MetadataConditionExpression("AttributeType", MetadataConditionOperator.Equals, AttributeTypeCode.Boolean)
};

//A filter expression to apply the optionsetAttributeTypes condition expression
MetadataFilterExpression AttributeFilter = new MetadataFilterExpression(LogicalOperator.Or);
AttributeFilter.Conditions.AddRange(optionsetAttributeTypes);

//A Properties expression to limit the properties to be included with attributes
MetadataPropertiesExpression AttributeProperties = new MetadataPropertiesExpression() { AllProperties = false };
AttributeProperties.PropertyNames.Add("OptionSet");
AttributeProperties.PropertyNames.Add("AttributeType");

Récupérer les métadonnées de relation

La propriété EntityQueryExpression.RelationshipQuery accepte une relation RelationshipQueryExpression pour préciser la relation de l’entité Criteria et Properties que vous souhaitez pour les entités qui correspondent à EntityQueryExpressionCriteria et Properties.

Utilisez la propriété RelationshipType dans vos critères pour spécifier si vous souhaitez retourner les relations ManyToMany ou OneToMany.

Le tableau suivant répertorie les propriétés de métadonnées de relation qui ne peuvent pas être utilisées dans MetadataFilterExpression :

Récupérer les étiquettes

Enfin, la propriété EntityQueryExpression.LabelQuery accepte une LabelQueryExpression qui vous permet de préciser une ou plusieurs valeurs LCID d’entier pour déterminer les étiquettes localisées à renvoyer. Les valeurs d’ID de paramètres régionaux valides sont disponibles sur la page Tableau des ID de paramètres régionaux (LCID). Si une organisation a plusieurs modules linguistiques installés, les étiquettes pour toutes les langues sont retournées sauf si vous spécifiez LabelQuery.

L’exemple suivant définit LabelQueryExpression qui limite les étiquettes à celles représentant la langue par défaut des utilisateurs.



private Guid _userId;
private int _languageCode;



_userId = ((WhoAmIResponse)_service.Execute(new WhoAmIRequest())).UserId;
_languageCode = RetrieveUserUILanguageCode(_userId);



protected int RetrieveUserUILanguageCode(Guid userId)
{
 QueryExpression userSettingsQuery = new QueryExpression("usersettings");
 userSettingsQuery.ColumnSet.AddColumns("uilanguageid", "systemuserid");
 userSettingsQuery.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, userId);
 EntityCollection userSettings = _service.RetrieveMultiple(userSettingsQuery);
 if (userSettings.Entities.Count > 0)
 {
  return (int)userSettings.Entities[0]["uilanguageid"];
 }
 return 0;
}




//A label query expression to limit the labels returned to only those for the user's preferred language
LabelQueryExpression labelQuery = new LabelQueryExpression();
labelQuery.FilterLanguages.Add(_languageCode);

Récupérer les métadonnées nouvelles ou modifiées

La classe RetrieveMetadataChangesResponse retourne un objet EntityMetadataCollection fortement tapé contenant les données demandées. La classe RetrieveMetadataChangesResponse fournit également une valeur ServerVersionStamp que vous pouvez passer à la propriété RetrieveMetadataChangesRequest.ClientVersionStamp dans les dernières requêtes. Lorsqu’une valeur est incluse pour la propriété ClientVersionStamp, seules les données qui correspondent à EntityQueryExpression et qui ont été modifiées depuis la récupération de ClientVersionStamp sont retournées. La seule exception est lorsque votre EntityQueryExpression.Properties inclut EntityMetadata.Privileges. Les privilèges sont toujours retournés indépendamment de ClientVersionStamp. Ainsi, votre application peut déterminer si des modifications importantes se sont produites depuis la dernière recherche de métadonnées. Vous pouvez ensuite fusionner les métadonnées nouvelles ou modifiées dans le cache de métadonnées persistant pour éviter que votre application rencontre des problèmes de performances liés au téléchargement de métadonnées inutiles.

La propriété HasChanged permet de détecter les éléments enfants modifiés dans un élément de métadonnées. Comme toutes les métadonnées sont retournées dans le cadre de l’élément de métadonnées conteneur, lorsque l’étiquette de OptionMetadata a été modifiée, les propriétés conteneurs EntityMetadata, AttributeMetadata et OptionSetMetadata sont retournées. Toutefois, la propriété HasChanged a la valeur false pour ces éléments de métadonnées conteneurs. Seule la propriété OptionMetadata.HasChanged est true.

L’exemple suivant effectue une demande initiale en définissant EntityQueryExpression et en effectuant une demande avec ClientVersionStamp défini sur la valeur null.



//An entity query expression to combine the filter expressions and property expressions for the query.
EntityQueryExpression entityQueryExpression = new EntityQueryExpression()
{

 Criteria = EntityFilter,
 Properties = EntityProperties,
 AttributeQuery = new AttributeQueryExpression()
 {
  Criteria = AttributeFilter,
  Properties = AttributeProperties
 },
 LabelQuery = labelQuery

};

//Retrieve the metadata for the query without a ClientVersionStamp
RetrieveMetadataChangesResponse initialRequest = getMetadataChanges(entityQueryExpression, null, DeletedMetadataFilters.OptionSet);



protected RetrieveMetadataChangesResponse getMetadataChanges(
 EntityQueryExpression entityQueryExpression,
 String clientVersionStamp,
 DeletedMetadataFilters deletedMetadataFilter)
{
 RetrieveMetadataChangesRequest retrieveMetadataChangesRequest = new RetrieveMetadataChangesRequest()
 {
  Query = entityQueryExpression,
  ClientVersionStamp = clientVersionStamp,
  DeletedMetadataFilters = deletedMetadataFilter
 };

 return (RetrieveMetadataChangesResponse)_service.Execute(retrieveMetadataChangesRequest);

}

Récupérer les informations relatives aux métadonnées supprimées

La propriété RetrieveMetadataChangesResponse.DeletedMetadata renvoie une DeletedMetadataCollection lorsque les propriétés ClientVersionStamp et DeletedMetadataFilters sont définies sur RetrieveMetadataChangesRequest. DeletedMetadataCollection contient les valeurs MetadataId des objets EntityMetadata, AttributeMetadata ou RelationshipMetadataBase qui ont été supprimés du système dans un délai spécifique. Pour plus d’informations, voir Expiration des métadonnées supprimées.

Utilisez l’énumération DeletedMetadataFilters avec RetrieveMetadataChangesRequest.DeletedMetadataFilters pour limiter les informations uniquement à ces types de métadonnées qui vous intéressent. L’énumération DeletedMetadataFilters fournit les options suivantes :

Expiration des métadonnées supprimées

Les éléments de métadonnées supprimées sont suivis pour une période limitée spécifiée par la valeur Organization.ExpireSubscriptionsInDays. Par défaut, cette valeur est 90 jours. Si la valeur RetrieveMetadataChangesRequest.ClientVersionStamp indique que la dernière requête de métadonnées a été émise avant la date d’expiration, le service lève une erreur ExpiredVersionStamp (0x80044352). Lorsque vous récupérez les données à actualiser et le cache de métadonnées existant, vous devez toujours essayer d’intercepter cette erreur et être prêt à réinitialiser le cache des métadonnées avec les résultats d’une deuxième demande passée sans ClientVersionStamp. L’erreur ExpiredVersionStamp est également levée lorsque des modifications du serveur, telles que des modifications de la valeur ExpireSubscriptionsInDays, affectent le suivi des métadonnées supprimées.

L’exemple suivant transmet ClientVersionStamp et intercepte ExpiredVersionStamp. Si l’erreur est interceptée, le cache est réinitialisé en transmettant une nouvelle demande avec ClientVersionStamp défini sur null.



protected String updateOptionLabelList(EntityQueryExpression entityQueryExpression, String clientVersionStamp)
{
 //Retrieve metadata changes and add them to the cache
 RetrieveMetadataChangesResponse updateResponse;
 try
 {
  updateResponse = getMetadataChanges(entityQueryExpression, clientVersionStamp, DeletedMetadataFilters.OptionSet);
  addOptionLabelsToCache(updateResponse.EntityMetadata, true);
  removeOptionLabelsFromCache(updateResponse.DeletedMetadata, true);

 }
 catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
 {
  // Check for ErrorCodes.ExpiredVersionStamp (0x80044352)
  // Will occur when the timestamp exceeds the Organization.ExpireSubscriptionsInDays value, which is 90 by default.
  if (ex.Detail.ErrorCode == unchecked((int)0x80044352))
  {
   //reinitialize cache
   _optionLabelList.Clear();

   updateResponse = getMetadataChanges(entityQueryExpression, null, DeletedMetadataFilters.OptionSet);
   //Add them to the cache and display the changes
   addOptionLabelsToCache(updateResponse.EntityMetadata, true);

  }
  else
  {
   throw ex;
  }

 }
 return updateResponse.ServerVersionStamp;
}

Performances lors de la récupération des métadonnées supprimées

Lorsqu’un élément de métadonnées est supprimé, il est enregistré dans la base de données, et pas dans le cache des métadonnées de Dynamics 365 Customer Engagement. Bien que les métadonnées supprimées soient limitées à MetadataId et au type d’élément de métadonnées, l’accès à la base de données est une opération qui nécessite davantage de ressources du serveur qu’une simple recherche de modifications.

Voir aussi

Écrire des applications et des extensions de serveur
Utilisation hors connexion des services Dynamics 365 Customer Engagement
Exemple : Rechercher des métadonnées et détecter les modifications
Étendre le modèle de métadonnées pour Dynamics 365 Customer Engagement
Personnaliser les métadonnées d’entité
Personnaliser les métadonnées d’attribut d’entité
Personnaliser des métadonnées de relation d’entité
Rechercher des métadonnées à l’aide de JavaScript