Recuperar y detectar cambios en metadatos
Las clases del espacio de nombres Microsoft.Xrm.Sdk.Metadata.Query y las clases RetrieveMetadataChangesRequest y RetrieveMetadataChangesResponse permiten crear consultas eficaces de metadatos y capturar los cambios que se producen según suceden en el tiempo.
Todos los ejemplos de código a los que se hace referencia en este documento se encuentran en Ejemplo: consultar metadatos y detectar cambios.
El artículo técnico Consultar metadatos mediante JavaScript proporciona una biblioteca de JavaScript para utilizar los objetos y mensajes en el código del lado del cliente.
Estrategias para usar metadatos
Los metadatos permiten crear aplicaciones que se adaptan a medida que cambia el modelo de datos de Dynamics 365 Customer Engagement (on-premises). Los metadatos son importantes para los siguientes tipos de aplicaciones:
Interfaz de usuario para aplicaciones cliente
Herramientas de integración que tienen que asignar datos de Dynamics 365 Customer Engagement (on-premises) a sistemas externos
Herramientas de desarrollo
Mediante las clases del espacio de nombres Microsoft.Xrm.Sdk.Metadata.Query puede implementar los diseños que existirán en algún punto entre una consulta ligera y una memoria caché de metadatos persistente.
Consulta ligera
Un ejemplo de una consulta ligera es cuando tiene una interfaz de usuario de recurso web personalizado que proporciona un control de selección para mostrar las opciones actuales en un atributo de conjunto de opciones de Dynamics 365 Customer Engagement (lista desplegable). No conviene configurar de forma rígida estas opciones porque habrá que actualizar dicho código si las opciones disponibles cambian en algún momento. En su lugar, se puede crear una consulta solo para recuperar esos valores y etiquetas de opciones desde los metadatos.
No tiene que almacenar en memoria caché estos datos porque puede usar las clases de Microsoft.Xrm.Sdk.Metadata.Query para recuperarlos directamente de la memoria caché de aplicación de Dynamics 365 Customer Engagement.
Memoria caché de metadatos persistente
Si tiene una aplicación que debe poder trabajar mientras está desconectada de Dynamics 365 Server o que es sensible a un ancho de banda de red limitado entre el cliente y el servidor, por ejemplo una aplicación móvil, le conviene implementar una memoria caché de metadatos persistente.
Con una caché de metadatos persistente, la aplicación tendrá que consultar todos los metadatos necesarios la primera vez que se conecta. A continuación, tendrá que guardar esos datos en la aplicación. La siguiente vez que la aplicación se conecta al servidor, se puede recuperar únicamente la diferencia desde la última consulta, lo que supondrá menor cantidad de datos que transferir; a continuación, se combinan los cambios en la memoria caché de metadatos cuando la aplicación se carga.
La frecuencia con la que debe sondear los cambios de metadatos depende de la volatilidad que se espera en los metadatos de la aplicación y cuánto tiempo la aplicación continuará ejecutándose. No hay ningún evento disponible que se pueda usar para detectar cuándo se producen cambios en los metadatos. Hay un límite del número de días que se guardan los cambios en los metadatos eliminados y una solicitud de los cambios que tienen lugar después de ese límite necesitará una reiniciación completa de la memoria caché de metadatos. Para obtener más información, consulte Caducidad de los metadatos eliminados.
Cuando no hay cambios, la consulta responde con rapidez y no habrá datos que transferir. Sin embargo, si hay cambios, especialmente si hay elementos eliminados en los metadatos que tienen que eliminarse de la memoria caché, es de esperar que la solicitud requiera un tiempo adicional para finalizar. Más información: Rendimiento cuando se recuperan metadatos eliminados
Recuperar solo los metadatos que se necesitan
Con frecuencia los metadatos se recuperan o sincronizan cuando una aplicación se inicia y afecta al tiempo que la aplicación necesita para cargarse. Esto es especialmente así con aplicaciones móviles que recuperan metadatos por primera vez. Recuperar solo los metadatos que se necesitan es muy importante para crear una aplicación que rinda correctamente.
La clase EntityQueryExpression proporciona una estructura coherente con la clase QueryExpression que se usa para crear consultas complejas para recuperar datos de entidad. A diferencia de las clases RetrieveAllEntitiesRequest, RetrieveEntityRequest, RetrieveAttributeRequest o RetrieveRelationshipRequest, RetrieveMetadataChangesRequest contiene un parámetro Query
que acepta una instancia de EntityQueryExpression que puede usar para especificar criterios específicos de los datos que se devuelven además de las propiedades que se desean. Puede usar RetrieveMetadataChangesRequest para devolver el conjunto completo de metadatos que se obtienen mediante RetrieveAllEntitiesRequest, o solo una etiqueta para un atributo específico.
Especificar los criterios de filtro
La propiedad EntityQueryExpression.Criteria acepta un MetadataFilterExpression que contiene una colección de objetos MetadataConditionExpression que permite definir las condiciones para filtrar las propiedades de entidad según su valor. Estas condiciones usan un MetadataConditionOperator que permite los operadores siguientes:
MetadataConditionOperator.Equals
MetadataConditionOperator.NotEquals
MetadataConditionOperator.GreaterThan
MetadataConditionOperator.LessThan
MetadataFilterExpression también incluye LogicalOperator que representa si se aplica la lógica
And
oOr
al evaluar las condiciones.No todas las propiedades se pueden usar como criterios de filtro. Solo las propiedades que representan tipos de datos simples, enumeraciones o tipos BooleanManagedProperty o AttributeRequiredLevelManagedProperty se pueden usar en MetadataFilterExpression. Cuando se especifica BooleanManagedProperty o AttributeRequiredLevelManagedProperty, solo se evalúa la propiedad
Value
.En la siguiente tabla se enumeran las propiedades EntityMetadata que no se pueden usar en MetadataFilterExpression:
El siguiente ejemplo muestra una MetadataFilterExpression que devolverá un conjunto de entidades que no se cruzan y son propiedad del usuario no incluidas en una lista de entidades que se excluyen:
// 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);
Especificar las propiedades que se desean
La propiedad Properties acepta una MetadataPropertiesExpression. Puede configurar MetadataPropertiesExpression .AllProperties a true
si desea devolver todas las propiedades o puede proporcionar una colección de cadenas al MetadataPropertiesExpression .PropertyNames para definir qué propiedades desea incluir en los resultados.
Los objetos con establecimiento inflexible de tipos devueltos incluirán todas las propiedades, pero solo las que se hayan solicitado tendrán datos. El resto de las propiedades serán nulas, con las pocas excepciones siguientes: cada elemento de metadatos incluirá los valores MetadataId, LogicalName
y HasChanged si existen para ese elemento. No tiene que especificarlos en las Properties que solicita.
Si no está usando código administrado y está analizando realmente la responseXML
devuelta de XMLHttpRequest, obtendrá elementos para cada propiedad pero solo las que se solicitan contendrán datos. El XML siguiente muestra el XML de los metadatos de entidad de contacto que se devolverán cuando IsVisibleInMobile
sea la única propiedad solicitada.
<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>
En una versión futura se logrará más eficacia no devolviendo elementos con valores null para propiedades que no se solicitaron. Si escribe código para analizar este XML, debe contar con que el XML devuelto para la misma consulta se podría reducir al XML siguiente únicamente.
<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>
Los metadatos se devuelven en una estructura jerárquica igual que cuando se usa RetrieveAllEntitiesRequest. Para obtener acceso a un atributo o relación específicos, debe crear una consulta que devuelva la entidad de la que forman parte. Si desea recuperar datos sobre un atributo específico, debe incluir la propiedad EntityMetadata.Attributes en su EntityQueryExpression.Properties. Para que las relaciones entre entidades se devuelvan, debe incluir una o varias de las siguientes propiedades: EntityMetadata: ManyToManyRelationships, ManyToOneRelationships o OneToManyRelationships.
El siguiente ejemplo devolverá la propiedad Attributes
para las entidades solicitadas:
//A properties expression to limit the properties to be included with entities
MetadataPropertiesExpression EntityProperties = new MetadataPropertiesExpression()
{
AllProperties = false
};
EntityProperties.PropertyNames.AddRange(new string[] { "Attributes" });
Recuperar metadatos de atributo
La propiedad EntityQueryExpression.AttributeQuery acepta un AttributeQueryExpression que define Criteria y Properties para que los atributos se devuelvan para las entidades que coincidan con EntityQueryExpressionCriteria y Properties.
En la siguiente tabla se enumeran las propiedades AttributeMetadata que no se pueden usar en MetadataFilterExpression
En el siguiente ejemplo se limitarán los atributos devueltos a los que tengan OptionSet
y se devolverán únicamente las propiedades OptionSet y AttributeType para esos atributos:
//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");
Recuperar metadatos de relaciones
La propiedad EntityQueryExpression.RelationshipQuery acepta un RelationshipQueryExpression para especificar la relación entre entidades Criteria y Properties que desea para las entidades que coinciden con EntityQueryExpressionCriteria y Properties.
Use la propiedad RelationshipType en sus criterios para especificar si desea devolver relaciones ManyToMany o OneToMany.
En la siguiente tabla se enumeran las propiedades de relaciones que no se pueden usar en MetadataFilterExpression:
- OneToManyRelationshipMetadata.AssociatedMenuConfiguration
- OneToManyRelationshipMetadata.CascadeConfiguration
-
ManyToManyRelationshipMetadata.Entity1AssociatedMenuConfiguration
- ManyToManyRelationshipMetadata.Entity2AssociatedMenuConfiguration
Recuperar etiquetas
Por último, la propiedad EntityQueryExpression.LabelQuery acepta un LabelQueryExpression que le permite especificar uno o más valores LCID
enteros para determinar qué etiquetas localizadas devolver. Los valores de identificadores de configuración regional válidos pueden encontrarse en el gráfico de identificadores de configuración regional (LCID). Si una organización tiene muchos paquetes de idioma instalados, se devolverán las etiquetas para todos los idiomas a menos que se especifique LabelQuery.
En el siguiente ejemplo se define LabelQueryExpression que limita las etiquetas a las que representan el idioma preferido de los usuarios únicamente.
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);
Recuperar metadatos nuevos o modificados
La clase RetrieveMetadataChangesResponse devuelve EntityMetadataCollection con establecimiento inflexible de tipos que contiene los datos solicitados. La clase RetrieveMetadataChangesResponse también proporciona un valor ServerVersionStamp que se puede pasar a la propiedad RetrieveMetadataChangesRequest.ClientVersionStamp en posteriores solicitudes. Cuando un valor está incluido para la propiedad ClientVersionStamp, solo se devolverán los datos coincidentes con EntityQueryExpression y que han cambiado desde que se recuperó ClientVersionStamp
. La única excepción a esto es cuando su EntityQueryExpression.Properties incluye EntityMetadata.Privileges. Los privilegios se devuelven siempre con independencia de ClientVersionStamp. De esta manera, la aplicación puede determinar si se han realizado cambios importantes que interesen desde la última vez que se consultaron los metadatos. Después, puede combinar los metadatos nuevos o modificados en la memoria caché de metadatos persistente de manera que la aplicación evite problemas de rendimiento descargando metadatos que no se necesitan.
La propiedad HasChanged proporciona una forma de detectar qué elementos secundarios de un elemento de metadatos han cambiado. Dado que todos los metadatos se devuelven como parte del elemento de metadatos contenedor, cuando la etiqueta de OptionMetadata cambia, se devuelven las propiedades EntityMetadata, AttributeMetadata y OptionSetMetadata contenedoras. Sin embargo, la propiedad HasChanged será falsa para los elementos de metadatos contenedores. Solo la propiedad OptionMetadata.HasChanged será verdadera.
En el siguiente ejemplo se realiza una solicitud inicial definiendo EntityQueryExpression y ejecutando una solicitud con ClientVersionStamp establecido en 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);
}
Recuperar información sobre metadatos eliminados
La propiedad RetrieveMetadataChangesResponse.DeletedMetadata devolverá un DeletedMetadataCollection cuando las propiedades ClientVersionStamp y DeletedMetadataFilters se establezcan en RetrieveMetadataChangesRequest. DeletedMetadataCollection contiene los valores MetadataId de cualquier objeto EntityMetadata, AttributeMetadata o RelationshipMetadataBase que se haya eliminado del sistema en un límite de tiempo. Para obtener más información, consulte Caducidad de los metadatos eliminados.
Use la enumeración DeletedMetadataFilters con RetrieveMetadataChangesRequest.DeletedMetadataFilters para limitar la información a aquellos tipos de metadatos que le interesan. La enumeración DeletedMetadataFilters proporciona las siguientes opciones:
DeletedMetadataFilters.Entity (predeterminado)
DeletedMetadataFilters.Attribute
DeletedMetadataFilters.Relationship
DeletedMetadataFilters.Label
DeletedMetadataFilters.OptionSet
También usará la enumeración DeletedMetadataFilters como clave para RetrieveMetadataChangesResponse .DeletedMetadata para filtrar los valores de
GUID
que se encuentran en la propiedad RetrieveMetadataChangesResponse .DeletedMetadata.Cuando diseñe una memoria caché de metadatos, le conviene usar MetadataId para cada elemento de modo que pueda identificar los elementos eliminados de los metadatos y quitarlos.
Caducidad de los metadatos eliminados
Todo elemento de metadatos que se elimina recibe un seguimiento durante un período de tiempo limitado especificado por el valor Organization.ExpireSubscriptionsInDays
. De manera predeterminada, el valor es 90 días. Si el valor RetrieveMetadataChangesRequest.ClientVersionStamp indica que la última consulta de metadatos se realizó antes de la fecha de vencimiento, el servicio producirá un error ExpiredVersionStamp
(0x80044352). Cuando se recuperan datos para actualizar una memoria caché de metadatos existente, debe intentar capturar este error y estar preparado para reiniciar la memoria caché con los resultados de una segunda solicitud pasada sin ClientVersionStamp. El error ExpiredVersionStamp
también se genera cuando los cambios en el servidor, como cambios en el valor ExpireSubscriptionsInDays
, afectan exactamente al seguimiento de los metadatos eliminados.
En el siguiente ejemplo se pasa ClientVersionStamp y recoge el ExpiredVersionStamp
. Si se captura el error, la memoria caché se reinicializa pasando una nueva solicitud con ClientVersionStamp establecido en 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;
}
Rendimiento cuando se recuperan metadatos eliminados
Cuando se elimina un elemento de metadatos, se guarda en la base de datos y no en la memoria caché de metadatos de Dynamics 365 Customer Engagement. Aunque los metadatos eliminados se limiten únicamente a MetadataId y al tipo del elemento de metadatos, el acceso a la base de datos es una operación que requerirá más recursos del servidor que una simple consulta de los cambios.
Vea también
Escribir aplicaciones y extensiones de servidor
Uso sin conexión de los servicios de Dynamics 365 Customer Engagement
Ejemplo: consultar metadatos y detectar cambios
Ampliar el modelo de los metadatos para Dynamics 365 Customer Engagement
Personalizar metadatos de entidad
Personalizar metadatos de atributos de entidad
Personalizar metadatos de relación de entidad