샘플: 쿼리 메타데이터 및 변경 검색
게시 날짜: 2017년 1월
적용 대상: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online
이 샘플 코드는 Microsoft Dynamics 365(온라인 및 온-프레미스)용입니다.Microsoft Dynamics CRM SDK 패키지를 다운로드합니다.
이 샘플 코드는 다운로드 패키지의 다음 위치에서 확인할 수 있습니다.
SampleCode\CS\Metadata\MetadataQuery\MetadataQuerySample.cs
요구 사항
이 SDK에서 제공된 샘플 코드를 실행하기 위한 요구 사항에 대한 자세한 내용은 샘플 및 도우미 코드 사용을 참조하십시오.
보여 주기
이 샘플에서는 Microsoft.Xrm.Sdk.Metadata.Query 및 Microsoft.Xrm.Sdk.Metadata 네임스페이스의 클래스와 메서드를 사용하여 메타데이터의 특정 항목에 대해 쿼리한 후 조기 메타데이터의 변경 내용을 추적하는 방법을 보여 줍니다.
예제
이 샘플은 특정 엔터티에 대한 모든 옵션 집합 옵션 레이블의 메모리 내 캐시를 만듭니다. 그런 다음 옵션 집합 특성이 포함된 사용자 지정 엔터티가 추가되고 옵션 집합 옵션 레이블의 캐시가 업데이트됩니다. 그런 다음 사용자 지정 옵션 집합 특성 중 하나에 단일 옵션이 추가되고 이를 추가하기 위해 캐시가 업데이트됩니다. 마지막으로 사용자 지정 엔터티가 삭제되고 옵션 집합 옵션 레이블의 캐시가 이 변경 내용을 반영하기 위해 업데이트됩니다.
샘플은 완료되면 다음과 유사한 출력을 표시합니다.
엔터티 5개에 대해 옵션 레이블 463개가 캐시에 추가되었습니다.
ClientVersionStamp: 296297!10/22/2012 21:41:57
sample_ExampleOptionSet라는 사용자 지정 옵션 집합 특성을 사용하여 sample_SampleEntityForMetadataQuery라는 사용자 지정 엔터티 추가
엔터티 1개에 대해 옵션 레이블 8개가 캐시에 추가되었습니다.
옵션 레이블 471개 캐시됨
제거된 옵션 레이블이 없습니다.
ClientVersionStamp: 296646!10/22/2012 21:42:06
sample_ExampleOptionSet 특성 옵션에 추가 옵션을 추가합니다.
엔터티 1개에 대해 옵션 레이블 1개가 캐시에 추가되었습니다.
옵션 레이블 472개 캐시됨
제거된 옵션 레이블이 없습니다.
ClientVersionStamp: 296649!10/22/2012 21:42:16
현재 옵션: 472
sample_SampleEntityForMetadataQuery 사용자 지정 엔터티 삭제
캐시에 추가된 옵션 레이블이 없습니다.
옵션 레이블 9개 제거됨
현재 캐시된 총 옵션 레이블 463개
ClientVersionStamp: 297079!10/22/2012 21:42:34
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.IO;
using System.Collections.Generic;
// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
// located in the SDK\bin folder of the SDK download.
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Metadata.Query;
namespace Microsoft.Crm.Sdk.Samples
{
public class MetadataQuerySample
{
#region Class Level Members
private OrganizationServiceProxy _serviceProxy;
private IOrganizationService _service;
private List<OptionSetOption> _optionLabelList = new List<OptionSetOption>();
private String _customEntitySchemaName = "sample_SampleEntityForMetadataQuery";
private String _customAttributeSchemaName = "sample_ExampleOptionSet";
private Guid _userId;
private int _languageCode;
#endregion Class Level Members
/// <summary>
/// This method connects to the Organization _service.
/// </summary>
/// <param name="serverConfig">Contains server connection information.</param>
public void Run(ServerConnection.Configuration serverConfig)
{
try
{
// Connect to the Organization _service.
// The using statement assures that the _service proxy will be properly disposed.
using (_serviceProxy = new OrganizationServiceProxy(serverConfig.OrganizationUri, serverConfig.HomeRealmUri, serverConfig.Credentials, serverConfig.DeviceCredentials))
{
// This statement is required to enable early-bound type support.
_serviceProxy.EnableProxyTypes();
_service = (IOrganizationService)_serviceProxy;
_userId = ((WhoAmIResponse)_service.Execute(new WhoAmIRequest())).UserId;
_languageCode = RetrieveUserUILanguageCode(_userId);
// 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);
//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 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 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);
//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);
//Add option labels to the cache and display the changes
addOptionLabelsToCache(initialRequest.EntityMetadata, false);
String ClientVersionStamp = initialRequest.ServerVersionStamp;
Console.WriteLine("{0} option labels for {1} entities added to the cache.",
_optionLabelList.Count,
initialRequest.EntityMetadata.Count);
Console.WriteLine("");
Console.WriteLine("ClientVersionStamp: {0}", ClientVersionStamp);
Console.WriteLine("");
//Add new custom entity with optionset
Console.WriteLine("Adding a custom entity named {0} with a custom optionset attribute named : {1}",
_customEntitySchemaName, _customAttributeSchemaName);
Console.WriteLine("");
addCustomEntityWithOptionSet();
//Publishing isn't necessary when adding a custom entity
//Add new option labels to the cache and display the results
ClientVersionStamp = updateOptionLabelList(entityQueryExpression, ClientVersionStamp);
Console.WriteLine("ClientVersionStamp: {0}", ClientVersionStamp);
Console.WriteLine("");
//Add a new option to the custom optionset in the custom entity and publish the custom entity
Console.WriteLine("Adding an additional option to the {0} attribute options.",
_customAttributeSchemaName);
Console.WriteLine("");
addOptionToCustomEntityOptionSet();
//It is necessary to publish updates to metadata. Create and Delete operations are published automatically.
publishUpdatedEntity();
//Add the new option label to the cache and display the results
ClientVersionStamp = updateOptionLabelList(entityQueryExpression, ClientVersionStamp);
Console.WriteLine("ClientVersionStamp: {0}", ClientVersionStamp);
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("Current Options: {0}", _optionLabelList.Count.ToString());
Console.WriteLine("");
//Delete the custom entity
Console.WriteLine("");
Console.WriteLine("Deleting the {0} custom entity",
_customEntitySchemaName);
Console.WriteLine("");
deleteCustomEntityWithOptionSet();
//Publishing isn't necessary when deleting a custom entity
//Retrieve metadata changes to remove option labels from deleted attributes and display the results
ClientVersionStamp = updateOptionLabelList(entityQueryExpression, ClientVersionStamp);
Console.WriteLine("ClientVersionStamp: {0}", ClientVersionStamp);
Console.WriteLine("");
}
}
// Catch any _service fault exceptions that Microsoft Dynamics CRM throws.
catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
{
// You can handle an exception here or pass it back to the calling method.
throw;
}
}
protected RetrieveMetadataChangesResponse getMetadataChanges(
EntityQueryExpression entityQueryExpression,
String clientVersionStamp,
DeletedMetadataFilters deletedMetadataFilter)
{
RetrieveMetadataChangesRequest retrieveMetadataChangesRequest = new RetrieveMetadataChangesRequest()
{
Query = entityQueryExpression,
ClientVersionStamp = clientVersionStamp,
DeletedMetadataFilters = deletedMetadataFilter
};
return (RetrieveMetadataChangesResponse)_service.Execute(retrieveMetadataChangesRequest);
}
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 void addOptionLabelsToCache(EntityMetadataCollection entityMetadataCollection, Boolean showChanges)
{
List<OptionSetOption> changes = new List<OptionSetOption>();
foreach (EntityMetadata em in entityMetadataCollection)
{
foreach (AttributeMetadata am in em.Attributes)
{
switch (am.AttributeType)
{
case AttributeTypeCode.Boolean:
BooleanAttributeMetadata booleanAttribute = (BooleanAttributeMetadata)am;
//Labels will not be included if they aren't new
if (booleanAttribute.OptionSet.FalseOption.Label.UserLocalizedLabel != null)
{
changes.Add(new OptionSetOption(
(Guid)booleanAttribute.OptionSet.MetadataId,
0,
booleanAttribute.OptionSet.FalseOption.Label.UserLocalizedLabel.Label)
);
}
//Labels will not be included if they aren't new
if (booleanAttribute.OptionSet.TrueOption.Label.UserLocalizedLabel != null)
{
changes.Add(new OptionSetOption(
(Guid)booleanAttribute.OptionSet.MetadataId,
1,
booleanAttribute.OptionSet.TrueOption.Label.UserLocalizedLabel.Label));
}
break;
default:
EnumAttributeMetadata optionsetAttribute = (EnumAttributeMetadata)am;
foreach (OptionMetadata option in optionsetAttribute.OptionSet.Options)
{
//Labels will not be included if they aren't new
if (option.Label.UserLocalizedLabel != null)
{
changes.Add(new OptionSetOption(
(Guid)optionsetAttribute.OptionSet.MetadataId,
(int)option.Value,
option.Label.UserLocalizedLabel.Label));
}
}
break;
}
}
}
_optionLabelList.AddRange(changes);
if (showChanges)
{
if (changes.Count > 0)
{
Console.WriteLine("{0} option labels for {1} entities were added to the cache.", changes.Count, entityMetadataCollection.Count);
Console.WriteLine("{0} Option Labels cached", _optionLabelList.Count);
}
else
{ Console.WriteLine("No option labels were added to the cache."); }
}
}
protected void removeOptionLabelsFromCache(DeletedMetadataCollection DeletedMetadata, Boolean showChanges)
{
List<OptionSetOption> optionSetOptionsToRemove = new List<OptionSetOption>();
if (DeletedMetadata.Keys.Contains(DeletedMetadataFilters.OptionSet))
{
DataCollection<Guid> optionsetmetadataids = (DataCollection<Guid>)DeletedMetadata[DeletedMetadataFilters.OptionSet];
foreach (Guid metadataid in optionsetmetadataids)
{
foreach (OptionSetOption oso in _optionLabelList)
{
if (metadataid == oso.optionsetId)
{
optionSetOptionsToRemove.Add(oso);
}
}
}
}
foreach (OptionSetOption option in optionSetOptionsToRemove)
{
_optionLabelList.Remove(option);
}
if (showChanges)
{
if (optionSetOptionsToRemove.Count > 0)
{
Console.WriteLine("{0} Option Labels removed", optionSetOptionsToRemove.Count);
Console.WriteLine("{0} Total Option Labels currently cached", _optionLabelList.Count);
Console.WriteLine("");
}
else
{
Console.WriteLine("No Option Labels removed.");
Console.WriteLine("");
}
}
}
protected void addCustomEntityWithOptionSet()
{
String primaryAttributeSchemaName = "sample_SampleEntityForMetadataQueryName";
CreateEntityRequest createEntityRequest = new CreateEntityRequest
{
//Define the entity
Entity = new EntityMetadata
{
SchemaName = _customEntitySchemaName,
LogicalName = _customEntitySchemaName.ToLower(),
DisplayName = new Label("Entity for MetadataQuery Sample", _languageCode),
DisplayCollectionName = new Label("Entity for MetadataQuery Sample", _languageCode),
Description = new Label("An entity created for the MetadataQuery Sample", _languageCode),
OwnershipType = OwnershipTypes.UserOwned,
IsVisibleInMobile = new BooleanManagedProperty(true),
IsActivity = false,
},
// Define the primary attribute for the entity
PrimaryAttribute = new StringAttributeMetadata
{
SchemaName = primaryAttributeSchemaName,
LogicalName = primaryAttributeSchemaName.ToLower(),
RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
MaxLength = 100,
Format = StringFormat.Text,
DisplayName = new Label("Entity for MetadataQuery Sample Name", _languageCode),
Description = new Label("The primary attribute for the Bank Account entity.", _languageCode)
}
};
_service.Execute(createEntityRequest);
//PublishXmlRequest publishXmlRequest = new PublishXmlRequest { ParameterXml = String.Format("<importexportxml><entities><entity>{0}</entity></entities></importexportxml>", _customEntitySchemaName.ToLower()) };
//_service.Execute(publishXmlRequest);
//Add an optionset attribute
CreateAttributeRequest createAttributeRequest = new CreateAttributeRequest
{
EntityName = _customEntitySchemaName.ToLower(),
Attribute = new PicklistAttributeMetadata
{
SchemaName = _customAttributeSchemaName,
DisplayName = new Label("Example OptionSet for MetadataQuery Sample", _languageCode),
RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
OptionSet = new OptionSetMetadata
{
IsGlobal = false,
OptionSetType = OptionSetType.Picklist,
Options = {
new OptionMetadata(new Label("First Option",_languageCode),null),
new OptionMetadata(new Label("Second Option",_languageCode),null),
new OptionMetadata(new Label("Third Option",_languageCode),null),
new OptionMetadata(new Label("Fourth Option",_languageCode),null)
}
}
}
};
_service.Execute(createAttributeRequest);
}
protected void addOptionToCustomEntityOptionSet()
{
InsertOptionValueRequest insertOptionValueRequest =
new InsertOptionValueRequest
{
AttributeLogicalName = _customAttributeSchemaName.ToLower(),
EntityLogicalName = _customEntitySchemaName.ToLower(),
Label = new Label("Fifth Option", _languageCode)
};
_service.Execute(insertOptionValueRequest);
}
protected void deleteCustomEntityWithOptionSet()
{
DeleteEntityRequest request = new DeleteEntityRequest()
{
LogicalName = _customEntitySchemaName.ToLower(),
};
_service.Execute(request);
}
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;
}
protected void publishUpdatedEntity()
{
PublishXmlRequest publishXmlRequest = new PublishXmlRequest
{
ParameterXml = "<importexportxml><entities><entity>" + _customEntitySchemaName.ToLower() + "</entity></entities></importexportxml>"
};
_service.Execute(publishXmlRequest);
}
#region Main method
/// <summary>
/// Based on the Main() method used by most SDK samples.
/// </summary>
/// <param name="args"></param>
static public void Main(string[] args)
{
try
{
// Obtain the target organization's Web address and client logon
// credentials from the user.
ServerConnection serverConnect = new ServerConnection();
ServerConnection.Configuration config = serverConnect.GetServerConfiguration();
MetadataQuerySample app = new MetadataQuerySample();
app.Run(config);
}
catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
Console.WriteLine("Message: {0}", ex.Detail.Message);
Console.WriteLine("Trace: {0}", ex.Detail.TraceText);
Console.WriteLine("Inner Fault: {0}",
null == ex.Detail.InnerFault ? "Has Inner Fault" : "No Inner Fault");
}
catch (System.TimeoutException ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine("Message: {0}", ex.Message);
Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
Console.WriteLine("Inner Fault: {0}",
null == ex.InnerException.Message ? "Has Inner Fault" : "No Inner Fault");
}
catch (System.Exception ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine(ex.Message);
// Display the details of the inner exception.
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.Message);
FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
if (fe != null)
{
Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
Console.WriteLine("Message: {0}", fe.Detail.Message);
Console.WriteLine("Trace: {0}", fe.Detail.TraceText);
Console.WriteLine("Inner Fault: {0}",
null == fe.Detail.InnerFault ? "Has Inner Fault" : "No Inner Fault");
}
}
}
//Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
//SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.
finally
{
Console.WriteLine("Press <Enter> to exit.");
Console.ReadLine();
}
}
#endregion Main method
}
public class OptionSetOption {
public OptionSetOption(Guid OptionsetId, int OptionValue, String Label)
{
this._optionsetId = OptionsetId;
this._optionValue = OptionValue;
this._label = Label;
}
private Guid _optionsetId;
private int _optionValue;
private String _label;
public Guid optionsetId { get { return this._optionsetId; } }
public int optionValue { get { return this._optionValue; } }
public String lable { get { return this._label; } }
}
}
참고 항목
Microsoft Dynamics 365
© 2017 Microsoft. All rights reserved. 저작권 정보