10
votes

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>";
        }
    }
}
2
Good question John. No answer for you (yet?) but I note that in my MSCRM 2011 deployment, campaignactivity.serviceid is a lookup to Appointment and has a relationship name of campaignactivity_appointment (which also does not exist). EDITED: I misread what was in my system CampaignActivity_Appointments exists but is not the same of course)Greg Owens
Also note: I'm connecting to the server using an account with Access Mode = Read-Write, License Type=Full and a member of the System Administrators role.John M. Wright

2 Answers

1
votes

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.

0
votes

I'm having trouble understanding the issue here. Entities with a lookup field may or may not have a target entity. Granted I spot checked some of these, but none of the referenced lookups were required. In the case of the timezonerule entity, the organizationid is always NULL. Based on your update though, I think you may have come to the same conclusion.

Granted I use Fetch mostly, but I tend to just iterate through the attributes and based on the attribute type and add each into a Dictionary object. The dictionary object is wrapped in a List (i.e. List<Dictionary<string, object>> )

This way I can just pass a generic Fetch statement and get back a clean iList of the populated attributes. For the lookups, you can also append the "_name" attribute to get the display value.