Как получить перечислимый набор записей сущностей по первичному идентификатору с помощью Dynamics 365 XRM Tooling SDK

Я пытаюсь написать универсальный метод, который с любым именем сущности Dynamics 365 и набором идентификаторов (GUIDS) будет отвечать набором записей сущностей, соответствующих этому перечисляемому набору. Я разочарован тем, что не представляется эффективным способом заставить API просто использовать "первичный ключ идентификатора", не получая его сначала из метаданных, другого (казалось бы, ненужного) обхода.

Рассмотрим следующий (взломанный) метод:

    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));
    }

Обратите внимание, что здесь я просто использую стандарт де-факто, который я наблюдал, поскольку кажется, что Microsoft решила назвать первичные атрибуты id для сущностей с именем сущности, за которым следует строка "id". Это явно небезопасный и ужасный способ сделать это.

Я могу сделать это "правильным" способом, но это неэффективно:

    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));
    }

Обратите внимание, что в этом случае мне нужно сделать отдельный вызов службы (со всеми вытекающими издержками), чтобы получить метаданные сущности, чтобы определить первичный атрибут. Тьфу.

Я хотел бы сделать что-то вроде следующего (фантазия / не работает) метод:

    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));
    }

Я был бы очень рад использовать какую-то другую реализацию QueryBase в сервисе RetrieveMultiple.

2 ответа

Поскольку "взломанный" метод должен работать подавляющее большинство времени, возможно, сначала попробуйте "взломанный" метод, добавив id на имя объекта. Если это не удается, извлеките метаданные объекта, чтобы получить primaryId.

Первичный ключ устанавливается CRM при создании объекта, и он следует формату "имя объекта" + "идентификатор", следование этому формату не является хаком.

Если вас это не устраивает, я бы воспользовался сервисом метаданных, чтобы получить подробную информацию один раз.

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

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

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

Я не совсем уверен, что делать с этим, возможно, вы делаете несколько вызовов сервисов для каждого объекта, один дополнительный вызов метаданных не повредит. Что касается ожидания, что сервер "просто знает" эту информацию; в любом случае, возможно, придется запросить таблицы метаданных.

Наконец, если кто-то должен предоставить вам имена сущностей, вы также можете запросить поле первичного ключа.

Другие вопросы по тегам