Shaolinq.Persistence.TypeDescriptorProvider.TypeDescriptorProvider C# (CSharp) Method

TypeDescriptorProvider() public method

public TypeDescriptorProvider ( Type dataAccessModelType, DataAccessModelConfiguration configuration ) : System
dataAccessModelType System.Type
configuration DataAccessModelConfiguration
return System
		public TypeDescriptorProvider(Type dataAccessModelType, DataAccessModelConfiguration configuration)
		{
			this.Configuration = configuration;
			this.DataAccessModelType = dataAccessModelType;

			var dataAccessModelAttribute = dataAccessModelType.GetFirstCustomAttribute<DataAccessModelAttribute>(true);

			if (typeof(DataAccessModel).IsAssignableFrom(dataAccessModelType) && dataAccessModelAttribute == null)
			{
				throw new InvalidDataAccessObjectModelDefinition("The DataAccessModel type '{0}' is missing a DataAccessModelAttribute", dataAccessModelType.Name);
			}

			foreach (var type in this.DataAccessModelType
				.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)
				.Where(c => c.PropertyType.GetGenericTypeDefinitionOrNull() == typeof(DataAccessObjects<>))
				.Select(c => c.PropertyType.GetGenericArguments()[0]))
			{
				var currentType = type;

				while (currentType != null 
					&& currentType != typeof(DataAccessObject) 
					&& !(currentType.GetGenericTypeDefinitionOrNull() == typeof(DataAccessObject<>)))
				{
					var dataAccessObjectAttribute = currentType.GetFirstCustomAttribute<DataAccessObjectAttribute>(false);

					if (dataAccessObjectAttribute != null)
					{
						if (!this.typeDescriptorsByType.ContainsKey(currentType))
						{
							if (!typeof(DataAccessObject).IsAssignableFrom(currentType))
							{
								throw new InvalidDataAccessObjectModelDefinition("The type {0} is decorated with a [DataAccessObject] attribute but does not extend DataAccessObject<T>", currentType.Name);
							}

							var typeDescriptor = new TypeDescriptor(this, currentType);

							if (typeDescriptor.PrimaryKeyProperties.Count(c => c.PropertyType.IsNullableType()) > 0)
							{
								throw new InvalidDataAccessObjectModelDefinition("The type {0} illegally defines a nullable primary key", currentType.Name);
							}

							this.typeDescriptorsByType[currentType] = typeDescriptor;
						}
					}
					else
					{
						throw new InvalidDataAccessObjectModelDefinition("Type '{0}' does not have a DataAccessObject attribute", currentType);
					}

					currentType = currentType.BaseType;
				}
			}

			var typesSet = new HashSet<Type>(this.typeDescriptorsByType.Keys);

			var typesReferenced = this.typeDescriptorsByType
				.Values
				.SelectMany(c => c.PersistedPropertiesWithoutBackreferences)
				.Select(c => c.PropertyType)
				.Where(c => typeof(DataAccessObject).IsAssignableFrom(c))
				.Distinct();

			var first = typesReferenced.FirstOrDefault(c => !typesSet.Contains(c));

			if (first != null)
			{
				throw new InvalidDataAccessModelDefinitionException($"Type {first.Name} is referenced but is not declared as a property {dataAccessModelType.Name}");
			}	

			// Enums

			this.enumTypeDescriptorsByType = this.typeDescriptorsByType
				.Values
				.SelectMany(c => c.PersistedPropertiesWithoutBackreferences)
				.Select(c => c.PropertyType.GetUnwrappedNullableType())
				.Where(c => c.IsEnum)
				.Distinct()
				.Select(c => new EnumTypeDescriptor(c))
				.ToDictionary(c => c.EnumType, c => c);

			// Resolve relationships

			foreach (var typeDescriptor in this.typeDescriptorsByType.Values)
			{
				foreach (var propertyDescriptor in typeDescriptor.RelationshipRelatedProperties.Where(c => c.IsRelatedDataAccessObjectsProperty))
				{
					if (typeof(RelatedDataAccessObjects<>).IsAssignableFromIgnoreGenericParameters(propertyDescriptor.PropertyType))
					{
						var currentType = propertyDescriptor.PropertyType;
						
						while (currentType != null && currentType.GetGenericTypeDefinitionOrNull() != typeof(RelatedDataAccessObjects<>))
						{
							currentType = currentType?.BaseType;
						}

						if (currentType == null)
						{
							throw new InvalidOperationException("Code should be unreachable");
						}

						var relatedTypeDescriptor = this.typeDescriptorsByType[currentType.GetSequenceElementType()];

						var relatedProperty = relatedTypeDescriptor
							.RelationshipRelatedProperties
							.Where(c => c.IsBackReferenceProperty)
							.SingleOrDefault(c => c.PropertyName == propertyDescriptor.RelatedDataAccessObjectsAttribute.BackReferenceName);

						relatedProperty = relatedProperty ?? relatedTypeDescriptor
							.RelationshipRelatedProperties
							.Where(c => c.IsBackReferenceProperty)
							.Where(c => !c.PropertyTypeTypeDescriptor.RelationshipRelatedProperties.Any(d => string.Equals(d.RelatedDataAccessObjectsAttribute?.BackReferenceName, c.PropertyName, StringComparison.InvariantCultureIgnoreCase)))
							.SingleOrDefault(c => typeDescriptor.Type == c.PropertyType);

						relatedProperty = relatedProperty ?? relatedTypeDescriptor
							.RelationshipRelatedProperties
							.Where(c => c.IsBackReferenceProperty)
							.SingleOrDefault(c => typeDescriptor.Type.IsAssignableFrom(c.PropertyType));

						typeDescriptor.AddRelationshipInfo(RelationshipType.ParentOfOneToMany, propertyDescriptor, relatedProperty);
						relatedTypeDescriptor.AddRelationshipInfo(RelationshipType.ChildOfOneToMany, relatedProperty, propertyDescriptor);
					}
				}
			}

			// Fill in column names

			foreach (var typeDescriptor in typeDescriptorsByType.Values)
			{
				foreach (var columnInfo in QueryBinder.GetColumnInfos(this, typeDescriptor.PersistedProperties.ToArray()))
				{
					typeDescriptor.propertyDescriptorByColumnName[columnInfo.ColumnName] = columnInfo.RootProperty;
				}
			}


			this.ModelTypeDescriptor = new ModelTypeDescriptor(this, dataAccessModelType);
		}