object DoRetrieveEntities(object arg)
        {
            SendStepChange("Retrieving entities...");

            var entities = new List<EntityDefinition>();

            var queryComponents = new QueryExpression(SolutionComponent.EntityLogicalName);
            queryComponents.ColumnSet.AllColumns = true;
            queryComponents.Criteria.AddCondition("componenttype", ConditionOperator.Equal, (int)componenttype.Entity);

            var linkSolution = queryComponents.AddLink(Solution.EntityLogicalName, "solutionid", "solutionid");
            linkSolution.EntityAlias = "solution";
            linkSolution.Columns.AddColumn("uniquename");

            var linkPublisher = linkSolution.AddLink(Publisher.EntityLogicalName, "publisherid", "publisherid");
            linkPublisher.EntityAlias = "publisher";
            linkPublisher.Columns.AddColumn("customizationprefix");
            queryComponents.Criteria.AddCondition("solutionid", ConditionOperator.Equal, _solution.Id);

            var components = _service.RetrieveMultiple(queryComponents).Entities;

            var max = components.Count;
            var current = 1;

            foreach (SolutionComponent component in components)
            {
                var entity = _service.RetrieveEntity(new RetrieveEntityRequest { MetadataId = component.ObjectId.Value, EntityFilters = EntityFilters.Entity | EntityFilters.Attributes | EntityFilters.Relationships }).EntityMetadata;

                var solutionName = component.GetAttributeValue<AliasedValue>("solution.uniquename").Value as string;
                Prefix = component.GetAttributeValue<AliasedValue>("publisher.customizationprefix").Value as string;

                var entityDefinition = new EntityDefinition
                {
                    LogicalName = entity.LogicalName,
                    Name = RemovePrefix(entity.SchemaName).FormatText() + "Definition",
                    IsActivity = entity.IsActivity.Value,
                    IsLoaded = true
                };

                ClassDefinition keyDefinition = null;
                if (entity.Keys != null && entity.Keys.Any())
                {
                    keyDefinition = new ClassDefinition
                    {
                        LogicalName = "AlternateKeyNames",
                        Name = "AlternateKeyNames"
                    };
                    entityDefinition.AdditionalClassesCollection.Add(keyDefinition);
                    foreach (var key in entity.Keys)
                    {
                        keyDefinition.Attributes.Add(new AttributeDefinition { LogicalName = key.LogicalName, Name = key.DisplayName.UserLocalizedLabel.Label.FormatText(), Value = key.LogicalName });
                    }
                }

                if (entity.OneToManyRelationships.Any())
                {
                    var classDefinition = new ClassDefinition
                    {
                        LogicalName = "OneToManyRelationships",
                        Name = "OneToManyRelationships"
                    };
                    entityDefinition.AdditionalClassesCollection.Add(classDefinition);
                    foreach (var relationship in entity.OneToManyRelationships)
                    {
                        classDefinition.Attributes.Add(new AttributeDefinition { LogicalName = relationship.SchemaName, Name = relationship.SchemaName, Value = relationship.SchemaName });
                    }
                }

                if (entity.ManyToManyRelationships.Any())
                {
                    var classDefinition = new ClassDefinition
                    {
                        LogicalName = "ManyToManyRelationships",
                        Name = "ManyToManyRelationships"
                    };
                    entityDefinition.AdditionalClassesCollection.Add(classDefinition);
                    foreach (var relationship in entity.ManyToManyRelationships)
                    {
                        classDefinition.Attributes.Add(new AttributeDefinition { LogicalName = relationship.SchemaName, Name = relationship.SchemaName, Value = relationship.SchemaName });
                    }
                }

                var lookupFields = new Dictionary<string, List<OneToManyRelationshipMetadata>>();
                if (entity.ManyToOneRelationships.Any())
                {
                    ClassDefinition classDefinition = new ClassDefinition
                    {
                        LogicalName = "ManyToOneRelationships",
                        Name = "ManyToOneRelationships"
                    };
                    entityDefinition.AdditionalClassesCollection.Add(classDefinition);
                    foreach (var relationship in entity.ManyToOneRelationships)
                    {
                        classDefinition.Attributes.Add(new AttributeDefinition { LogicalName = relationship.SchemaName, Name = relationship.SchemaName, Value = relationship.SchemaName });

                        List<OneToManyRelationshipMetadata> list;

                        if (lookupFields.ContainsKey(relationship.ReferencingAttribute))
                        {
                            list = lookupFields[relationship.ReferencingAttribute];
                        }
                        else
                        {
                            list = new List<OneToManyRelationshipMetadata>();
                            lookupFields.Add(relationship.ReferencingAttribute, list);
                        }

                        list.Add(relationship);
                    }
                }

                SendStepChange(string.Format("({1}/{2}) Retrieved '{0}' entity", entity.LogicalName, current.ToString("00"), max.ToString("00")));
                current++;

                entities.Add(entityDefinition);

                foreach (AttributeMetadata attributeMetadata in entity.Attributes.OrderBy(a => a.LogicalName))
                {
                    if (!attributeMetadata.IsValidForCreate.Value && !attributeMetadata.IsValidForRead.Value && !attributeMetadata.IsValidForUpdate.Value)
                    {
                        continue;
                    }
                    if (attributeMetadata.AttributeType.Value == AttributeTypeCode.EntityName || !string.IsNullOrEmpty(attributeMetadata.AttributeOf))
                    {
                        continue;
                    }

                    EnumDefinition enumDefinition = null;

                    if (attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist || attributeMetadata.AttributeType.Value == AttributeTypeCode.State || attributeMetadata.AttributeType.Value == AttributeTypeCode.Status)
                    {
                        var meta = ((EnumAttributeMetadata)attributeMetadata).OptionSet;

                        var enumLogicalName = meta.IsGlobal.Value ? meta.Name : entity.LogicalName + "_" + attributeMetadata.LogicalName;

                        var tempEnumDefinition = new EnumDefinition
                        {
                            LogicalName = enumLogicalName,
                            IsGlobal = meta.IsGlobal.Value,
                            HasNullValue = attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist
                        };

                        if (attributeMetadata.AttributeType.Value == AttributeTypeCode.State)
                        {
                            tempEnumDefinition.Name = entityDefinition.Name.Replace("Definition", "") + "State";
                        }
                        else if (attributeMetadata.AttributeType.Value == AttributeTypeCode.Status)
                        {
                            tempEnumDefinition.Name = entityDefinition.Name.Replace("Definition", "") + "Status";
                        }
                        else
                        {
                            tempEnumDefinition.Name = meta.DisplayName.UserLocalizedLabel.Label.FormatText();
                        }

                        foreach (var option in meta.Options)
                        {
                            tempEnumDefinition.Values.Add(new EnumValueDefinition
                            {
                                Name = option.Label.UserLocalizedLabel.Label.FormatText(),
                                LogicalName = option.Value.Value.ToString(),
                                DisplayName = option.Label.UserLocalizedLabel.Label,
                                Value = option.Value.Value.ToString()
                            });
                        }

                        if (!EnumDefinitionCollection.Instance.Contains(enumLogicalName))
                        {
                            enumDefinition = tempEnumDefinition;
                            EnumDefinitionCollection.Instance.Add(enumDefinition);
                        }
                        else
                        {
                            enumDefinition = EnumDefinitionCollection.Instance[enumLogicalName];
                            enumDefinition.Merge(tempEnumDefinition);
                        }
                    }

                    var name = RemovePrefix(attributeMetadata.SchemaName);

                    if (attributeMetadata.LogicalName == entity.PrimaryIdAttribute)
                    {
                        name = "Id";
                    }

                    var attributeDefinition = new AttributeDefinition
                    {
                        LogicalName = attributeMetadata.LogicalName,
                        Name = name,
                        DisplayName = attributeMetadata.DisplayName.UserLocalizedLabel == null ? attributeMetadata.SchemaName : attributeMetadata.DisplayName.UserLocalizedLabel.Label,
                        IsValidForAdvancedFind = attributeMetadata.IsValidForAdvancedFind.Value,
                        IsValidForCreate = attributeMetadata.IsValidForCreate.Value,
                        IsValidForRead = attributeMetadata.IsValidForRead.Value,
                        IsValidForUpdate = attributeMetadata.IsValidForUpdate.Value,
                        Type = attributeMetadata.AttributeType.Value.ToString(),
                        Enum = enumDefinition,
                        ParentEntity = entityDefinition,
                        IsPrimaryIdAttribute = attributeMetadata.LogicalName == entity.PrimaryIdAttribute,
                        IsPrimaryNameAttribute = attributeMetadata.LogicalName == entity.PrimaryNameAttribute,
                        IsPrimaryImageAttribute = attributeMetadata.LogicalName == entity.PrimaryImageAttribute,
                        Relationships = lookupFields.ContainsKey(attributeMetadata.LogicalName) ? lookupFields[attributeMetadata.LogicalName] : null
                    };

                    if (keyDefinition != null)
                    {
                        foreach (var key in entity.Keys.Where(k => k.KeyAttributes.Contains(attributeDefinition.LogicalName)))
                        {
                            attributeDefinition.KeyNames.Add(keyDefinition.Attributes[key.LogicalName].Name);
                        }
                    }

                    if (attributeDefinition.IsPrimaryIdAttribute || attributeDefinition.IsPrimaryNameAttribute || attributeDefinition.IsPrimaryImageAttribute || attributeDefinition.KeyNames.Any())
                    {
                        attributeDefinition.IsSelected = true;
                    }

                    entityDefinition.AttributesCollection.Add(attributeDefinition);
                }

            }
            SendStepChange(string.Empty);
            return entities;
        }
 internal void RetrieveAttributes(EntityDefinition item, Action<object> callback)
 {
     Run(DoRetrieveAttributes, callback, item);
 }