In order to get all the metadata for an entity using SOAP endpoint, you can use RetrieveEntityRequest:
var request = new RetrieveEntityRequest
{
EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.All,
LogicalName = "account"
}
var response = (RetrieveEntityResponse)organizationService.Execute(request);
EntityFiters is an enum which allows you to specify what metadata are you trying to get:
[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
}
This is a flag enum so you can use it like that:
var request = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.Privileges | EntityFilters.Entity,
LogicalName = "account"
}
Or simply use the All
value to get all necessary metadata. In your attempt you failed to retrieve metadata, because you asked only for Entity metadata and you are interested in Attributes metadata.
So, taking your code snippet as a base, I would use this in the following way:
[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}"));
}
}
}
Method GetEntityMetadata
is simply doing what was in previous example, so calling RetrieveEntityRequest
and returning RetrieveEntityResponse
.
Of course the implementation of method CheckIfValueIsProvided
depends on how your Account model class is defined, but probably you will need some kind of mapping between your model and CRM Entity model (to know how to map for example field "accountnumber" to some field in your model). This is far beyond the scope of this question, but I believe that you already know enough to get started.
Just remember, that this is only an example. You should not keep this logic inside your controller class, you should move it to some utility class which you can reuse in different controllers. Metadata does not change often (and you probably have control over this changes), so you also probably would like to cache the metadata somewhere in your web application etc. I hope that you already have an idea what can be done, but the whole design if the logic is another story.
If you want to do this from JavaScript you should probably stick to the webAPI:
http://CRMADDRESS/api/data/v8.2/EntityDefinitions(LogicalName='account')/Attributes?$select=LogicalName,RequiredLevel
Will get you what you want (name of the attribute and its Required level). It will look like that:
{
"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"
}
You can use this to validate the request on client side, to give user a hint that he is missing important data before he sends a request to the server.