public AssemblyKey(Type dataAccessModelType, DataAccessModelConfiguration configuration)
 {
     this.configuration       = configuration;
     this.dataAccessModelType = dataAccessModelType;
     this.configurationHash   = configuration.GetSha1();
     this.configurationXml    = XmlSerializer <DataAccessModelConfiguration> .New().SerializeToString(configuration);
 }
Exemple #2
0
        public BaseTests(string providerName)
        {
            this.ProviderName = providerName;

            XmlConfigurator.Configure();

            try
            {
                if (providerName == "default")
                {
                    model = DataAccessModel.BuildDataAccessModel <T>();
                }
                else
                {
                    configuration = this.Create(providerName, this.GetType().Name);
                    model         = DataAccessModel.BuildDataAccessModel <T>(configuration);
                }

                model.Create(DatabaseCreationOptions.DeleteExisting);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception while configuring provider: " + providerName);
                Console.WriteLine(e);
                Console.WriteLine(e.StackTrace);

                throw;
            }
        }
        public override RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration)
        {
            var typeDescriptorProvider = new TypeDescriptorProvider(dataAccessModelType);
            var originalAssembly = dataAccessModelType.Assembly;
            var builtAssembly = BuildAssembly(typeDescriptorProvider, configuration);

            return new RuntimeDataAccessModelInfo(typeDescriptorProvider, builtAssembly, originalAssembly);
        }
 public DataAccessModelConfigurationUniqueKey(DataAccessModelConfiguration config)
 {
     this.ConstraintDefaultsConfiguration = config.ConstraintDefaultsConfiguration;
     this.NamingTransforms = config.NamingTransforms;
     this.ReferencedTypes  = config.ReferencedTypes;
     this.ValueTypesAutoImplicitDefault   = config.ValueTypesAutoImplicitDefault;
     this.AlwaysSubmitDefaultValues       = config.AlwaysSubmitDefaultValues;
     this.IncludeImplicitDefaultsInSchema = config.IncludeImplicitDefaultsInSchema;
 }
		private static Assembly BuildAssembly(TypeDescriptorProvider typeDescriptorProvider, DataAccessModelConfiguration configuration)
		{
			DataAccessObjectTypeBuilder dataAccessObjectTypeBuilder;

			var hash = configuration.GetSha1();
			var assemblyName = new AssemblyName(typeDescriptorProvider.DataAccessModelType.Assembly.GetName().Name + "." + typeDescriptorProvider.DataAccessModelType.Name);
			var sharedAssemblyName = new AssemblyName("Shaolinq.GeneratedDataAccessModel");
			var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(sharedAssemblyName, AssemblyBuilderAccess.RunAndSave);
			var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + "." + hash + ".dll");

			var propertiesBuilder = moduleBuilder.DefineType("$$$DataAccessModelProperties", TypeAttributes.Class, typeof(object));
			
			var assemblyBuildContext = new AssemblyBuildContext(assemblyBuilder, propertiesBuilder);

			var dataAccessModelTypeBuilder = new DataAccessModelTypeBuilder(assemblyBuildContext, moduleBuilder);
			dataAccessModelTypeBuilder.BuildTypePhase1(typeDescriptorProvider.DataAccessModelType);

			var typeDescriptors = typeDescriptorProvider.GetTypeDescriptors();
			
			foreach (var typeDescriptor in typeDescriptors)
			{
				dataAccessObjectTypeBuilder = new DataAccessObjectTypeBuilder(typeDescriptorProvider, assemblyBuildContext, moduleBuilder, typeDescriptor.Type);
				dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(1));
			}

			foreach (var typeDescriptor in typeDescriptors)
			{
				dataAccessObjectTypeBuilder = assemblyBuildContext.TypeBuilders[typeDescriptor.Type];
				dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(2));
			}

			assemblyBuildContext.DataAccessModelPropertiesTypeBuilder.CreateType();
			dataAccessModelTypeBuilder.BuildTypePhase2();

			bool saveConcreteAssembly;
			bool.TryParse(ConfigurationManager.AppSettings["Shaolinq.SaveConcreteAssembly"], out saveConcreteAssembly);

#if DEBUG
			const bool isInDebugMode = true;
#else
			const bool isInDebugMode = false;
#endif

			// ReSharper disable once ConditionIsAlwaysTrueOrFalse
			if (saveConcreteAssembly || isInDebugMode)
			{
				ActionUtils.IgnoreExceptions(() => assemblyBuilder.Save(assemblyName + "." + hash + ".dll"));
			}

			return assemblyBuilder;
		}
        public override RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration)
        {
            var key = new AssemblyKey(dataAccessModelType, configuration);

            RuntimeDataAccessModelInfo runtimeDataAccessModelInfo;

            lock (this.assemblyBuildInfosByKey)
            {
                if (!this.assemblyBuildInfosByKey.TryGetValue(key, out runtimeDataAccessModelInfo))
                {
                    runtimeDataAccessModelInfo = this.provider.GetDataAccessModelAssembly(dataAccessModelType, configuration);

                    this.assemblyBuildInfosByKey[key] = runtimeDataAccessModelInfo;
                }
            }

            return runtimeDataAccessModelInfo;
        }
        private static Assembly BuildAssembly(TypeDescriptorProvider typeDescriptorProvider, DataAccessModelConfiguration configuration)
        {
            DataAccessObjectTypeBuilder dataAccessObjectTypeBuilder;

            var configMd5 = configuration.GetMd5();
            var assemblyName = new AssemblyName(typeDescriptorProvider.DataAccessModelType.Assembly.GetName().Name + "." + typeDescriptorProvider.DataAccessModelType.Name);
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + "." + configMd5 + ".dll");

            var assemblyBuildContext = new AssemblyBuildContext(assemblyBuilder);

            var dataAccessModelTypeBuilder = new DataAccessModelTypeBuilder(assemblyBuildContext, moduleBuilder);
            dataAccessModelTypeBuilder.BuildType(typeDescriptorProvider.DataAccessModelType);

            var typeDescriptors = typeDescriptorProvider.GetTypeDescriptors();

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = new DataAccessObjectTypeBuilder(typeDescriptorProvider, assemblyBuildContext, moduleBuilder, typeDescriptor.Type);
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(1));
            }

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = assemblyBuildContext.TypeBuilders[typeDescriptor.Type];
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(2));
            }

            bool saveConcreteAssembly;
            bool.TryParse(ConfigurationManager.AppSettings["Shaolinq.SaveConcreteAssembly"], out saveConcreteAssembly);

            #if DEBUG
            const bool isInDebugMode = true;
            #else
            const bool isInDebugMode = false;
            #endif

            if (saveConcreteAssembly || isInDebugMode)
            {
                ActionUtils.IgnoreExceptions(() => assemblyBuilder.Save(assemblyName + "." + configMd5 + ".dll"));
            }

            return assemblyBuilder;
        }
		public static DataAccessModelConfiguration Create(string databaseName, string serverName, string userName, string password,  bool poolConnections, string categories)
		{
			var retval = new DataAccessModelConfiguration
			{
				SqlDatabaseContextInfos = new SqlDatabaseContextInfo[]
				{
					new MySqlSqlDatabaseContextInfo
					{
						DatabaseName = databaseName,
						Categories = categories,
						ServerName = serverName,
						PoolConnections = poolConnections,
						UserName = userName,
						Password = password
					}
				},
				ConstraintDefaultsConfiguration = { IndexedStringMaximumLength = 255 }
			};
			
			return retval;
		}
        public static DataAccessModelConfiguration Create(string databaseName, string serverName, string userName, string password, bool poolConnections, string categories)
        {
            var retval = new DataAccessModelConfiguration
            {
                SqlDatabaseContextInfos = new List <SqlDatabaseContextInfo>
                {
                    new MySqlSqlDatabaseContextInfo
                    {
                        DatabaseName    = databaseName,
                        Categories      = categories,
                        ServerName      = serverName,
                        PoolConnections = poolConnections,
                        UserName        = userName,
                        Password        = password
                    }
                },
                ConstraintDefaultsConfiguration = { IndexedStringMaximumLength = 255 }
            };

            return(retval);
        }
		public abstract RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration);
 public AssemblyKey(Type dataAccessModelType, DataAccessModelConfiguration configuration)
     : this(dataAccessModelType, configuration.GetMd5())
 {
 }
Exemple #12
0
        private static Assembly BuildAssembly(TypeDescriptorProvider typeDescriptorProvider, DataAccessModelConfiguration configuration)
        {
            DataAccessObjectTypeBuilder dataAccessObjectTypeBuilder;

            var hash               = configuration.GetSha1();
            var assemblyName       = new AssemblyName(typeDescriptorProvider.DataAccessModelType.Assembly.GetName().Name + "." + typeDescriptorProvider.DataAccessModelType.Name);
            var sharedAssemblyName = new AssemblyName("Shaolinq.GeneratedDataAccessModel");
            var assemblyBuilder    = AppDomain.CurrentDomain.DefineDynamicAssembly(sharedAssemblyName, AssemblyBuilderAccess.RunAndSave);
            var moduleBuilder      = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + "." + hash + ".dll");

            var propertiesBuilder = moduleBuilder.DefineType("$$$DataAccessModelProperties", TypeAttributes.Class, typeof(object));

            var assemblyBuildContext = new AssemblyBuildContext(assemblyBuilder, propertiesBuilder);

            var dataAccessModelTypeBuilder = new DataAccessModelTypeBuilder(assemblyBuildContext, moduleBuilder);

            dataAccessModelTypeBuilder.BuildTypePhase1(typeDescriptorProvider.DataAccessModelType);

            var typeDescriptors = typeDescriptorProvider.GetTypeDescriptors();

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = new DataAccessObjectTypeBuilder(typeDescriptorProvider, assemblyBuildContext, moduleBuilder, typeDescriptor.Type);
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(1));
            }

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = assemblyBuildContext.TypeBuilders[typeDescriptor.Type];
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(2));
            }

            assemblyBuildContext.DataAccessModelPropertiesTypeBuilder.CreateType();
            dataAccessModelTypeBuilder.BuildTypePhase2();

            bool saveConcreteAssembly;

            bool.TryParse(ConfigurationManager.AppSettings["Shaolinq.SaveConcreteAssembly"], out saveConcreteAssembly);

#if DEBUG
            const bool isInDebugMode = true;
#else
            const bool isInDebugMode = false;
#endif

            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
            if (saveConcreteAssembly || isInDebugMode)
            {
                ActionUtils.IgnoreExceptions(() => assemblyBuilder.Save(assemblyName + "." + hash + ".dll"));
            }

            return(assemblyBuilder);
        }
Exemple #13
0
        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 this.typeDescriptorsByType.Values)
            {
                foreach (var columnInfo in QueryBinder.GetColumnInfos(this, typeDescriptor.PersistedProperties.ToArray()))
                {
                    typeDescriptor.propertyDescriptorByColumnName[columnInfo.ColumnName] = columnInfo.RootProperty;
                }
            }

            this.ModelTypeDescriptor = new ModelTypeDescriptor(this, dataAccessModelType);
        }
Exemple #14
0
 public AssemblyKey(Type dataAccessModelType, DataAccessModelConfiguration configuration)
     : this(dataAccessModelType, configuration.GetMd5())
 {
 }
Exemple #15
0
        private static Assembly BuildAssembly(TypeDescriptorProvider typeDescriptorProvider, DataAccessModelConfiguration configuration)
        {
            DataAccessObjectTypeBuilder dataAccessObjectTypeBuilder;
            var serializedConfiguration = XmlSerializer <DataAccessModelConfigurationUniqueKey> .New().SerializeToString(new DataAccessModelConfigurationUniqueKey(configuration));

            var filename = GetFileName(typeDescriptorProvider, configuration.GeneratedAssembliesSaveDirectory, serializedConfiguration, out var fullhash);

            if (configuration.SaveAndReuseGeneratedAssemblies ?? false)
            {
                if (filename != null && File.Exists(filename))
                {
                    var candidate = Assembly.LoadFile(filename);

                    if (ReadResource(candidate, "configuration.xml") == serializedConfiguration &&
                        ReadResource(candidate, "sha1.txt") == fullhash)
                    {
                        return(candidate);
                    }
                }
            }

            var filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
            var typeDescriptors          = typeDescriptorProvider.GetTypeDescriptors();
            var assemblyName             = new AssemblyName(filenameWithoutExtension);
            var assemblyBuilder          = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, Path.GetDirectoryName(filename));
            var moduleBuilder            = assemblyBuilder.DefineDynamicModule(filenameWithoutExtension, filenameWithoutExtension + ".dll");

            assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(TypeUtils.GetConstructor(() => new GeneratedAssemblyAttribute()), Type.EmptyTypes));

            var propertiesBuilder    = moduleBuilder.DefineType("$$$DataAccessModelProperties", TypeAttributes.Class, typeof(object));
            var assemblyBuildContext = new AssemblyBuildContext(assemblyBuilder, moduleBuilder, propertiesBuilder);

            var dataAccessModelTypeBuilder = new DataAccessModelTypeBuilder(assemblyBuildContext, moduleBuilder);

            dataAccessModelTypeBuilder.BuildTypePhase1(typeDescriptorProvider.DataAccessModelType);

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = new DataAccessObjectTypeBuilder(typeDescriptorProvider, assemblyBuildContext, moduleBuilder, typeDescriptor.Type);
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(1));
            }

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = assemblyBuildContext.TypeBuilders[typeDescriptor.Type];
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(2));
            }

            assemblyBuildContext.DataAccessModelPropertiesTypeBuilder.CreateType();
            assemblyBuildContext.FinishConstantsContainer();
            assemblyBuildContext.ConstantsContainer.CreateType();

            dataAccessModelTypeBuilder.BuildTypePhase2();

#if DEBUG
            const bool isInDebugMode = true;
#else
            const bool isInDebugMode = false;
#endif

            var saveAssembly = configuration.SaveAndReuseGeneratedAssemblies ?? !isInDebugMode;

            if (saveAssembly)
            {
                try
                {
                    moduleBuilder.DefineManifestResource("configuration.xml", new MemoryStream(Encoding.UTF8.GetBytes(serializedConfiguration)), ResourceAttributes.Public);
                    moduleBuilder.DefineManifestResource("sha1.txt", new MemoryStream(Encoding.UTF8.GetBytes(fullhash)), ResourceAttributes.Public);

                    assemblyBuilder.Save(Path.GetFileName(filename));
                }
                catch
                {
                }
            }

            return(assemblyBuilder);
        }
        public override RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration)
        {
            var typeDescriptorProvider = new TypeDescriptorProvider(dataAccessModelType);
            var originalAssembly       = dataAccessModelType.Assembly;
            var builtAssembly          = BuildAssembly(typeDescriptorProvider, configuration);

            return(new RuntimeDataAccessModelInfo(typeDescriptorProvider, builtAssembly, originalAssembly));
        }
        public override RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration)
        {
            var key = new AssemblyKey(dataAccessModelType, configuration);

            RuntimeDataAccessModelInfo runtimeDataAccessModelInfo;

            lock (this.assemblyBuildInfosByKey)
            {
                if (!this.assemblyBuildInfosByKey.TryGetValue(key, out runtimeDataAccessModelInfo))
                {
                    runtimeDataAccessModelInfo = this.provider.GetDataAccessModelAssembly(dataAccessModelType, configuration);

                    this.assemblyBuildInfosByKey[key] = runtimeDataAccessModelInfo;
                }
            }

            return(runtimeDataAccessModelInfo);
        }
 public abstract RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration);
		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);
		}
        private static Assembly BuildAssembly(TypeDescriptorProvider typeDescriptorProvider, DataAccessModelConfiguration configuration)
        {
            DataAccessObjectTypeBuilder dataAccessObjectTypeBuilder;

            var configMd5       = configuration.GetMd5();
            var assemblyName    = new AssemblyName(typeDescriptorProvider.DataAccessModelType.Assembly.GetName().Name + "." + typeDescriptorProvider.DataAccessModelType.Name);
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            var moduleBuilder   = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + "." + configMd5 + ".dll");

            var assemblyBuildContext = new AssemblyBuildContext(assemblyBuilder);

            var dataAccessModelTypeBuilder = new DataAccessModelTypeBuilder(assemblyBuildContext, moduleBuilder);

            dataAccessModelTypeBuilder.BuildType(typeDescriptorProvider.DataAccessModelType);

            var typeDescriptors = typeDescriptorProvider.GetTypeDescriptors();

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = new DataAccessObjectTypeBuilder(typeDescriptorProvider, assemblyBuildContext, moduleBuilder, typeDescriptor.Type);
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(1));
            }

            foreach (var typeDescriptor in typeDescriptors)
            {
                dataAccessObjectTypeBuilder = assemblyBuildContext.TypeBuilders[typeDescriptor.Type];
                dataAccessObjectTypeBuilder.Build(new DataAccessObjectTypeBuilder.TypeBuildContext(2));
            }

            bool saveConcreteAssembly;

            bool.TryParse(ConfigurationManager.AppSettings["Shaolinq.SaveConcreteAssembly"], out saveConcreteAssembly);

#if DEBUG
            const bool isInDebugMode = true;
#else
            const bool isInDebugMode = false;
#endif

            if (saveConcreteAssembly || isInDebugMode)
            {
                ActionUtils.IgnoreExceptions(() => assemblyBuilder.Save(assemblyName + "." + configMd5 + ".dll"));
            }

            return(assemblyBuilder);
        }
        public override RuntimeDataAccessModelInfo GetDataAccessModelAssembly(Type dataAccessModelType, DataAccessModelConfiguration configuration)
        {
            var key = new AssemblyKey(dataAccessModelType, new DataAccessModelConfigurationUniqueKey(configuration));

            lock (this.buildingSet)
            {
                while (true)
                {
                    if (this.assemblyBuildInfosByKey.TryGetValue(key, out var runtimeDataAccessModelInfo))
                    {
                        return(runtimeDataAccessModelInfo);
                    }

                    if (this.buildingSet.Contains(key))
                    {
                        Monitor.Wait(this.buildingSet);

                        continue;
                    }

                    try
                    {
                        this.buildingSet.Add(key);

                        runtimeDataAccessModelInfo = this.provider.GetDataAccessModelAssembly(dataAccessModelType, configuration);

                        this.assemblyBuildInfosByKey[key] = runtimeDataAccessModelInfo;

                        return(runtimeDataAccessModelInfo);
                    }
                    finally
                    {
                        this.buildingSet.Remove(key);

                        Monitor.PulseAll(this.buildingSet);
                    }
                }
            }
        }