/// <summary>
        /// Gets the type of the entity schema data for.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <returns></returns>
        private EntityTypeSchemaData GetEntitySchemaDataForType(Type entityType)
        {
            EntityTypeSchemaData toReturn = null;

            _entityTypeSchemaDataPerType.TryGetValue(entityType, out toReturn);
            return(toReturn);
        }
        /// <summary>
        /// Adds the explorer item for the entity with the name specified to the entity's schemadata object. If it's already present,
        /// it simply returns that explorer item.
        /// </summary>
        /// <param name="entityName">Name of the entity.</param>
        private void AddEntityExplorerItem(string entityName)
        {
            EntityTypeSchemaData entitySchemaData = null;

            if (!_entityTypeSchemaDataPerEntityName.TryGetValue(entityName, out entitySchemaData))
            {
                return;
            }
            if (entitySchemaData.RelatedExplorerItem != null)
            {
                return;
            }
            // not yet created.
            var entityNameForElement = entityName.Replace("Entity", "");
            var suffix = string.Empty;

            if (!string.IsNullOrEmpty(entitySchemaData.SuperTypeName))
            {
                suffix = string.Format(" (Sub-type of '{0}')", entitySchemaData.SuperTypeName.Replace("Entity", ""));
            }
            entitySchemaData.RelatedExplorerItem = new ExplorerItem(entityNameForElement + suffix, ExplorerItemKind.QueryableObject, ExplorerIcon.Table)
            {
                DragText     = entityNameForElement,
                IsEnumerable = true,
                Children     = new List <ExplorerItem>()
            };

            var dummyInstance = entitySchemaData.Factory.Create();

            DiscoverEntityFields(dummyInstance, entitySchemaData);
        }
        /// <summary>
        /// Discovers the entity fields for the entity type specified.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="resourceType">Type of the resource.</param>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="customState">State of the custom.</param>
        private void DiscoverEntityFields(IEntityCore instance, EntityTypeSchemaData entitySchemaData)
        {
            var instanceFieldsToTravese = instance.Fields
                                          .Where(f => !f.IsPrimaryKey || (f.IsPrimaryKey && (f.ActualContainingObjectName == f.ContainingObjectName)))
                                          .ToDictionary(f => f.Name);
            var allProperties         = TypeDescriptor.GetProperties(entitySchemaData.EntityType).Cast <PropertyDescriptor>();
            var propertiesToTraverse  = allProperties.Where(p => instanceFieldsToTravese.ContainsKey(p.Name));
            var childrenCollection    = entitySchemaData.RelatedExplorerItem.Children;
            List <ExplorerItem> toAdd = new List <ExplorerItem>();

            foreach (PropertyDescriptor property in propertiesToTraverse)
            {
                var  field      = instance.Fields[property.Name];
                bool isPK       = false;
                bool isFK       = false;
                int  fieldIndex = -1;
                if (field == null)
                {
                    // other property
                    if (typeof(IEntityCore).IsAssignableFrom(property.PropertyType) || typeof(IEntityCollectionCore).IsAssignableFrom(property.PropertyType))
                    {
                        // entity navigator, done later
                        continue;
                    }
                    else
                    {
                        if (!property.IsBrowsable)
                        {
                            continue;
                        }
                    }
                }
                else
                {
                    isPK       = field.IsPrimaryKey;
                    isFK       = field.IsForeignKey;
                    fieldIndex = field.FieldIndex;
                }
                string propertyText = property.Name;
                if (isFK)
                {
                    propertyText += " (FK)";
                }
                var icon = isPK ? ExplorerIcon.Key : ExplorerIcon.Column;
                if (field.ActualContainingObjectName != field.ContainingObjectName)
                {
                    // inherited field.
                    propertyText += string.Format(" (Inherited from '{0}')", field.ContainingObjectName.Replace("Entity", ""));
                    icon          = ExplorerIcon.Inherited;
                }
                toAdd.Add(new ExplorerItem(propertyText, ExplorerItemKind.Property, icon)
                {
                    DragText = property.Name,
                    Tag      = fieldIndex
                });
            }
            childrenCollection.AddRange(toAdd.OrderBy(i => (int)i.Tag).ThenBy(i => i.Text));
        }
        /// <summary>
        /// Gets the schema.
        /// </summary>
        /// <returns></returns>
        internal List <ExplorerItem> GetSchema()
        {
            VerifyEntityAssemblyVersion();
            // We're only interested in the DataSource(2)<TEntity> returning properties.
            var queryableProperties = _linqMetaDataType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                      .Where(p => typeof(IQueryable).IsAssignableFrom(p.PropertyType));

            _entityTypeSchemaDataPerEntityName = new Dictionary <string, EntityTypeSchemaData>();
            _entityTypeSchemaDataPerType       = new Dictionary <Type, EntityTypeSchemaData>();

            // first we discover all entity types.
            foreach (var property in queryableProperties)
            {
                if ((!property.PropertyType.IsGenericType) && !typeof(IQueryable).IsAssignableFrom(property.PropertyType))
                {
                    continue;
                }
                var entityType = property.PropertyType.GetGenericArguments()[0];
                ProduceEntityFactoryDelegate(entityType);
                var factory = GetFactory(entityType);
                if (factory == null)
                {
                    continue;
                }
                var dummyInstance    = factory.Create();
                var entitySchemaData = new EntityTypeSchemaData()
                {
                    EntityType           = entityType,
                    Factory              = factory,
                    InheritanceInfo      = dummyInstance.GetInheritanceInfo(),
                    LinqMetaDataProperty = property
                };
                _entityTypeSchemaDataPerEntityName[dummyInstance.LLBLGenProEntityName] = entitySchemaData;
                _entityTypeSchemaDataPerType[entityType] = entitySchemaData;
            }

            // traverse all entity type schema data objects we've created and recurse over the types to build all property data
            foreach (var entityName in _entityTypeSchemaDataPerEntityName.Keys)
            {
                AddEntityExplorerItem(entityName);
            }

            // second iteration, we discover all navigators. Because all entity types are known we can simply use the lookups
            foreach (var entityStateData in _entityTypeSchemaDataPerEntityName.Values.Distinct())
            {
                DiscoverEntityNavigators(entityStateData);
            }
            return(_entityTypeSchemaDataPerType.Values.Select(v => v.RelatedExplorerItem).ToList());
        }
        /// <summary>
        /// Discovers the entity navigators.
        /// </summary>
        /// <param name="entitySchemaData">The entity schema data.</param>
        private void DiscoverEntityNavigators(EntityTypeSchemaData entitySchemaData)
        {
            var entityType = entitySchemaData.EntityType;

            if (entitySchemaData.Factory == null)
            {
                throw new InvalidOperationException(string.Format("Factory is null in entitySchemaData for entity '{0}'", entitySchemaData.EntityType.FullName));
            }
            var dummyInstance              = entitySchemaData.Factory.Create();
            var relationsWithMappedFields  = dummyInstance.GetAllRelations().Where(r => !string.IsNullOrEmpty(r.MappedFieldName) && !r.IsHierarchyRelation);
            var relationPerMappedFieldName = relationsWithMappedFields.ToDictionary(r => r.MappedFieldName);

            var properties          = TypeDescriptor.GetProperties(entityType).Cast <PropertyDescriptor>();
            var inheritedProperties = new HashSet <PropertyDescriptor>(DetermineInheritedProperties(entitySchemaData.EntityType, properties));
            var childrenCollection  = entitySchemaData.RelatedExplorerItem.Children;

            foreach (PropertyDescriptor property in properties)
            {
                if (!(typeof(IEntityCore).IsAssignableFrom(property.PropertyType) || typeof(IEntityCollectionCore).IsAssignableFrom(property.PropertyType)))
                {
                    // not an entity navigator,
                    continue;
                }
                string suffix    = string.Empty;
                bool   inherited = false;
                if (inheritedProperties.Contains(property))
                {
                    suffix    = string.Format(" (Inherited from '{0}')", property.ComponentType.Name.Replace("Entity", ""));
                    inherited = true;
                }
                // relationMapped can be null, in the case of a m:n navigator.
                IEntityRelation relationMapped = null;
                relationPerMappedFieldName.TryGetValue(property.Name, out relationMapped);
                if (typeof(IEntityCore).IsAssignableFrom(property.PropertyType))
                {
                    var relatedEntitySchemaData = GetEntitySchemaDataForType(property.PropertyType);
                    // single entity navigator, or property added manually.
                    if (relationMapped == null)
                    {
                        // property added manually, ignore.
                        continue;
                    }
                    var icon = relationMapped.TypeOfRelation == RelationType.ManyToOne ? ExplorerIcon.ManyToOne : ExplorerIcon.OneToOne;
                    if (inherited)
                    {
                        icon = ExplorerIcon.Inherited;
                    }
                    childrenCollection.Add(new ExplorerItem(property.Name + suffix, ExplorerItemKind.ReferenceLink, icon)
                    {
                        DragText        = property.Name,
                        HyperlinkTarget = relatedEntitySchemaData == null ? null : relatedEntitySchemaData.RelatedExplorerItem
                    });
                    continue;
                }
                if (typeof(IEntityCollectionCore).IsAssignableFrom(property.PropertyType))
                {
                    // collection navigator. We have to determine which entity type is returned from the collection.
                    // First, check if there's a TypeContainedAttribute on the property. If so, use the type in the attribute. If not,
                    // try to determine the type using the linq utils method.
                    Type containedType          = null;
                    var  typeContainedAttribute = property.Attributes[typeof(TypeContainedAttribute)] as TypeContainedAttribute;
                    if ((typeContainedAttribute != null) && typeContainedAttribute.TypeContainedInCollection != null)
                    {
                        containedType = typeContainedAttribute.TypeContainedInCollection;
                    }
                    else
                    {
                        containedType = LinqUtils.DetermineEntityTypeFromEntityCollectionType(property.PropertyType);
                    }
                    var relatedEntitySchemaData = GetEntitySchemaDataForType(containedType);
                    var icon = relationMapped == null ? ExplorerIcon.ManyToMany : ExplorerIcon.OneToMany;
                    if (inherited)
                    {
                        icon = ExplorerIcon.Inherited;
                    }
                    childrenCollection.Add(new ExplorerItem(property.Name + suffix, ExplorerItemKind.CollectionLink, icon)
                    {
                        DragText        = property.Name,
                        HyperlinkTarget = relatedEntitySchemaData == null ? null : relatedEntitySchemaData.RelatedExplorerItem
                    });
                }
            }
        }