简体   繁体   中英

How to generically get enumerated set of entity records by primary id with Dynamics 365 XRM Tooling SDK

I am attempting to write a generic method that given any Dynamics 365 entity name and a set of IDs (GUIDS) will respond with a set of entity records matching that enumerated set. My frustration is that there doesn't seem to be an efficient way to get the API to simply use the "primary ID key" without getting it first from metadata, another (seemingly unnecessary) round trip.

Consider the following (Hacked) method:

    public EntityCollection HackedFetchEntityRecordsById(IOrganizationService orgSvc, string entityName, IEnumerable<Guid> primaryEntityAttributeIds)
    {
        // Defacto HACK for getting primary entity attribute
        string primaryEntityAttribute = $"{entityName}id";

        StringBuilder sb = new StringBuilder();

        foreach (Guid guid in primaryEntityAttributeIds)
        {
            sb.AppendLine($@"<value>{guid}</value>");
        }

        string fetchXml = $@"
                    <fetch mapping='logical'>
                        <entity name='{entityName}'>
                            <no-attrs />
                            <filter>
                                <condition attribute='{primaryEntityAttribute}' operator='in'>
                                    {sb}
                                </condition>
                            </filter>
                        </entity>
                    </fetch> ";

        return orgSvc.RetrieveMultiple(new FetchExpression(fetchXml));
    }

Note that here I am simply using a defacto standard I have observed, in that it seems Microsoft has chosen to name primary id attributes on entities with the entity name followed by the string "id". This is clearly unsafe and a terrible way to do this.

I can do it the "right" way but it is inefficient:

    public EntityCollection InefficientFetchEntityRecordsById(IOrganizationService orgSvc, string entityName, IEnumerable<Guid> primaryEntityAttributeIds)
    {
        // "Correct" but inefficient way of getting primary entity attribute
        string primaryEntityAttribute = ((RetrieveEntityResponse) orgSvc.Execute(new RetrieveEntityRequest
        {
            LogicalName = entityName
        })).EntityMetadata.PrimaryIdAttribute;

        StringBuilder sb = new StringBuilder();

        foreach (Guid guid in primaryEntityAttributeIds)
        {
            sb.AppendLine($@"<value>{guid}</value>");
        }

        string fetchXml = $@"
                    <fetch mapping='logical'>
                        <entity name='{entityName}'>
                            <no-attrs />
                            <filter>
                                <condition attribute='{primaryEntityAttribute}' operator='in'>
                                    {sb}
                                </condition>
                            </filter>
                        </entity>
                    </fetch> ";

        return orgSvc.RetrieveMultiple(new FetchExpression(fetchXml));
    }

Note that in this case, I need to make a separate service call (with all the overhead that entails) to get the entity metadata in order to discern what the primary attribute is. Yuck.

I would like to do something like the following (fantasy/not working) method:

    public EntityCollection FantasyFetchEntityRecordsById(IOrganizationService orgSvc, string entityName, IEnumerable<Guid> primaryEntityAttributeIds)
    {
        StringBuilder sb = new StringBuilder();

        foreach (Guid guid in primaryEntityAttributeIds)
        {
            sb.AppendLine($@"<value>{guid}</value>");
        }

        // ILLEGAL XML - made up element "primaryEntityAttribute"
        string fetchXml = $@"
                    <fetch mapping='logical'>
                        <entity name='{entityName}'>
                            <no-attrs />
                            <filter>
                                <primaryEntityAttribute operator='in'>
                                    {sb}
                                </primaryEntityAttribute>
                            </filter>
                        </entity>
                    </fetch> ";

        return orgSvc.RetrieveMultiple(new FetchExpression(fetchXml));
    }

I would be very happy to use some other implementation of QueryBase in the RetrieveMultiple service.

Since the "hacked" method should work the vast majority of the time, perhaps try the "hacked" method first by appending id to the entity name. If that fails, retrieve the entity metadata to get the primaryId.

The primary key is set by CRM when the entity is created, and it follows the format of "entity name" + "id", following this format isn't a hack .

If you arn't happy with that I would use the metadata service to retrieve the details once in bulk.

RetrieveAllEntitiesRequest request = new RetrieveAllEntitiesRequest()
{
    EntityFilters = EntityFilters.Entity,
    RetrieveAsIfPublished = true
};

RetrieveAllEntitiesResponse response = (RetrieveAllEntitiesResponse)_serviceProxy.Execute(request);

foreach (EntityMetadata currentEntity in response.EntityMetadata)
{
    currentEntity.PrimaryIdAttribute
}

I'm not quite sure what the concern with doing this, presumably you are making multiple service calls for each entity, one extra metadata call won't hurt. As for expecting the server to 'just know' this information; it would probably have to query the metadata tables anyway.

Finally, if someone has to supply you with entity names, you could also ask for the primary key field.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM