В Dynamics 2011 SDK, как определить целевую сущность при LookupAttributeMetadata.Targets пуст?

Использование CRM 2011 SDK (v5.0.10) У меня возникает проблема с несколькими полями Lookup, где цель не заполняется, и я надеюсь, что кто-то может помочь мне определить лучший способ определить ссылочную сущность в этих случаев.

В частности, я получаю метаданные Entity, Attribute and Relationship с этим вызовом:

var entityRequest = new RetrieveAllEntitiesRequest
                        {
                           RetrieveAsIfPublished = true,
                           EntityFilters = EntityFilters.Entity | EntityFilters.Attributes | EntityFilters.Relationships
                           };

var entityResponse = (RetrieveAllEntitiesResponse)_organizationService.Execute(entityRequest);
return entityResponse.EntityMetadata.ToList();

Позже, работая с объектом EntityMetadata, возвращаемым с этого вызова, я проверяю коллекцию атрибутов и объекты LookupAttributeMetadata, я пытаюсь определить объект, на который ссылается поиск, используя свойство Targets объекта LookupAttributeMetadata.

Однако есть случаи, когда объект LookupAttributeMetadata имеет пустую коллекцию Targets. Например, объект Activity Activity (логическое имя: campaignactivity) имеет поле Service (логическое имя: serviced), определенное как поле Lookup, но свойство Targets объекта LookupAttributeMetadata пуст.

VS QuickWatch

Когда я просматриваю экран настройки веб-интерфейса пользователя для объекта и открываю поле "Сервис", в разделе "Тип" отображается "Тип": "Поиск", "Тип цели": "Аккаунт", "Имя отношения": campaignactivity_account.

Web UI field view

Откуда эта информация?

Также обратите внимание: нет никаких связей с именем "campaignactivity_account" в объектах кампании или аккаунта.

Обновление: Я запускаю инвентаризацию Dynamics CRM 2011 Rollup 8 (хотя я видел это и на Rolloup 7). В то время как активность кампании - это поле, которое я использовал в моем примере, в этой статье содержится 14 итоговых данных, перечисленных ниже. Я ищу общее решение (по сравнению с одноразовым решением для каждого), чтобы у меня не было кучи логики if (entityName=="rollupfield" && fieldName=="organizationid")... в моем коде, поскольку сущности и поля, с которыми я работаю, выбираются пользователем во время выполнения и Я не обязательно заранее знаю, что мне передадут.

  • Объект: поле свертки (rollupfield) Поле: идентификатор организации (организация)
  • Entity: Rollup Query (goalrollupquery) Поле: владеющий пользователем (владеющий)
  • Entity: Process Log (workflowlog) Поле: Относительно (aboutobjectid)
  • Объект: сохраненный вид (userquery) Поле: родительский запрос (parentqueryid)
  • Объект: активность кампании (активность кампании) Поле: Сервис (serviceid)
  • Объект: поиск по электронной почте (электронный поиск) Поле: родительский (родительский объект)
  • Сущность: определение часового пояса (timezonedefinition) Поле: организация (организация)
  • Entity: Response Response (campaignresponse) Поле: Сервис (serviceid)
  • Сущность: Quick Campaign (bulkoperation) Поле: Сервис (serviceid)
  • Сущность: разрешение поля (полеперполяция) Поле: Идентификатор организации (организация)
  • Сущность: Локализованное имя временной зоны (timezonelocalizedname) Поле: Организация (организация)
  • Сущность: правило часового пояса (timezonerule) Поле: организация (организация)
  • Объект: Аудит (аудит) Поле: Запись (objectid)
  • Entity: Post (post) Поле: RegOffjectObjectId (aboutobjectid)

Обновление: Для воспроизведения проблемы можно использовать следующее консольное приложение.

//Requires the following Referenses:
// Microsoft.CSharp
// Microsoft.IdentityModel
// Microsoft.xrm.sdk
// System
// System.Core
// System.Data
// System.Runtime.Serialization
// System.ServiceModel

using System;
using System.Linq;
using System.Net;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

namespace TargetlessLookupsPOC
{
    internal static class Program
    {
        private const string OrganizationServiceURL =
            "http://dynamicscrm1/DynamicsCRM1/XRMServices/2011/Organization.svc";

        private static void Main(string[] args)
        {
            Console.WriteLine("====== Authenticating ");

            var organizationServiceMngt =
                ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(OrganizationServiceURL));
            var authCred = new AuthenticationCredentials();
            authCred.ClientCredentials.Windows.ClientCredential = new NetworkCredential();
            IOrganizationService orgProxy = new OrganizationServiceProxy(organizationServiceMngt,
                                                                         authCred.ClientCredentials);

            Console.WriteLine("====== Fetching All Entity Metadata ");
            var entityRequest = new RetrieveAllEntitiesRequest
                {
                    RetrieveAsIfPublished = true,
                    EntityFilters = EntityFilters.Entity | EntityFilters.Attributes | EntityFilters.Relationships
                };

            var entityResponse = (RetrieveAllEntitiesResponse) orgProxy.Execute(entityRequest);

            Console.WriteLine("====== Searching For Targetless Lookups ");
            foreach (var ent in entityResponse.EntityMetadata)
            {
                foreach (var field in ent.Attributes
                    .OfType<LookupAttributeMetadata>()
                    .Where(lookup => !lookup.Targets.Any()))
                {
                    Console.WriteLine("Entity: {0} ({1}), Field: {2} ({3}) (type: {4}) is targetless",
                                      ent.DisplayName.LabelText(), ent.LogicalName,
                                      field.DisplayName.LabelText(), field.LogicalName,
                                      field.AttributeType);
                }
            }

            Console.WriteLine("=========================== Done");
            Console.WriteLine("** Press any key to continue **");
            Console.ReadKey();
        }

        public static string LabelText(this Label label)
        {
            return (label != null && label.UserLocalizedLabel != null)
                       ? label.UserLocalizedLabel.Label
                       : "<no label>";
        }
    }
}

Ответ 1

Вместо этого вы пытались использовать RetrieveRelationshipRequest?

Это вернет класс RetrieveRelationshipResponse, у которого есть свойство RelationshipMetadata, которое, в свою очередь, в зависимости от отношения, будет OneToManyRelationshipMetadata или ManyToManyRelationshipMetadata. Среди атрибутов класса OneToManyRelationshipMetadata вы найдете свойства ReferencingAttribute и ReferencedAttribute, которые вы хотите.

Ответ 2

У меня возникли проблемы с пониманием проблемы здесь. Объекты с полем поиска могут иметь или не иметь целевой объект. Конечно, я заметил некоторые из них, но ни один из ссылочных запросов не требовался. В случае объекта timezonerule организатор всегда имеет значение NULL. Исходя из вашего обновления, я думаю, вы, возможно, пришли к такому же выводу.

Конечно, я использую Fetch в основном, но я стараюсь просто перебирать атрибуты и основываться на типе атрибута и добавлять их в объект Dictionary. Объект словаря завернут в список (т.е. List<Dictionary<string, object>>)

Таким образом, я могу просто передать общий оператор Fetch и вернуть чистый iList из заполненных атрибутов. Для поиска вы также можете добавить атрибут "_name", чтобы получить отображаемое значение.