检索并检测元数据更改

 

发布日期: 2016年11月

适用于: Dynamics CRM 2015

使用 Microsoft.Xrm.Sdk.Metadata.Query 命名空间中的类以及 RetrieveMetadataChangesRequestRetrieveMetadataChangesResponse 类,可以构建高效的元数据查询和捕获元数据随时间进行的更改。

本文中引用的所有代码示例均可在示例:查询元数据并检测元数据更改中找到。

技术文章使用 JavaScript 查询元数据 提供了 JavaScript 库,以使用客户端代码中的对象和消息。

本主题内容

使用元数据的策略

仅检索所需的元数据

检索新的或已更改的元数据

检索有关已删除的元数据的信息

使用元数据的策略

通过元数据,可以创建适应 Microsoft Dynamics 365 数据模型更改的应用程序。 元数据对于下列应用程序类型非常重要:

  • 客户端应用程序的 UI

  • 必须将 CRM 数据映射到外部系统的集成工具

  • 开发工具

使用 Microsoft.Xrm.Sdk.Metadata.Query 命名空间的类,可实现中在轻量查询与持久元数据缓存之间存在的设计。

轻量查询

轻量查询的一个示例是,您具有自定义 Web 资源 UI,该 UI 提供一个选择控件以显示 Microsoft Dynamics CRM 选项集(选择列表)属性中的当前选项。 您不需要对这些选项硬编码,因为如果提供的选项已更改,您必须更新该代码。 相反,您可以构建一个仅从元数据检索这些选项的值和标签的查询。

您不必缓存此数据,因为您可以使用 Microsoft.Xrm.Sdk.Metadata.Query 类直接从 Microsoft CRM 应用程序缓存检索此数据。

持久元数据缓存

如果您有一个当与 Microsoft CRM 服务器断开连接时必须能够工作的应用程序,或者对客户端与服务器之间的有限网络带宽很敏感的应用程序(例如移动应用程序),则您需要实施持久元数据缓存。

使用持久元数据缓存,您的应用程序在第一次连接时必须查询所有所需的元数据。 然后,您将该数据保存在应用程序中。 下次应用程序连接到服务器时,您可以仅检索自从上次查询以来不同的内容(这可以极大减少传输数据量),然后当应用程序时加载时将更改合并到元数据缓存中。

对元数据更改的轮询频率取决于应用程序元数据中的预期变更以及应用程序保持运行的时间。 没有任何可用来检测元数据何时发生更改的事件。 对于保存已删除元数据的天数是有限制的,如果请求查找超过该限制所发生的更改,则需要完全重新初始化元数据缓存。 有关详细信息,请参阅已删除的元数据的过期时间。

如果没有任何更改,则查询应当快速响应,不传回任何数据。 但是如果存在更改,特别是如果必须从缓存移除已删除的元数据项,则您会预计到请求可能需要一些额外时间完成。详细信息:检索已删除的元数据时的性能

仅检索所需的元数据

通常在应用程序启动时检索或同步元数据,这可能影响应用程序加载所花费的时间。 当移动应用程序首次检索元数据时,尤其会这样。 仅检索所需的元数据对于创建执行力良好的应用程序非常重要。

EntityQueryExpression 类提供与您创建复杂查询以检索实体数据时使用的 QueryExpression 类一致的结构。 与 RetrieveAllEntitiesRequestRetrieveEntityRequestRetrieveAttributeRequestRetrieveRelationshipRequest 类不同,RetrieveMetadataChangesRequest 包含 Query 参数,该参数接受 EntityQueryExpression 实例,您可以使用该实例指定要返回的数据的除所需属性外的特定条件。 可以使用 RetrieveMetadataChangesRequest 返回通过 RetrieveAllEntitiesRequest 获取的完整元数据集,或者仅返回特定属性的标签。

指定筛选条件

EntityQueryExpression.Criteria 属性接受MetadataFilterExpression,它包含 MetadataConditionExpression 对象的集合,这些对象允许根据特定实体属性值来定义实体属性的筛选条件。 这些条件使用 MetadataConditionOperator,它允许下列运算符:

MetadataFilterExpression 还包括 LogicalOperator,以表示当您评估条件时是否应用 AndOr 逻辑。

并非所有属性都可以用作筛选条件。 只有表示简单数据类型、枚举、BooleanManagedPropertyAttributeRequiredLevelManagedProperty 类型的属性可用于 MetadataFilterExpression。 当指定 BooleanManagedPropertyAttributeRequiredLevelManagedProperty 时,仅评估 Value 属性。

下表列出无法在 MetadataFilterExpression 中使用的 EntityMetadata 属性:

Attributes

Description

DisplayCollectionName

DisplayName

ManyToManyRelationships

ManyToOneRelationships

OneToManyRelationships

Privileges

以下示例显示 MetadataFilterExpression(它返回一组非相交的、要排除的实体列表中未包括的用户负责的实体):



     // 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);


' An array SchemaName values for non-intersect, user-owned entities that should not be returned.
                  Dim excludedEntities() As String =
                      {
                          "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.
Dim EntityFilter As 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))
Dim isVisibileInMobileTrue As New MetadataConditionExpression("IsVisibleInMobile", MetadataConditionOperator.Equals, True)
EntityFilter.Conditions.Add(isVisibileInMobileTrue)

指定所需的属性

属性 Properties 接受 MetadataPropertiesExpression。 如果要返回所有属性,则可以将 MetadataPropertiesExpression.AllProperties 设置为 true,或者可以向 MetadataPropertiesExpression.PropertyNames 提供字符串的集合以定义要在结果中包括的属性。

返回强类型对象将包括所有属性,但是只有那些您请求的属性具有数据。 所有其他属性将为 null,但有以下几个例外:元数据的每一项将包含 MetadataIdLogicalNameHasChanged 值(如果该项存在这些值)。 您不必在请求的 Properties 中指定它们。

如果您不使用托管代码而实际上要分析从 XMLHttpRequest 返回的 responseXML,则您将获得每个属性的元素,但是只有您请求的那些属性将包含数据。 以下 XML 显示当 IsVisibleInMobile 是唯一请求的属性时将返回的联系人实体元数据 xml。

<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>

在未来的版本中,将通过不返回未请求的属性的具有 null 值的元素,来进一步提高效率。 如果您编写代码来分析该 XML,您应该预期相同查询返回的 XML 可减少到只有以下 XML。

<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>

元数据按层次结构返回,就像它在使用 RetrieveAllEntitiesRequest 一样。 若要访问特定属性或关系,您必须创建一个查询来返回其所属的实体。 如果要检索有关特定属性的数据,您必须在 EntityQueryExpression.Properties 中包括 EntityMetadata.Attributes 属性。 若要获取返回的实体关系,您必须包括下列一个或多个 EntityMetadata 属性: ManyToManyRelationshipsManyToOneRelationshipsOneToManyRelationships

以下示例将返回请求的实体 Attributes 属性:


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

'A properties expression to limit the properties to be included with entities
Dim EntityProperties As New MetadataPropertiesExpression() With {.AllProperties = False}
EntityProperties.PropertyNames.AddRange(New String() { "Attributes" })

检索属性元数据

EntityQueryExpression.AttributeQuery 属性接受 AttributeQueryExpression,它定义了为匹配 EntityQueryExpressionCriteriaProperties 的实体返回的属性的 CriteriaProperties

下表列出无法在 MetadataFilterExpression 中使用的 AttributeMetadata 属性

Description

DisplayName

OptionSet

Targets

以下示例将返回的特性限制在具有 OptionSet 的那些特性,并且仅返回这些特性的 OptionSetAttributeType 属性:


//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");

'A condition expresson to return optionset attributes
                  Dim optionsetAttributeTypes() As 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
Dim AttributeFilter As New MetadataFilterExpression(LogicalOperator.Or)
AttributeFilter.Conditions.AddRange(optionsetAttributeTypes)

'A Properties expression to limit the properties to be included with attributes
Dim AttributeProperties As New MetadataPropertiesExpression() With {.AllProperties = False}
AttributeProperties.PropertyNames.Add("OptionSet")
AttributeProperties.PropertyNames.Add("AttributeType")

检索关系元数据

EntityQueryExpression.RelationshipQuery 属性接受 RelationshipQueryExpression,以指定您所需的匹配 EntityQueryExpressionCriteriaProperties 的实体的 CriteriaProperties 实体关系。

在条件中使用 RelationshipType 属性可指定是要返回 ManyToMany 关系还是 OneToMany 关系。

下表列出无法在 MetadataFilterExpression 中使用的关系元数据属性:

OneToManyRelationshipMetadataAssociatedMenuConfiguration

OneToManyRelationshipMetadataCascadeConfiguration

ManyToManyRelationshipMetadataEntity1AssociatedMenuConfiguration

ManyToManyRelationshipMetadataEntity2AssociatedMenuConfiguration

检索标签

最后,EntityQueryExpression.LabelQuery 属性接受 LabelQueryExpression,它允许您指定一个或多个 LCID 整数值以确定要返回的本地化标签。可在区域设置 ID (LCID) 图表中找到有效区域设置 ID 值。 如果组织装有多个语言包,则返回所有语言的标签,除非您指定 LabelQuery

以下示例定义 LabelQueryExpression,它将标签限制在代表用户首选语言的那些标签。


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);

Private _userId As Guid
Private _languageCode As Integer

_userId = (CType(_service.Execute(New WhoAmIRequest()), WhoAmIResponse)).UserId
_languageCode = RetrieveUserUILanguageCode(_userId)

 Protected Function RetrieveUserUILanguageCode(ByVal userId As Guid) As Integer
  Dim userSettingsQuery As New QueryExpression("usersettings")
  userSettingsQuery.ColumnSet.AddColumns("uilanguageid", "systemuserid")
  userSettingsQuery.Criteria.AddCondition("systemuserid", ConditionOperator.Equal, userId)
  Dim userSettings As EntityCollection = _service.RetrieveMultiple(userSettingsQuery)
  If userSettings.Entities.Count > 0 Then
Return CInt(Fix(userSettings.Entities(0)("uilanguageid")))
  End If
  Return 0
 End Function


'A label query expression to limit the labels returned to only those for the user's preferred language
Dim labelQuery As New LabelQueryExpression()
labelQuery.FilterLanguages.Add(_languageCode)

检索新的或已更改的元数据

RetrieveMetadataChangesResponse 类返回强类型 EntityMetadataCollection,它包含请求的数据。RetrieveMetadataChangesResponse 类还提供 ServerVersionStamp 值,您可以将该值传递到以后的请求中的 RetrieveMetadataChangesRequest.ClientVersionStamp 属性。 如果 ClientVersionStamp 属性包括了值,则仅返回匹配 EntityQueryExpression 的、自从检索 ClientVersionStamp 以来已更改的数据。 其唯一例外是当您的 EntityQueryExpression.Properties 包括 EntityMetadata.Privileges 时。 无论 ClientVersionStamp 如何,将始终返回权限。 这样,您的应用程序可以确定自从您上次查询元数据以来,是否发生了您关注的任何重要更改。 然后,您可以将所有新的或已更改的元数据合并到持久元数据缓存中,以便应用程序能够避免下载不需要的元数据时的性能问题。

HasChanged 属性提供一种方法来检测元数据项中哪些子元素已发生更改。 因为所有元数据都作为包含元数据项的一部分返回,所以当 OptionMetadata 的标签更改时,将返回包含 EntityMetadataAttributeMetadataOptionSetMetadata 属性。 但是,对于这些包含元数据项,HasChanged 属性将为 false。 只有 OptionMetadataHasChanged 属性将为 true。

以下示例通过定义 EntityQueryExpression 并执行 ClientVersionStamp 设置为 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);

}

'An entity query expression to combine the filter expressions and property expressions for the query.
                  Dim entityQueryExpression_Renamed As New EntityQueryExpression() With
                      {
                          .Criteria = EntityFilter,
                          .Properties = EntityProperties,
                          .AttributeQuery = New AttributeQueryExpression() With
                                            {
                                                .Criteria = AttributeFilter,
                                                .Properties = AttributeProperties
                                            },
                          .LabelQuery = labelQuery
                      }

'Retrieve the metadata for the query without a ClientVersionStamp
                  Dim initialRequest As RetrieveMetadataChangesResponse =
                      getMetadataChanges(entityQueryExpression_Renamed, Nothing, DeletedMetadataFilters.OptionSet)

Protected Function getMetadataChanges(ByVal entityQueryExpression_Renamed As EntityQueryExpression,
                                      ByVal clientVersionStamp As String,
                                      ByVal deletedMetadataFilter As DeletedMetadataFilters) As RetrieveMetadataChangesResponse
    Dim retrieveMetadataChangesRequest_Renamed As New RetrieveMetadataChangesRequest() With
        {
            .Query = entityQueryExpression_Renamed,
            .ClientVersionStamp = clientVersionStamp,
            .DeletedMetadataFilters = deletedMetadataFilter
        }

    Return CType(_service.Execute(retrieveMetadataChangesRequest_Renamed), RetrieveMetadataChangesResponse)

End Function

检索有关已删除的元数据的信息

当在 RetrieveMetadataChangesRequest 上设置 ClientVersionStampDeletedMetadataFilters 属性时,RetrieveMetadataChangesResponse.DeletedMetadata 属性将返回 DeletedMetadataCollectionDeletedMetadataCollection 包含在某个时间限制下已从系统中删除的所有EntityMetadataAttributeMetadataRelationshipMetadataBase 对象的 MetadataId 值。 有关详细信息,请参阅已删除的元数据的过期时间。

DeletedMetadataFilters 枚举与RetrieveMetadataChangesRequest.DeletedMetadataFilters 结合使用,可以将信息限制在您感兴趣的那些元数据类型。 DeletedMetadataFilters 枚举提供以下选项:

您也可以将 DeletedMetadataFilters 枚举作为 RetrieveMetadataChangesResponse.DeletedMetadata 的键,以筛选 RetrieveMetadataChangesResponse.DeletedMetadata 属性中的 GUID 值。

当您设计元数据缓存时,需要针对每个项目使用 MetadataId,以便确定已删除的元数据项并移除它们。

已删除的元数据的过期时间

所有已删除的元数据都将被跟踪 Organization.ExpireSubscriptionsInDays 值所指定的一段有限时间。 默认情况下该值设置为 90 天。 如果 RetrieveMetadataChangesRequestClientVersionStamp 值指示上次元数据查询是在到期日期之前,则服务将引发 ExpiredVersionStamp 错误(0x80044352)。当检索要刷新的数据和现有元数据缓存时,应始终尝试捕捉此错误,并准备用传递的另一个请求的结果(不带 ClientVersionStamp)来重新初始化元数据缓存。 当服务器上的更改(例如对 ExpireSubscriptionsInDays 值的更改)影响对已删除元数据的精确跟踪时,也会引发 ExpiredVersionStamp 错误。

以下示例传递 ClientVersionStamp 并捕获 ExpiredVersionStamp。 如果捕获到错误,则通过传入 ClientVersionStamp 设置为 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;
}

Protected Function updateOptionLabelList(ByVal entityQueryExpression_Renamed As EntityQueryExpression,
                                         ByVal clientVersionStamp As String) As String
    'Retrieve metadata changes and add them to the cache
    Dim updateResponse As RetrieveMetadataChangesResponse
    Try
        updateResponse = getMetadataChanges(entityQueryExpression_Renamed, clientVersionStamp, DeletedMetadataFilters.OptionSet)
        addOptionLabelsToCache(updateResponse.EntityMetadata, True)
        removeOptionLabelsFromCache(updateResponse.DeletedMetadata, True)

    Catch ex As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault)
        ' Check for ErrorCodes.ExpiredVersionStamp (0x80044352)
        ' Will occur when the timestamp exceeds the Organization.ExpireSubscriptionsInDays value, which is 90 by default.
        'INSTANT VB TODO TASK: There is no VB equivalent to 'unchecked' in this context:
        If ex.Detail.ErrorCode = CInt(&amp;H80044352) Then
            'reinitialize cache
            _optionLabelList.Clear()

            updateResponse = getMetadataChanges(entityQueryExpression_Renamed, Nothing, DeletedMetadataFilters.OptionSet)
            'Add them to the cache and display the changes
            addOptionLabelsToCache(updateResponse.EntityMetadata, True)

        Else
            Throw ex
        End If

    End Try
    Return updateResponse.ServerVersionStamp
End Function

检索已删除的元数据时的性能

删除元数据项后,它会保存在数据库中,不保存在 Microsoft CRM 元数据缓存中。 虽然已删除的元数据限制在 MetadataId 和元数据项类型,但是访问数据库是一项将需要比仅查询更改更多的服务器资源的操作。

另请参阅

编写应用程序和服务器扩展
脱机使用 Microsoft Dynamics CRM 2015 服务
示例:查询元数据并检测元数据更改
扩展元数据模型
自定义实体元数据
自定义实体属性元数据
自定义实体关系元数据
使用 JavaScript 查询元数据

© 2017 Microsoft。 保留所有权利。 版权