Как запросить метаданные для всех существующих полей

Мы хотим, чтобы клиент отправлял сообщение в конечную точку, например:

    [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"
}

Вы можете использовать это, чтобы проверить запрос на стороне клиента, чтобы дать пользователю указание, что он пропускает важные данные, прежде чем он отправит запрос на сервер.