【问题标题】:How to query metadata for all existing fields如何查询所有现有字段的元数据
【发布时间】:2017-09-17 19:17:54
【问题描述】:

我们希望让客户端能够发布到一个端点,例如:

    [Route("Account", Name = "CreateAccount", Order = 1)]
    [HttpPost]
    public Account CreateAccount([FromBody] Account account)
    {
        var newAccount = _accountService.CreateAccountEntity(account);
        return newAccount;
    }

我们know表示可以这样做:

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
}

我们如何向消费者展示每个 post/put 的要求?

换个说法,如果我需要使用 CRM 2016 提供的 Web API 更新自定义或基础实体上的记录,我如何知道创建或更新实体需要哪些字段?

编辑: 我尝试了 Hank 的方法,但这并没有返回实体上的任何元数据:

【问题讨论】:

  • 您失败了,因为您使用了错误的实体元数据过滤器。检查我的答案以获得澄清

标签: c# .net visual-studio dynamics-crm dynamics-crm-2016


【解决方案1】:

您可以使用 WebApi 端点查询 Dynamics 365 元数据,如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

【讨论】:

  • 非常感谢,所以如果我想使用 web api 来创建或更新数据,你能举个例子来说明我会怎么做吗?我会先查询元数据,然后看看需要什么?一个有解释的例子会很棒
  • @MeggieLuski - 请将此作为新问题发布!如果它回答了您发布的问题,请记住将此答案标记为正确。
【解决方案2】:

为了使用 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 值来获取所有必要的元数据。在您的尝试中,您未能检索到元数据,因为您只询问了实体元数据并且您对属性元数据感兴趣。

因此,以您的代码 sn-p 为基础,我将按以下方式使用它:

[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 的实现取决于您的 Account 模型类的定义方式,但您可能需要在模型和 CRM 实体模型之间进行某种映射(了解如何将例如字段“accountnumber”映射到某个字段在您的模型中)。这远远超出了这个问题的范围,但我相信你已经足够了解了。 请记住,这只是一个例子。您不应该将此逻辑保留在您的控制器类中,您应该将其移动到可以在不同控制器中重用的某个实用程序类中。元数据不会经常更改(并且您可能可以控制这些更改),因此您可能还希望将元数据缓存在 Web 应用程序等的某个位置。我希望您已经知道可以做什么,但是整个设计如果逻辑是另一个故事。

如果您想通过 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"
}

您可以使用它来验证客户端的请求,在用户向服务器发送请求之前提示他丢失了重要数据。

【讨论】:

  • 非常感谢,所以如果我想使用 web api 来创建或更新数据,你能举个例子来说明我会怎么做吗?我会先查询元数据,然后看看需要什么?一个有解释的例子会很棒
  • @MeggieLuski 我修改了我的答案,给你一个提示如何完成
  • "当然,方法 CheckIfValueIsProvided 的实现取决于您的 Account 模型类是如何定义的,但您可能需要在模型和 CRM 实体模型之间进行某种映射(例如,要知道如何映射字段“帐号”到您模型中的某个字段)。“ - 对不起,我不明白为什么我们应该在 crm 模型和客户端传入的内容之间实现一个内部层。增加的复杂性有什么意义?跨度>
  • 您可以使用元数据构建有效的请求。但是您的问题(我仍然指的是它,因为我不想在这里偏离主题)是您想检查是否填充了所需的值。如果您知道元数据中需要“accountnumber”,则必须以某种方式将此“accountnumber”“映射”到您使用的模型(也许这将是一个名为“accountnumber”的字段,但也可能是“CrmAccountNumber”例如,我不知道您的型号)并检查此字段是否有值。我希望你知道这一点很清楚。 Stack Overflow 上禁止讨论,所以我希望如此 :)
  • 您当前的问题实际上与原始问题相去甚远——您想获取哪些字段是必需的信息,并且这些信息存储在元数据中。你现在应该尝试一下,如果你有问题,你可以在 SO 上创建一个新问题,但是最初的问题已经解决了,所以我们不要在这里继续讨论。
【解决方案3】:

您可以使用RetrieveEntityRequest 获取实体的元数据。

在以下示例中,检索实体 Account 的元数据:

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;

【讨论】:

  • 这没有回答如何使用 WebApi 端点检索元数据。
  • @Nicknow 查看问题的编辑历史,添加的 C# 代码 sn-p 和标签,问题对我来说似乎有点模棱两可。也许这里的编辑是合适的?
  • @HenkvanBoeijen 我已经尝试过你建议的方法,请查看我编辑的问题
  • @MeggieLuski 我的错。 EntityFilters 属性是一个标志枚举。当需要属性的元数据时,必须设置属性标志。在我的示例中,Attributes 集合为空。我修改了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 2014-11-13
  • 1970-01-01
  • 2013-08-29
  • 1970-01-01
  • 2013-11-09
相关资源
最近更新 更多