Как запросить метаданные для всех существующих полей
Мы хотим, чтобы клиент отправлял сообщение в конечную точку, например:
[Route("Account", Name = "CreateAccount", Order = 1)]
[HttpPost]
public Account CreateAccount([FromBody] Account account)
{
var newAccount = _accountService.CreateAccountEntity(account);
return newAccount;
}
Мы знаем, что это можно сделать:
POST [Organization URI]/api/data/v8.2/accounts HTTP/1.1
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
{
"name": "Sample Account",
"creditonhold": false,
"address1_latitude": 47.639583,
"description": "This is the description of the sample account",
"revenue": 5000000,
"accountcategorycode": 1
}
Как мы можем предоставить потребителю требования к каждому сообщению/помещению?
Чтобы выразить это словами, , если мне нужно обновить запись на пользовательском или базовом объекте с использованием веб-API, как это предусмотрено CRM 2016, как узнать, какие поля необходимы для создания или обновления объекта?
Edit:
Я попытался использовать подход Хэнка, и это не возвращало никаких метаданных в сущности:
![введите описание изображения здесь]()
Ответы
Ответ 1
Вы можете запросить метаданные Dynamics 365 с использованием конечной точки WebApi, как показано в SDK.
Например, чтобы получить все атрибуты (включая уровень требований) для объекта account
:
GET [Organization URI]/api/data/v8.2/EntityDefinitions(LogicalName='account')/Attributes HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
Content-Type: application/json; charset=utf-8
Ответ 2
Вы можете использовать RetrieveEntityRequest
для получения метаданных для объекта.
В следующем примере извлекаются метаданные для учетной записи сущности:
var request = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.Entity | EntityFilters.Attributes,
LogicalName = "account"
};
var response = (RetrieveEntityResponse)_serviceProxy.Execute(request);
Объект ответа содержит свойство EntityMetadata
. В нем вы можете найти параметр атрибута, например:
EntityMetadata metadata = reponse.EntityMetadata;
bool isRevenueRequired = metadata.Attributes
.First<AttributeMetadata>(a -> a.LogicalName == "revenue")
.RequiredLevel.Value == AttributeRequiredLevel.ApplicationRequired;
Ответ 3
Чтобы получить все метаданные для объекта, использующего конечную точку SOAP, вы можете использовать RetrieveEntityRequest:
var request = new RetrieveEntityRequest
{
EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.All,
LogicalName = "account"
}
var response = (RetrieveEntityResponse)organizationService.Execute(request);
EntityFiters - это перечисление, которое позволяет указать, какие метаданные вы пытаетесь получить:
[Flags]
public enum EntityFilters
{
//
// Summary:
// Use this to retrieve only entity information. Equivalent to EntityFilters.Default.
// Value = 1.
Entity = 1,
//
// Summary:
// Use this to retrieve only entity information. Equivalent to EntityFilters.Entity.
// Value = 1.
Default = 1,
//
// Summary:
// Use this to retrieve entity information plus attributes for the entity. Value
// = 2.
Attributes = 2,
//
// Summary:
// Use this to retrieve entity information plus privileges for the entity. Value
// = 4.
Privileges = 4,
//
// Summary:
// Use this to retrieve entity information plus entity relationships for the entity.
// Value = 8.
Relationships = 8,
//
// Summary:
// Use this to retrieve all data for an entity. Value = 15.
All = 15
}
Это перечисление флага, поэтому вы можете использовать его следующим образом:
var request = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.Privileges | EntityFilters.Entity,
LogicalName = "account"
}
Или просто используйте значение All
, чтобы получить все необходимые метаданные. В вашей попытке вам не удалось получить метаданные, потому что вы запрашивали только метаданные Entity, и вас интересуют метаданные атрибутов.
Итак, взяв фрагмент кода в качестве базы, я бы использовал его следующим образом:
[Route("Account", Name = "CreateAccount", Order = 1)]
[HttpPost]
public Account CreateAccount([FromBody] Account account)
{
VerifyRequiredFields(account);
var newAccount = _accountService.CreateAccountEntity(account);
return newAccount;
}
private void VerifyRequiredFields(Account account)
{
var response = GetEntityMetadata(account);
var requiredAttributes = response.EntityMetadata.Attributes.Where(a => a.RequiredLevel?.Value == AttributeRequiredLevel.SystemRequired);
foreach(var requiredAttribute in requiredAttributes)
{
if(CheckIfValueIsProvided(requiredAttribute.LogicalName, account))
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"You are missing required value {requiredAttribute.LogicalName}"));
}
}
}
Метод GetEntityMetadata
просто выполняет то, что было в предыдущем примере, поэтому вызываем RetrieveEntityRequest
и возвращаем RetrieveEntityResponse
.
Конечно, реализация метода CheckIfValueIsProvided
зависит от того, как определяется класс модели вашей учетной записи, но, вероятно, вам понадобится какое-то сопоставление между вашей моделью и моделью CRM Entity (чтобы знать, как сопоставить, например, поле "номер учетной записи" для некоторого поля в вашей модели). Это далеко выходит за рамки этого вопроса, но я считаю, что вы уже знаете достаточно, чтобы начать.
Просто помните, что это только пример. Вы не должны держать эту логику внутри своего класса контроллера, вы должны перенести ее в какой-то класс утилиты, который можно повторно использовать в разных контроллерах. Метаданные не часто меняются (и вы, вероятно, контролируете эти изменения), поэтому вы, вероятно, захотите также кэшировать метаданные где-то в своем веб-приложении и т.д. Я надеюсь, что у вас уже есть идея, что можно сделать, но весь дизайн если логика - это еще одна история.
Если вы хотите сделать это с помощью JavaScript, вы должны, вероятно, придерживаться webAPI:
http://CRMADDRESS/api/data/v8.2/EntityDefinitions(LogicalName='account')/Attributes?$select=LogicalName,RequiredLevel
Вы получите то, что хотите (имя атрибута и его требуемый уровень). Это будет выглядеть так:
{
"LogicalName":"preferredcontactmethodcodename","RequiredLevel":{
"Value":"None","CanBeChanged":false,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"8663b910-af86-4dea-826e-8222706372f4"
},{
"@odata.type":"#Microsoft.Dynamics.CRM.StringAttributeMetadata","LogicalName":"emailaddress3","RequiredLevel":{
"Value":"None","CanBeChanged":true,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"97fb4aae-ea5d-427f-9b2b-9a6b9754286e"
},{
"@odata.type":"#Microsoft.Dynamics.CRM.StringAttributeMetadata","LogicalName":"emailaddress2","RequiredLevel":{
"Value":"None","CanBeChanged":true,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"98b09426-95ab-4f21-87a0-f6775f2b4210"
},{
"@odata.type":"#Microsoft.Dynamics.CRM.StringAttributeMetadata","LogicalName":"emailaddress1","RequiredLevel":{
"Value":"None","CanBeChanged":true,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"b254ab69-de5a-4edb-8059-bdeb6863c544"
},{
"@odata.type":"#Microsoft.Dynamics.CRM.StringAttributeMetadata","LogicalName":"masteraccountidyominame","RequiredLevel":{
"Value":"None","CanBeChanged":false,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"a15dedfc-9382-43ac-8d10-7773aa3eefeb"
},{
"@odata.type":"#Microsoft.Dynamics.CRM.StringAttributeMetadata","LogicalName":"address1_city","RequiredLevel":{
"Value":"None","CanBeChanged":true,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"ca8d0a94-8569-4154-b511-718e11635449"
},{
"@odata.type":"#Microsoft.Dynamics.CRM.LookupAttributeMetadata","LogicalName":"slaid","RequiredLevel":{
"Value":"None","CanBeChanged":true,"ManagedPropertyLogicalName":"canmodifyrequirementlevelsettings"
},"MetadataId":"6bdcd7f1-5865-4fef-91b0-676824b18641"
}
Вы можете использовать это, чтобы проверить запрос на стороне клиента, чтобы дать пользователю указание, что он пропускает важные данные, прежде чем он отправит запрос на сервер.