Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Dynamics 2011 SDK, how to determine target entity when LookupAttributeMetadata.Targets is empty?

Using the CRM 2011 SDK (v5.0.10) I'm running into an issue with a handful of Lookup fields where the target is not populated and am hoping someone can help me determine the best way to determine the referenced entity in these cases.

Specifically, I'm retrieving the Entity, Attribute and Relationship metadata with this call:

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

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

Later, while working with an EntityMetadata object returned from that call, I inspect the Attributes collection and for LookupAttributeMetadata objects, I attempt to determine the entity referenced by the lookup using the Targets property of the LookupAttributeMetadata object.

However, there are some cases where a LookupAttributeMetadata object has an empty Targets collection. For example, the Campaign Activity (logical name: campaignactivity) entity has the Service field (logical name: serviced) defined as a Lookup field, but the Targets property on the LookupAttributeMetadata object is empty.

VS QuickWatch

When I look at the web UI Customizations screen for the entity and open the Service field, under the Type section is shows Type: Lookup, Target Record Type: Account, Relationship Name: campaignactivity_account.

Web UI field view

Where does this information come from?

Also note: there are no relationships named “campaignactivity_account” on either the Campaign Activity or Account entities.

Update: I'm running a stock install of Dynamics CRM 2011 Rollup 8 (although I saw this on Rolloup 7 as well). While Campaign Activity is the field I used in my example, there are 14 total with this issue, listed below. I'm looking for a general solution (vs one-off solution for each) to keep from having a bunch of if (entityName=="rollupfield" && fieldName=="organizationid")... logic in my code, since the entities and fields I'm working with are selected by the user at runtime and I don't necessarily know ahead of time what I'll be handed.

  • Entity: Rollup Field (rollupfield) Field: Organization Id (organizationid)
  • Entity: Rollup Query (goalrollupquery) Field: Owning User (owninguser)
  • Entity: Process Log (workflowlog) Field: Regarding (regardingobjectid)
  • Entity: Saved View (userquery) Field: Parent Query (parentqueryid)
  • Entity: Campaign Activity (campaignactivity) Field: Service (serviceid)
  • Entity: E-Mail Search (emailsearch) Field: Parent (parentobjectid)
  • Entity: Time Zone Definition (timezonedefinition) Field: Organization (organizationid)
  • Entity: Campaign Response (campaignresponse) Field: Service (serviceid)
  • Entity: Quick Campaign (bulkoperation) Field: Service (serviceid)
  • Entity: Field Permission (fieldpermission) Field: Organization Id (organizationid)
  • Entity: Time Zone Localized Name (timezonelocalizedname) Field: Organization (organizationid)
  • Entity: Time Zone Rule (timezonerule) Field: Organization (organizationid)
  • Entity: Auditing (audit) Field: Record (objectid)
  • Entity: Post (post) Field: RegardingObjectId (regardingobjectid)

Update: The following console app can be used to reproduce the issue.

//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>";
        }
    }
}
like image 714
John M. Wright Avatar asked May 31 '12 15:05

John M. Wright


1 Answers

Have you tried using the RetrieveRelationshipRequest message instead?

That will return a RetrieveRelationshipResponse class, that has a RelationshipMetadata property, which in turn, depending on the relationship, it will be a OneToManyRelationshipMetadata or ManyToManyRelationshipMetadata. Among the attributes of the OneToManyRelationshipMetadata class, you will find the ReferencingAttribute and ReferencedAttribute properties, which is what you want.

like image 92
Jordi Avatar answered Nov 10 '22 12:11

Jordi