private string GetName(MemberInfo memberInfo, string autoNamePattern, TypeDescriptor typeDescriptor)
        {
            if (autoNamePattern == null)
            {
                return memberInfo.Name;
            }

            return VariableSubstitutor.Substitute(autoNamePattern, (value) =>
            {
                switch (value)
                {
                    case "$(PERSISTEDTYPENAME)":
                        return typeDescriptor.PersistedName;
                    case "$(PERSISTEDTYPENAME_LOWER)":
                        return memberInfo.ReflectedType.Name.ToLower();
                    case "$(TYPENAME)":
                        return memberInfo.ReflectedType.Name;
                    case "$(TYPENAME_LOWER)":
                        return memberInfo.ReflectedType.Name.ToLower();
                    case "$(PROPERTYNAME)":
                        return memberInfo.Name;
                    default:
                        throw new NotSupportedException(value);
                }
            });
        }
Beispiel #2
0
		public PropertyDescriptor(TypeDescriptor declaringTypeDescriptor, Type ownerType, PropertyInfo propertyInfo)
		{
			this.OwnerType = ownerType;
			this.PropertyInfo = propertyInfo;
			this.DeclaringTypeDescriptor = declaringTypeDescriptor;

			this.ValueRequiredAttribute = propertyInfo.GetFirstCustomAttribute<ValueRequiredAttribute>(true);
			this.DefaultValueAttribute = propertyInfo.GetFirstCustomAttribute<DefaultValueAttribute>(true);
			this.BackReferenceAttribute = propertyInfo.GetFirstCustomAttribute<BackReferenceAttribute>(true);
			this.RelatedDataAccessObjectsAttribute = propertyInfo.GetFirstCustomAttribute<RelatedDataAccessObjectsAttribute>(true);
			this.PersistedMemberAttribute = propertyInfo.GetFirstCustomAttribute<PersistedMemberAttribute>(true);
			this.ComputedMemberAttribute = propertyInfo.GetFirstCustomAttribute<ComputedMemberAttribute>(true);
			this.ComputedTextMemberAttribute = propertyInfo.GetFirstCustomAttribute<ComputedTextMemberAttribute>(true);
			this.ForeignObjectConstraintAttribute = propertyInfo.GetFirstCustomAttribute<ForeignObjectConstraintAttribute>(true);

			if (PersistedMemberAttribute == null)
			{
				this.PersistedMemberAttribute = (PersistedMemberAttribute)this.ComputedMemberAttribute ?? this.ComputedTextMemberAttribute;
			}

			if (this.PropertyType.IsIntegerType(true) || this.PropertyType.GetUnwrappedNullableType() == typeof(Guid))
			{
				this.AutoIncrementAttribute = propertyInfo.GetFirstCustomAttribute<AutoIncrementAttribute>(true);
			}

			this.PrimaryKeyAttribute = this.PropertyInfo.GetFirstCustomAttribute<PrimaryKeyAttribute>(true);
			this.IsPrimaryKey = this.PrimaryKeyAttribute != null && this.PrimaryKeyAttribute.IsPrimaryKey;
			this.IndexAttributes = this.PropertyInfo.GetCustomAttributes(typeof(IndexAttribute), true).OfType<IndexAttribute>().ToReadOnlyCollection();
			this.UniqueAttribute = this.PropertyInfo.GetFirstCustomAttribute<UniqueAttribute>(true);

			var named = this.PersistedMemberAttribute ?? this.BackReferenceAttribute ?? (NamedMemberAttribute)this.RelatedDataAccessObjectsAttribute;

			if (named != null)
			{
				this.PersistedName = named.GetName(this, this.DeclaringTypeDescriptor.TypeDescriptorProvider.Configuration.NamingTransforms?.PersistedMemberName);
				this.PrefixName = named.GetPrefixName(this, this.DeclaringTypeDescriptor.TypeDescriptorProvider.Configuration.NamingTransforms?.PersistedMemberPrefixName);
				this.SuffixName = named.GetSuffixName(this, this.DeclaringTypeDescriptor.TypeDescriptorProvider.Configuration.NamingTransforms?.PersistedMemberSuffixName);
			}

			var expression = this.ComputedMemberAttribute?.GetSetLambdaExpression(this.DeclaringTypeDescriptor.TypeDescriptorProvider.Configuration, this)?.Body.StripConvert();

			if (expression?.NodeType == ExpressionType.Assign)
			{
				var assignmentExpression = expression as BinaryExpression;

				if (assignmentExpression.Left.NodeType == ExpressionType.MemberAccess)
				{
					var memberAccess = assignmentExpression.Left as MemberExpression;

					this.ComputedMemberAssignTarget = memberAccess.Member as PropertyInfo;
					this.ComputedMemberAssignmentValue = assignmentExpression.Right;
				}
			}
		}
Beispiel #3
0
		public static string Substitute(string pattern, TypeDescriptor typeDescriptor)
		{
			var root = false;

			if (pattern == null)
			{
				return typeDescriptor.TypeName;
			}

			if (visitedTypes == null)
			{
				root = true;
				visitedTypes = new HashSet<TypeDescriptor>();
			}

			try
			{
				return Substitute(pattern, value =>
				{
					switch (value.ToUpper())
					{
					case "TYPENAME":
						return typeDescriptor.TypeName;
					case "TABLENAME":
					case "PERSISTED_TYPENAME":
						if (visitedTypes.Contains(typeDescriptor))
						{
							throw new InvalidOperationException("Recursive variable substitution");
						}
						visitedTypes.Add(typeDescriptor);
						return typeDescriptor.PersistedName;
					default:
						throw new NotSupportedException(value);
					}
				});
			}
			finally
			{
				visitedTypes.Remove(typeDescriptor);

				Debug.Assert(!root || (root && visitedTypes.Count == 0));
			}
		}
Beispiel #4
0
        public PropertyDescriptor(TypeDescriptor declaringTypeDescriptor, Type ownerType, PropertyInfo propertyInfo)
        {
            this.OwnerType = ownerType;
            this.PropertyInfo = propertyInfo;
            this.DeclaringTypeDescriptor = declaringTypeDescriptor;

            this.ValueRequiredAttribute = propertyInfo.GetFirstCustomAttribute<ValueRequiredAttribute>(true);
            this.DefaultValueAttribute = propertyInfo.GetFirstCustomAttribute<DefaultValueAttribute>(true);
            this.BackReferenceAttribute = propertyInfo.GetFirstCustomAttribute<BackReferenceAttribute>(true);
            this.RelatedDataAccessObjectsAttribute = propertyInfo.GetFirstCustomAttribute<RelatedDataAccessObjectsAttribute>(true);
            this.PersistedMemberAttribute = propertyInfo.GetFirstCustomAttribute<PersistedMemberAttribute>(true);
            this.ComputedMemberAttribute = propertyInfo.GetFirstCustomAttribute<ComputedMemberAttribute>(true);
            this.ComputedTextMemberAttribute = propertyInfo.GetFirstCustomAttribute<ComputedTextMemberAttribute>(true);
            this.ForeignObjectConstraintAttribute = propertyInfo.GetFirstCustomAttribute<ForeignObjectConstraintAttribute>(true);

            if (this.PropertyType.IsIntegerType(true) || this.PropertyType.GetUnwrappedNullableType() == typeof(Guid))
            {
                this.AutoIncrementAttribute = propertyInfo.GetFirstCustomAttribute<AutoIncrementAttribute>(true);
            }

            this.PrimaryKeyAttribute = this.PropertyInfo.GetFirstCustomAttribute<PrimaryKeyAttribute>(true);
            this.IsPrimaryKey = this.PrimaryKeyAttribute != null && this.PrimaryKeyAttribute.IsPrimaryKey;

            if (this.PersistedMemberAttribute != null)
            {
                this.PersistedName = this.PersistedMemberAttribute.GetName(this.PropertyInfo, declaringTypeDescriptor);
                this.PersistedShortName = this.PersistedMemberAttribute.GetShortName(this.PropertyInfo, this.DeclaringTypeDescriptor);
            }
            else if (this.BackReferenceAttribute != null)
            {
                this.PersistedName = this.BackReferenceAttribute.GetName(this.PropertyInfo, declaringTypeDescriptor);
                this.PersistedShortName = this.PersistedName;
            }
            else if (this.RelatedDataAccessObjectsAttribute != null)
            {
                this.PersistedName = propertyInfo.Name;
                this.PersistedShortName = propertyInfo.Name;
            }

            this.IndexAttributes = new ReadOnlyList<IndexAttribute>(this.PropertyInfo.GetCustomAttributes(typeof(IndexAttribute), true).OfType<IndexAttribute>().ToList());
            this.UniqueAttribute = this.PropertyInfo.GetFirstCustomAttribute<UniqueAttribute>(true);
        }
 public string GetName(MemberInfo memberInfo, TypeDescriptor typeDescriptor)
 {
     return this.GetName(memberInfo, this.Name, typeDescriptor);
 }
		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);
		}
Beispiel #7
0
        public TypeRelationshipInfo SetOrCreateRelationshipInfo(TypeDescriptor relatedTypeDescriptor, EntityRelationshipType entityRelationshipType, PropertyDescriptor relatedProperty)
        {
            TypeRelationshipInfo retval;

            if (this.relationshipInfos.TryGetValue(relatedTypeDescriptor, out retval))
            {
                retval.EntityRelationshipType = entityRelationshipType;
                retval.ReferencingProperty = relatedProperty;
                retval.RelatedTypeTypeDescriptor = relatedTypeDescriptor;

                return retval;
            }

            retval = new TypeRelationshipInfo(relatedTypeDescriptor, entityRelationshipType, relatedProperty);

            this.relationshipInfos[relatedTypeDescriptor] = retval;

            return retval;
        }
Beispiel #8
0
        public TypeRelationshipInfo GetRelationshipInfo(TypeDescriptor relatedTypeDescriptor)
        {
            TypeRelationshipInfo retval;

            if (this.relationshipInfos.TryGetValue(relatedTypeDescriptor, out retval))
            {
                return retval;
            }

            return null;
        }
        protected virtual IDbCommand BuildUpdateCommand(TypeDescriptor typeDescriptor, DataAccessObject dataAccessObject)
        {
            IDbCommand      command;
            SqlCommandValue sqlCommandValue;
            var             updatedProperties = dataAccessObject.GetAdvanced().GetChangedPropertiesFlattened();

            if (updatedProperties.Count == 0)
            {
                return(null);
            }

            var primaryKeys = dataAccessObject.GetAdvanced().GetPrimaryKeysForUpdateFlattened();
            var commandKey  = new SqlCommandKey(dataAccessObject.GetType(), updatedProperties);

            if (this.TryGetUpdateCommand(commandKey, out sqlCommandValue))
            {
                command             = CreateCommand();
                command.CommandText = sqlCommandValue.commandText;
                FillParameters(command, updatedProperties, primaryKeys);

                return(command);
            }

            var assignments = updatedProperties.Select(c => (Expression) new SqlAssignExpression(new SqlColumnExpression(c.PropertyType, null, c.PersistedName), Expression.Constant(c.Value))).ToReadOnlyList();

            Expression where = null;

            var i = 0;

            Debug.Assert(primaryKeys.Length > 0);

            foreach (var primaryKey in primaryKeys)
            {
                var currentExpression = Expression.Equal(new SqlColumnExpression(primaryKey.PropertyType, null, primaryKey.PersistedName), Expression.Constant(primaryKey.Value));

                if (where == null)
                {
                    where = currentExpression;
                }
                else
                {
                    where = Expression.And(where, currentExpression);
                }

                i++;
            }

            var expression = new SqlUpdateExpression(new SqlTableExpression(typeDescriptor.PersistedName), assignments, where);

            expression = (SqlUpdateExpression)ObjectOperandComparisonExpander.Expand(expression);

            var result = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(expression, SqlQueryFormatterOptions.Default & ~SqlQueryFormatterOptions.OptimiseOutConstantNulls);

            command = CreateCommand();

            command.CommandText = result.CommandText;
            CacheUpdateCommand(commandKey, new SqlCommandValue()
            {
                commandText = command.CommandText
            });
            FillParameters(command, updatedProperties, primaryKeys);

            return(command);
        }
        public TypeDescriptorProvider(Type dataAccessModelType)
        {
            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.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(DataAccessObjects<>))
                .Select(c => c.PropertyType.GetGenericArguments()[0]))
            {
                var currentType = type;

                while (currentType != null
                    && currentType != typeof(DataAccessObject)
                    && !(currentType.IsGenericType && currentType.GetGenericTypeDefinition() == 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.PersistedProperties)
                .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.PersistedProperties)
                .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.RelatedProperties)
                {
                    if (typeof(RelatedDataAccessObjects<>).IsAssignableFromIgnoreGenericParameters(propertyDescriptor.PropertyType))
                    {
                        var currentType = propertyDescriptor.PropertyType;

                        while (!currentType.IsGenericType || (currentType.IsGenericType && currentType.GetGenericTypeDefinition() != typeof(RelatedDataAccessObjects<>)))
                        {
                            currentType = currentType.BaseType;
                        }

                        var relatedType = currentType.GetGenericArguments()[0];

                        var relatedTypeDescriptor = this.typeDescriptorsByType[relatedType];
                        var typeRelationshipInfo = typeDescriptor.GetRelationshipInfo(relatedTypeDescriptor);

                        if (typeRelationshipInfo != null)
                        {
                            if (typeRelationshipInfo.RelatedTypeTypeDescriptor != relatedTypeDescriptor)
                            {
                                throw new InvalidDataAccessObjectModelDefinition("The type {0} defines multiple relationships with the type {1}", typeDescriptor.Type.Name, relatedTypeDescriptor.Type.Name);
                            }

                            typeRelationshipInfo.EntityRelationshipType = EntityRelationshipType.ManyToMany;
                            typeRelationshipInfo.RelatedTypeTypeDescriptor = relatedTypeDescriptor;
                            relatedTypeDescriptor.SetOrCreateRelationshipInfo(typeDescriptor, EntityRelationshipType.ManyToMany, null);
                        }
                        else
                        {
                            var relatedProperty = relatedTypeDescriptor.GetRelatedProperty(typeDescriptor.Type);
                            relatedTypeDescriptor.SetOrCreateRelationshipInfo(typeDescriptor, EntityRelationshipType.ChildOfOneToMany, relatedProperty);
                        }
                    }
                }
            }

            foreach (var typeDescriptor in this.typeDescriptorsByType.Values)
            {
                foreach (var relationshipInfo in typeDescriptor.GetRelationshipInfos())
                {
                    var closedCelationshipInfo = relationshipInfo;

                    if (relationshipInfo.EntityRelationshipType == EntityRelationshipType.ChildOfOneToMany)
                    {
                        if (!typeDescriptor.RelatedProperties.Any(c => c.BackReferenceAttribute != null && c.PropertyType == closedCelationshipInfo.ReferencingProperty.PropertyType))
                        {
                            throw new InvalidDataAccessObjectModelDefinition("The child type {0} participates in a one-many relationship with the parent type {1} but does not explicitly define a BackReference property", typeDescriptor, relationshipInfo.ReferencingProperty.DeclaringTypeDescriptor);
                        }
                    }
                }
            }

            this.ModelTypeDescriptor = new ModelTypeDescriptor(this, dataAccessModelType);
        }
Beispiel #11
0
        public TypeDescriptorProvider(Type dataAccessModelType)
        {
            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.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(DataAccessObjects <>))
                     .Select(c => c.PropertyType.GetGenericArguments()[0]))
            {
                var currentType = type;

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

                    if (dataAccessObjectAttribute != null)
                    {
                        if (!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);
                            }

                            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.PersistedProperties)
                                  .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(string.Format("Type {0} is referenced but is not declared as a property {1}", first.Name, dataAccessModelType.Name));
            }

            // Enums

            this.enumTypeDescriptorsByType = this.typeDescriptorsByType
                                             .Values
                                             .SelectMany(c => c.PersistedProperties)
                                             .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 typeDescriptorsByType.Values)
            {
                foreach (var propertyDescriptor in typeDescriptor.RelatedProperties)
                {
                    if (typeof(RelatedDataAccessObjects <>).IsAssignableFromIgnoreGenericParameters(propertyDescriptor.PropertyType))
                    {
                        var currentType = propertyDescriptor.PropertyType;

                        while (!currentType.IsGenericType || (currentType.IsGenericType && currentType.GetGenericTypeDefinition() != typeof(RelatedDataAccessObjects <>)))
                        {
                            currentType = currentType.BaseType;
                        }

                        var relatedType = currentType.GetGenericArguments()[0];

                        var relatedTypeDescriptor = this.typeDescriptorsByType[relatedType];
                        var typeRelationshipInfo  = typeDescriptor.GetRelationshipInfo(relatedTypeDescriptor);

                        if (typeRelationshipInfo != null)
                        {
                            if (typeRelationshipInfo.RelatedTypeTypeDescriptor != relatedTypeDescriptor)
                            {
                                throw new InvalidDataAccessObjectModelDefinition("The type {0} defines multiple relationships with the type {1}", typeDescriptor.Type.Name, relatedTypeDescriptor.Type.Name);
                            }

                            typeRelationshipInfo.EntityRelationshipType    = EntityRelationshipType.ManyToMany;
                            typeRelationshipInfo.RelatedTypeTypeDescriptor = relatedTypeDescriptor;
                            relatedTypeDescriptor.SetOrCreateRelationshipInfo(typeDescriptor, EntityRelationshipType.ManyToMany, null);
                        }
                        else
                        {
                            var relatedProperty = relatedTypeDescriptor.GetRelatedProperty(typeDescriptor.Type);
                            relatedTypeDescriptor.SetOrCreateRelationshipInfo(typeDescriptor, EntityRelationshipType.ChildOfOneToMany, relatedProperty);
                        }
                    }
                }
            }

            foreach (var typeDescriptor in this.typeDescriptorsByType.Values)
            {
                foreach (var relationshipInfo in typeDescriptor.GetRelationshipInfos())
                {
                    var closedCelationshipInfo = relationshipInfo;

                    if (relationshipInfo.EntityRelationshipType == EntityRelationshipType.ChildOfOneToMany)
                    {
                        if (!typeDescriptor.RelatedProperties.Any(c => c.BackReferenceAttribute != null && c.PropertyType == closedCelationshipInfo.ReferencingProperty.PropertyType))
                        {
                            throw new InvalidDataAccessObjectModelDefinition("The child type {0} participates in a one-many relationship with the parent type {1} but does not explicitly define a BackReference property", typeDescriptor, relationshipInfo.ReferencingProperty.DeclaringTypeDescriptor);
                        }
                    }
                }
            }

            this.ModelTypeDescriptor = new ModelTypeDescriptor(this, dataAccessModelType);
        }
		internal string GetName(TypeDescriptor type, string transformString = "")
		{
			return VariableSubstituter.SedTransform(VariableSubstituter.Substitute(this.Name ?? type.TypeName, type), transformString);
		}