/// <summary>
        /// Gets an <c>ICollection</c> containing <c>Key</c> objects that match the supplied modifiedDate.
        /// </summary>
        /// <param name="modifiedDate">A <c>DateTime</c> containing the last modified date for the object keys to be returned.</param>
        /// <returns>An <c>ICollection</c> of keys for entities that have been deleted in the source system.</returns>
        public ICollection ReadDeletedObjectKeys(DateTime modifiedDate)
        {
            this.Initialize();

            // Currently the dynamic object provider does not support the retrieval of deleted entities
            var entitysMetaDataRequest = new RetrieveAllEntitiesRequest();
            entitysMetaDataRequest.EntityFilters = EntityFilters.Relationships;
            entitysMetaDataRequest.RetrieveAsIfPublished = true;
            var entitysMetaDataResponse = this.CrmAdapter.OrganizationService.Execute(entitysMetaDataRequest) as RetrieveAllEntitiesResponse;
            List<ManyToManyRelationshipMetadata> entityMToMRelationships = new List<ManyToManyRelationshipMetadata>();

            foreach (EntityMetadata crmMetadata in entitysMetaDataResponse.EntityMetadata)
            {
                entityMToMRelationships = (from meta in crmMetadata.ManyToManyRelationships
                                           where meta.SchemaName == this.ProvidedEntityName
                                           select meta).ToList();

                if (entityMToMRelationships.Count > 0)
                {
                    break;
                }
            }

            if (entityMToMRelationships.Count > 0)
            {
                return this.GetDissassociateRelationship(modifiedDate);
            }

            return this.GetDeletedEntityKeys(modifiedDate);
        }
        /// <summary>
        /// Retrieves the associations that have been deleted using the audit functionality in CRM.
        /// </summary>
        /// <param name="modifiedDate">The <see cref="DateTime"/> to check for disassociations for in the audit history.</param>
        /// <returns>An <see cref="ICollection"/> of the entities that have been disassociated.</returns>
        private ICollection GetDissassociateRelationship(DateTime modifiedDate)
        {
            RetrieveMultipleRequest retrieveRequest = new RetrieveMultipleRequest();
            var query = new QueryExpression("audit");
            query.ColumnSet = new ColumnSet(true);
            query.Criteria.AddCondition(new ConditionExpression("createdon", ConditionOperator.GreaterEqual, modifiedDate));
            query.Criteria.AddCondition(new ConditionExpression("action", ConditionOperator.Equal, 34));
            query.PageInfo = new PagingInfo();
            query.PageInfo.Count = 5000;
            query.PageInfo.PageNumber = 1;
            query.PageInfo.PagingCookie = null;

            retrieveRequest.Query = query;
            var retrievedEntities = this.GetDissassociateRelationshipKeys(retrieveRequest);

            var entitysMetaDataRequest = new RetrieveAllEntitiesRequest();
            entitysMetaDataRequest.EntityFilters = EntityFilters.Relationships;
            entitysMetaDataRequest.RetrieveAsIfPublished = true;
            var entitysMetaDataResponse = this.CrmAdapter.OrganizationService.Execute(entitysMetaDataRequest) as RetrieveAllEntitiesResponse;

            List<ManyToManyRelationshipMetadata> entityMToMRelationships = new List<ManyToManyRelationshipMetadata>();
            foreach (EntityMetadata crmMetadata in entitysMetaDataResponse.EntityMetadata)
            {
                entityMToMRelationships = (from meta in crmMetadata.ManyToManyRelationships
                                           where meta.SchemaName == this.ProvidedEntityName
                                           select meta).ToList();

                if (entityMToMRelationships.Count > 0)
                {
                    break;
                }
            }

            if (entityMToMRelationships.Count > 0)
            {
                string strEntityRelationshipName = entityMToMRelationships[0].IntersectEntityName;
                string entity1Name = entityMToMRelationships[0].Entity1LogicalName;
                string entity1IntersectAtt = entityMToMRelationships[0].Entity1IntersectAttribute;
                string entity2Name = entityMToMRelationships[0].Entity2LogicalName;
                string entity2IntersectAtt = entityMToMRelationships[0].Entity2IntersectAttribute;

                List<RelationshipData> keyList = new List<RelationshipData>();
                foreach (Entity crmEntity in retrievedEntities)
                {
                    string transactionID = crmEntity.Attributes["transactionid"].ToString();
                    var retrievedTransactionEntities = (from meta in retrievedEntities
                                                        where meta.Attributes["transactionid"].ToString() == transactionID.ToString()
                                                        select meta).ToList();

                    Dictionary<string, object> entityDictionary = new Dictionary<string, object>();
                    if (retrievedTransactionEntities.Count < 2)
                    {
                        continue;
                    }

                    bool entity1NameID = false;
                    bool entity2NameID = false;
                    foreach (Entity crmTransEntity in retrievedTransactionEntities)
                    {
                        EntityReference objectID = (EntityReference)crmTransEntity.Attributes["objectid"];

                        if (objectID.LogicalName == entity1Name)
                        {
                            entityDictionary.Add(entity1IntersectAtt, objectID.Id.ToString());
                            entity1NameID = true;
                        }

                        if (objectID.LogicalName == entity2Name)
                        {
                            entityDictionary.Add(entity2IntersectAtt, objectID.Id.ToString());
                            entity2NameID = true;
                        }
                    }

                    if (!entity1NameID || !entity2NameID)
                    {
                        continue;
                    }

                    entityDictionary.Add("ProvidedEntityName", this.ProvidedEntityName);
                    entityDictionary.Add("EntityRelationshipName", strEntityRelationshipName);
                    entityDictionary.Add("Entity1Name", entity1Name);
                    entityDictionary.Add("Entity1IntersectAtt", entity1IntersectAtt);
                    entityDictionary.Add("Entity2Name", entity2Name);
                    entityDictionary.Add("Entity2IntersectAtt", entity2IntersectAtt);

                    RelationshipData relationshipData = new RelationshipData();
                    relationshipData.ReturnedDictionary = entityDictionary;
                    keyList.Add(relationshipData);
                }

                return keyList;
            }

            return null;
        }
        /// <summary>
        /// Gets an <c>ICollection</c> containing <c>Key</c> objects that match the supplied modifiedDate
        /// </summary>
        /// <param name="modifiedDate">A <c>DateTime</c> containing the last modified date for the object keys to be returned</param>
        /// <returns>An <c>ICollection</c> containing keys that can be used to retrieve instances of the provided object from the source system</returns>
        public ICollection ReadObjectKeys(DateTime modifiedDate)
        {
            this.Initialize();
            var entitysMetaDataRequest = new RetrieveAllEntitiesRequest();
            entitysMetaDataRequest.EntityFilters = EntityFilters.Relationships;
            entitysMetaDataRequest.RetrieveAsIfPublished = true;
            var entitysMetaDataResponse = this.CrmAdapter.OrganizationService.Execute(entitysMetaDataRequest) as RetrieveAllEntitiesResponse;
            List<ManyToManyRelationshipMetadata> entityMToMRelationships = new List<ManyToManyRelationshipMetadata>();

            foreach (EntityMetadata crmMetadata in entitysMetaDataResponse.EntityMetadata)
            {
                entityMToMRelationships = (from meta in crmMetadata.ManyToManyRelationships
                                           where meta.SchemaName == this.ProvidedEntityName
                                           select meta).ToList();

                if (entityMToMRelationships.Count > 0)
                {
                    break;
                }
            }

            if (entityMToMRelationships.Count > 0)
            {
                string strEntityRelationshipName = entityMToMRelationships[0].IntersectEntityName;
                string entity1Name = entityMToMRelationships[0].Entity1LogicalName;
                string entity1IntersectAtt = entityMToMRelationships[0].Entity1IntersectAttribute;
                string entity2Name = entityMToMRelationships[0].Entity2LogicalName;
                string entity2IntersectAtt = entityMToMRelationships[0].Entity2IntersectAttribute;

                var relationshipMetaDataResponse = this.AssociatedRetrival(strEntityRelationshipName, entity1Name, entity1IntersectAtt);

                List<RelationshipData> keyList = new List<RelationshipData>();
                if (relationshipMetaDataResponse.EntityCollection.Entities.Count > 0)
                {
                    Dictionary<string, object> returnedDictionary = new Dictionary<string, object>();
                    string aliaseEntity1IntersectAtt = "aa." + entity1IntersectAtt;
                    string aliaseEntity2IntersectAtt = "aa." + entity2IntersectAtt;

                    foreach (Entity entity in relationshipMetaDataResponse.EntityCollection.Entities)
                    {
                        Dictionary<string, object> entityDictionary = new Dictionary<string, object>();
                        entityDictionary.Add(entity1IntersectAtt, entity.GetAttributeValue<AliasedValue>(aliaseEntity1IntersectAtt).Value);
                        entityDictionary.Add(entity2IntersectAtt, entity.GetAttributeValue<AliasedValue>(aliaseEntity2IntersectAtt).Value);

                        entityDictionary.Add("ProvidedEntityName", this.ProvidedEntityName);
                        entityDictionary.Add("EntityRelationshipName", strEntityRelationshipName);
                        entityDictionary.Add("Entity1Name", entity1Name);
                        entityDictionary.Add("Entity1IntersectAtt", entity1IntersectAtt);
                        entityDictionary.Add("Entity2Name", entity2Name);
                        entityDictionary.Add("Entity2IntersectAtt", entity2IntersectAtt);

                        RelationshipData relationshipData = new RelationshipData();
                        relationshipData.ReturnedDictionary = entityDictionary;
                        keyList.Add(relationshipData);
                    }

                    return keyList.ToArray();
                }
            }

            return this.IsActivityEntity != true ? this.GetModifiedEntityKeys(modifiedDate, this.ProvidedEntityName + "id") : this.GetModifiedEntityKeys(modifiedDate, "activityid");
        }
        private List<ObjectProvider> GetObjectProviders()
        {
            if (this.InstallAdapter.MetadataTimestamp != this.InstallAdapter.RetrieveMetadataTimestamp())
            {
                List<ObjectProvider> MtoMAvailableProviders = new List<ObjectProvider>();

                this.availableProviders = new List<ObjectProvider>();
                RetrieveAllEntitiesRequest retrieveAll = new RetrieveAllEntitiesRequest();
                RetrieveAllEntitiesResponse response = new RetrieveAllEntitiesResponse();
                RetrieveEntityRequest entityRequest = new RetrieveEntityRequest();

                this.PublishPreConfigurationMessage(string.Format(CultureInfo.CurrentCulture, Resources.StartRetrievingEntityMetadata));
                response = this.InstallAdapter.OrganizationService.Execute(retrieveAll) as RetrieveAllEntitiesResponse;
                foreach (EntityMetadata crmMetadata in response.EntityMetadata)
                {
                    entityRequest.MetadataId = crmMetadata.MetadataId.Value;
                    entityRequest.EntityFilters = EntityFilters.Relationships;
                    RetrieveEntityResponse entityResponse = this.InstallAdapter.OrganizationService.Execute(entityRequest) as RetrieveEntityResponse;
                    if (entityResponse.EntityMetadata.DisplayName.LocalizedLabels.Count > 0 && !entityResponse.EntityMetadata.LogicalName.StartsWith("dynamics_deleted", StringComparison.OrdinalIgnoreCase) && entityResponse.EntityMetadata.IsCustomizable.Value)
                    {
                        this.availableProviders.Add(new DynamicObjectProvider() { Adapter = this.InstallAdapter, Id = crmMetadata.MetadataId.Value, DisplayName = entityResponse.EntityMetadata.DisplayName.LocalizedLabels[0].Label, Name = entityResponse.EntityMetadata.LogicalName });
                    }

                    List<ManyToManyRelationshipMetadata> entityMToMRelationships = (from meta in entityResponse.EntityMetadata.ManyToManyRelationships
                                                                                         where (meta.IsCustomRelationship.Value == true)
                                                                                         || (meta.SecurityTypes == SecurityTypes.ParentChild && meta.IsCustomRelationship.Value == false)
                                                                                         select meta).ToList();
                    if (entityMToMRelationships.Count > 0)
                    {
                        foreach (ManyToManyRelationshipMetadata relation in entityMToMRelationships)
                        {
                            if (!MtoMAvailableProviders.Any(f => f.DisplayName == relation.SchemaName))
                            {
                                MtoMAvailableProviders.Add(new DynamicObjectProvider() { Adapter = this.InstallAdapter, Id = relation.MetadataId.Value, DisplayName = relation.SchemaName, Name = relation.SchemaName + ",Relationship" });
                            }
                        }
                    }
                }

                foreach (ObjectProvider relationObjectProvider in MtoMAvailableProviders)
                {
                    this.availableProviders.Add(relationObjectProvider);
                }

                this.PublishPostConfigurationMessage(string.Format(CultureInfo.CurrentCulture, Resources.FinishedRetreivingEntityMetadata));
                this.InstallAdapter.MetadataTimestamp = this.InstallAdapter.RetrieveMetadataTimestamp();
                this.PublishPostConfigurationMessage(string.Format(CultureInfo.CurrentCulture, Resources.MetadataTimeStamp, this.InstallAdapter.MetadataTimestamp));
            }

            return this.availableProviders;
        }
        private void FillPicklistObjectDef(ObjectDefinition objDef)
        {
            var entityMetaDataRequest = new RetrieveAllEntitiesRequest();
            entityMetaDataRequest.EntityFilters = EntityFilters.Attributes;
            entityMetaDataRequest.RetrieveAsIfPublished = true;
            var entityMetaDataResponse = this.InstallAdapter.OrganizationService.Execute(entityMetaDataRequest) as RetrieveAllEntitiesResponse;

            GeneratePicklistObjectDefinitionEntries(objDef, entityMetaDataResponse);

            var allSetsRequest = new RetrieveAllOptionSetsRequest();
            var allSetsResponse = this.InstallAdapter.OrganizationService.Execute(allSetsRequest) as RetrieveAllOptionSetsResponse;
            GenerateGlobalPicklistObjectDefinitionEntries(objDef, allSetsResponse);
        }
        /// <summary>
        /// Gets an <c>ICollection</c> containing <c>Key</c> objects that match the supplied modifiedDate
        /// </summary>
        /// <param name="modifiedDate">A <c>DateTime</c> containing the last modified date for the object keys to be returned</param>
        /// <returns>An <c>ICollection</c> containing keys that can be used to retrieve instances of the provided object from the source system</returns>
        public ICollection ReadObjectKeys(DateTime modifiedDate)
        {
            List<Guid> keys = new List<Guid>();

            // Retrieve all entities and parse for local picklists
            RetrieveAllEntitiesRequest allRequest = new RetrieveAllEntitiesRequest() { RetrieveAsIfPublished = true, EntityFilters = EntityFilters.Attributes };
            RetrieveAllEntitiesResponse allresponse = (RetrieveAllEntitiesResponse)this.CallMetadataExecuteWebMethod(allRequest);
            foreach (var meta in allresponse.EntityMetadata.OrderBy(x => x.LogicalName))
            {
                var picklistMeta = meta.Attributes.Where(x => x.AttributeType == AttributeTypeCode.Picklist && x.IsCustomizable.Value == true && meta.DisplayName.LocalizedLabels.Count > 0);
                if (picklistMeta.Count() > 0)
                {
                    if (picklistMeta.Cast<PicklistAttributeMetadata>().Any(x => x.OptionSet.IsGlobal.Value == false))
                    {
                        keys.Add(meta.MetadataId.Value);
                    }
                }
            }

            // Retrieve global option sets that are customizable
            var globalOptionSetRequest = new RetrieveAllOptionSetsRequest() { RetrieveAsIfPublished = true };
            var globalResponse = (RetrieveAllOptionSetsResponse)this.CallMetadataExecuteWebMethod(globalOptionSetRequest);
            var usableGlobalOptionSets = globalResponse.OptionSetMetadata.Where(x => x.IsCustomizable.Value == true && x.DisplayName.LocalizedLabels.Count > 0);

            foreach (var globalOption in usableGlobalOptionSets)
            {
                keys.Add(globalOption.MetadataId.Value);
            }

            return keys;
        }