0
votes

I'm trying to copy lookup fields of a CRM to another CRM. But this fails because generally I got a exception saying that the attribute with the lookup logical name don't exist.

Ok I know that, because I'm trying to create it. When this occurs the referencing attribute of the relationship is the lookup, so the lookup must be in the referenced attribute.

But I try to search the relationship that have the lookup logical name in referenced attribute and I'm not finding, I tried the OneToMany and ManyToOne of both entities. So I need help to resolve this. Someone have a solution?

I don't want resolve this using solutions, because solutions cannot copy managed lookup fields. And I have all the attributes and entities to create in the another CRM, I only need to create the lookup and your relationship.

I have this basic code, but 2 CRMs is needed to test it.

public void CopyLookup() {

        List<EntityMetadata> OriginEntities = new List<EntityMetadata>();

        bool sucess = false;

        while (!sucess) {
            try {

                RetrieveAllEntitiesRequest metaDataRequest = new RetrieveAllEntitiesRequest();
                metaDataRequest.EntityFilters = EntityFilters.All;

                // Execute the request.

                RetrieveAllEntitiesResponse metaDataResponse = (RetrieveAllEntitiesResponse)Origin.IOrganizationService.Execute(metaDataRequest);
                OriginEntities = new List<EntityMetadata>(metaDataResponse.EntityMetadata);
                sucess = true;
                return entitiesMetadata;

            } catch (ThreadAbortException) {
            } catch (Exception _ex) {
                Console.WriteLine(String.Format("Fail to find Entities - {0}", _ex.Message));
                if (_ex.Message.Contains("There was no endpoint"))
                    sucess = false;
                else
                    throw new Exception(String.Format("Fail to find Entities - {0}", _ex.Message));
            }
        }


        foreach (EntityMetadata ent in OriginEntities.Where(wh => wh.LogicalName.Contains('_'))) {

                foreach (OneToManyRelationshipMetadata relation in ent.OneToManyRelationships) {

                    LookupAttributeMetadata lookup = (LookupAttributeMetadata)OriginEntities.Where(wh => relation.ReferencingEntity == wh.LogicalName).FirstOrDefault()
                                                                             .Attributes.Where(wa => wa.AttributeType == AttributeTypeCode.Lookup && wa.LogicalName == relation.ReferencingAttribute).FirstOrDefault();

                    if (lookup == null)
                        continue;

                    CreateOneToManyRequest createOtm = new CreateOneToManyRequest() {
                        OneToManyRelationship = relation,
                        Lookup = lookup
                    };

                    bool sucess2 = false;

                    while (!sucess2) {

                        try {
                            Migration.IOrganizationService.Execute(createOtm);
                        } catch (EndpointNotFoundException) {
                            sucess2 = false;
                        } catch (TimeoutException) {
                            sucess2 = false;
                        } catch (FaultException ex) {
                            if (ex.Message.Contains("endpoint")) {
                                sucess2 = false;
                            } else if (ex.Message.Contains("there is already")) {
                                sucess2 = true;
                            } else {
                                sucess2 = true;
                            }
                        } catch (Exception ex) {
                            if (ex.Message.Contains("This could be due to the service endpoint binding")) {
                                sucess2 = false;
                            } else if (ex.Message.Contains("is not unique")) {
                                sucess2 = true;
                            } else {
                                sucess2 = true;
                            }
                        }
                    }
                }
            }
        }
1

1 Answers

1
votes

I've always created relationships manually or via a solution, but I took a look at your code and got some ideas that might help...

This code reflects some of those ideas, with more info below.

var metaDataRequest = new RetrieveAllEntitiesRequest
{
    EntityFilters = EntityFilters.All
};

var metaDataResponse = (RetrieveAllEntitiesResponse)svc.Execute(metaDataRequest);
var OriginEntities = metaDataResponse.EntityMetadata.ToList();

foreach (var entity in OriginEntities.Where(e=> e.IsCustomizable.Value))
{
    foreach (var relationship in entity.OneToManyRelationships.Where(r => r.IsCustomRelationship == true))
    {
        var lookup = OriginEntities
            .Where(e => e.LogicalName == relationship.ReferencingEntity)
            .Single()
            .Attributes
            .Where(a => a.AttributeType == AttributeTypeCode.Lookup && a.LogicalName == relationship.ReferencingAttribute)
            .Single();
        var createOtm = new CreateOneToManyRequest()
        {
            OneToManyRelationship = relationship,
            Lookup = lookup as LookupAttributeMetadata
        };
    }
}
  1. I think you want to only deal with customizable entities, and custom relationships on them, which is why I modified the Where clauses in the foreach loops.
  2. If you're looking to make copies of managed lookup fields I think you'll want to reset the relationship's IsManaged flag to false and its IsCustomizable value to true. isManaged isCustomizable
  3. Once you manually create a solution's relationships as unmanaged you will never be able to install the managed solution in that system. So, I wonder if creating managed lookup fields via code is a good practice for the long term. Why not install the managed solution in the migration target system instead?
  4. Before you try copying a 1:N relationship, you might want to try creating one from scratch in code to see what info is really required. This could help you pare down what you're retrieving to the bare minimum of what you need to create one. For example, an Attribute's ColumnNumber is read-only, and the downloaded data has it. If you created a new Attribute from scratch, that should be blank.
  5. In the long term this approach might become more trouble than it's worth. Like if your organization starts using more sandbox environments and you've got to run code each time you want to set one up. Perhaps reconsider using solutions, or doing a hybrid approach of creating a few lookups in code and the rest via solutions.