using System; using System.Linq; using System.Reflection; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions { public static class DefinitionFactory { public static TableDefinition GetTableDefinition(Type modelType, ISqlSyntaxProvider sqlSyntax) { //Looks for NPoco's TableNameAtribute for the name of the table //If no attribute is set we use the name of the Type as the default convention var tableNameAttribute = modelType.FirstAttribute(); string tableName = tableNameAttribute == null ? modelType.Name : tableNameAttribute.Value; var tableDefinition = new TableDefinition {Name = tableName}; var objProperties = modelType.GetProperties().ToList(); foreach (var propertyInfo in objProperties) { //If current property has an IgnoreAttribute then skip it var ignoreAttribute = propertyInfo.FirstAttribute(); if (ignoreAttribute != null) continue; //If current property has a ResultColumnAttribute then skip it var resultColumnAttribute = propertyInfo.FirstAttribute(); if (resultColumnAttribute != null) continue; //Looks for ColumnAttribute with the name of the column, which would exist with ExplicitColumns //Otherwise use the name of the property itself as the default convention var columnAttribute = propertyInfo.FirstAttribute(); string columnName = columnAttribute != null ? columnAttribute.Name : propertyInfo.Name; var columnDefinition = GetColumnDefinition(modelType, propertyInfo, columnName, tableName, sqlSyntax); tableDefinition.Columns.Add(columnDefinition); //Creates a foreignkey definition and adds it to the collection on the table definition var foreignKeyAttributes = propertyInfo.MultipleAttribute(); if (foreignKeyAttributes != null) { foreach (var foreignKeyAttribute in foreignKeyAttributes) { var foreignKeyDefinition = GetForeignKeyDefinition(modelType, propertyInfo, foreignKeyAttribute, columnName, tableName); tableDefinition.ForeignKeys.Add(foreignKeyDefinition); } } //Creates an index definition and adds it to the collection on the table definition var indexAttribute = propertyInfo.FirstAttribute(); if (indexAttribute != null) { var indexDefinition = GetIndexDefinition(modelType, propertyInfo, indexAttribute, columnName, tableName); tableDefinition.Indexes.Add(indexDefinition); } } return tableDefinition; } public static ColumnDefinition GetColumnDefinition(Type modelType, PropertyInfo propertyInfo, string columnName, string tableName, ISqlSyntaxProvider sqlSyntax) { var definition = new ColumnDefinition{ Name = columnName, TableName = tableName, ModificationType = ModificationType.Create }; //Look for specific Null setting attributed a column var nullSettingAttribute = propertyInfo.FirstAttribute(); if (nullSettingAttribute != null) { definition.IsNullable = nullSettingAttribute.NullSetting == NullSettings.Null; } //Look for specific DbType attributed a column var databaseTypeAttribute = propertyInfo.FirstAttribute(); if (databaseTypeAttribute != null) { definition.CustomDbType = databaseTypeAttribute.DatabaseType; } else { definition.PropertyType = propertyInfo.PropertyType; } //Look for Primary Key for the current column var primaryKeyColumnAttribute = propertyInfo.FirstAttribute(); if (primaryKeyColumnAttribute != null) { string primaryKeyName = string.IsNullOrEmpty(primaryKeyColumnAttribute.Name) ? string.Format("PK_{0}", tableName) : primaryKeyColumnAttribute.Name; definition.IsPrimaryKey = true; definition.IsIdentity = primaryKeyColumnAttribute.AutoIncrement; definition.IsIndexed = primaryKeyColumnAttribute.Clustered; definition.PrimaryKeyName = primaryKeyName; definition.PrimaryKeyColumns = primaryKeyColumnAttribute.OnColumns ?? string.Empty; definition.Seeding = primaryKeyColumnAttribute.IdentitySeed; } //Look for Size/Length of DbType var lengthAttribute = propertyInfo.FirstAttribute(); if (lengthAttribute != null) { definition.Size = lengthAttribute.Length; } //Look for Constraint for the current column var constraintAttribute = propertyInfo.FirstAttribute(); if (constraintAttribute != null) { definition.ConstraintName = constraintAttribute.Name ?? string.Empty; definition.DefaultValue = constraintAttribute.Default ?? string.Empty; } return definition; } public static ForeignKeyDefinition GetForeignKeyDefinition(Type modelType, PropertyInfo propertyInfo, ForeignKeyAttribute attribute, string columnName, string tableName) { var referencedTable = attribute.Type.FirstAttribute(); var referencedPrimaryKey = attribute.Type.FirstAttribute(); string referencedColumn = string.IsNullOrEmpty(attribute.Column) ? referencedPrimaryKey.Value : attribute.Column; string foreignKeyName = string.IsNullOrEmpty(attribute.Name) ? string.Format("FK_{0}_{1}_{2}", tableName, referencedTable.Value, referencedColumn) : attribute.Name; var definition = new ForeignKeyDefinition { Name = foreignKeyName, ForeignTable = tableName, PrimaryTable = referencedTable.Value, OnDelete = attribute.OnDelete, OnUpdate = attribute.OnUpdate }; definition.ForeignColumns.Add(columnName); definition.PrimaryColumns.Add(referencedColumn); return definition; } public static IndexDefinition GetIndexDefinition(Type modelType, PropertyInfo propertyInfo, IndexAttribute attribute, string columnName, string tableName) { string indexName = string.IsNullOrEmpty(attribute.Name) ? string.Format("IX_{0}_{1}", tableName, columnName) : attribute.Name; var definition = new IndexDefinition { Name = indexName, IndexType = attribute.IndexType, ColumnName = columnName, TableName = tableName, }; if (string.IsNullOrEmpty(attribute.ForColumns) == false) { var columns = attribute.ForColumns.Split(Constants.CharArrays.Comma).Select(p => p.Trim()); foreach (var column in columns) { definition.Columns.Add(new IndexColumnDefinition {Name = column, Direction = Direction.Ascending}); } } if (string.IsNullOrEmpty(attribute.IncludeColumns) == false) { var columns = attribute.IncludeColumns.Split(',').Select(p => p.Trim()); foreach (var column in columns) { definition.IncludeColumns.Add(new IndexColumnDefinition { Name = column, Direction = Direction.Ascending }); } } return definition; } } }