Ejemplo n.º 1
0
        /// <summary>
        /// Return the new added column, tables or modified prooerty
        /// Property Rename is not supported. renaming a property x will end up removing the column x and adding column y so there will be dataloss
        /// Adding a primary key is not supported either
        /// Abstract classes are ignored by default
        /// </summary>
        /// <assembly> Null for the current executed Assembly </assembly>
        /// <returns></returns>
        protected CodeToDataBaseMergeCollection GetCodeLatestChanges(Assembly assembly = null)
        {
            var codeToDataBaseMergeCollection = new CodeToDataBaseMergeCollection(this);

            MethodHelper.GetDbEntitys(assembly ?? this.GetType().Assembly).ForEach(x =>
            {
                if (!x.IsAbstract) // Ignore abstract classes by default
                {
                    _dbSchema.GetDatabase_Diff(x, codeToDataBaseMergeCollection);
                }
            });
            return(codeToDataBaseMergeCollection);
        }
Ejemplo n.º 2
0
        public CodeToDataBaseMergeCollection GetDatabase_Diff(Type tableType, CodeToDataBaseMergeCollection str = null, List <Type> createdTables = null)
        {
            str = str ?? new CodeToDataBaseMergeCollection(_repository);
            if (tableType.GetCustomAttribute <ExcludeFromAbstract>() != null || _alreadyControlled.Any(x => x == tableType))
            {
                return(str);
            }
            _repository.CreateSchema(tableType);
            tableType = tableType.GetActualType();
            _alreadyControlled.Add(tableType);
            createdTables = createdTables ?? new List <Type>();
            if (createdTables.Any(x => x == tableType) || tableType.GetPrimaryKey() == null)
            {
                return(str);
            }

            if (CodeToDataBaseMergeCollection.ExecutedData.ContainsKey(tableType.FullName + _repository.DataBaseTypes.ToString()))
            {
                return(str);
            }

            createdTables.Add(tableType);
            var table               = _repository.GetColumnSchema(tableType);
            var tableName           = tableType.TableName();
            var props               = DeepCloner.GetFastDeepClonerProperties(tableType).Where(x => x.CanRead && !x.ContainAttribute <ExcludeFromAbstract>());
            var codeToDataBaseMerge = new CodeToDataBaseMerge()
            {
                Object_Type = tableType
            };
            var isPrimaryKey = "";

            if (!IsValidName(tableName.Name))
            {
                throw new EntityException(tableName.Name + " is not a valid Name for the current provider " + _repository.DataBaseTypes);
            }


            if (!table.Values.Any())
            {
                codeToDataBaseMerge.Sql = new StringBuilder($"CREATE TABLE {tableName.GetName(_repository.DataBaseTypes)} (");
                foreach (var prop in props.Where(x => (x.GetDbTypeByType(_repository.DataBaseTypes) != null || !x.IsInternalType ||
                                                       x.ContainAttribute <JsonDocument>() || x.ContainAttribute <XmlDocument>()) &&
                                                 !x.ContainAttribute <ExcludeFromAbstract>()).GroupBy(x => x.Name).Select(x => x.First())
                         .OrderBy(x => x.ContainAttribute <PrimaryKey>() ? null : x.Name))
                {
                    if (!prop.IsInternalType && !prop.ContainAttribute <JsonDocument>() && !prop.ContainAttribute <XmlDocument>())
                    {
                        if (!str.Any(x => x.Object_Type == prop.PropertyType.GetActualType()) && createdTables.All(x => x != prop.PropertyType.GetActualType()))
                        {
                            GetDatabase_Diff(prop.PropertyType, str, createdTables);
                        }
                        continue;
                    }

                    isPrimaryKey = prop.ContainAttribute <PrimaryKey>() ? prop.GetPropertyName() : isPrimaryKey;
                    var foreignKey = prop.GetCustomAttribute <ForeignKey>();
                    var dbType     = prop.GetDbTypeByType(_repository.DataBaseTypes);
                    var propName   = string.Format("[{0}]", prop.GetPropertyName());
                    codeToDataBaseMerge.Sql.Append(propName + " ");
                    if (!IsValidName(prop.GetPropertyName()))
                    {
                        throw new Exception(prop.GetPropertyName() + " is not a valid Name for the current provider " + _repository.DataBaseTypes);
                    }



                    if (!prop.ContainAttribute <PrimaryKey>() || _repository.DataBaseTypes == DataBaseTypes.Mssql)
                    {
                        codeToDataBaseMerge.Sql.Append(dbType + " ");
                    }


                    if (foreignKey != null && createdTables.All(x => x != foreignKey.Type))
                    {
                        GetDatabase_Diff(foreignKey.Type, str, createdTables);
                    }

                    if (prop.ContainAttribute <PrimaryKey>())
                    {
                        if (prop.PropertyType.IsNumeric() && prop.GetCustomAttribute <PrimaryKey>().AutoGenerate)
                        {
                            codeToDataBaseMerge.Sql.Append(_repository.DataBaseTypes == DataBaseTypes.Mssql ? "IDENTITY(1,1) NOT NULL," : (_repository.DataBaseTypes == DataBaseTypes.Sqllight ? " Integer PRIMARY KEY AUTOINCREMENT," : " BIGSERIAL PRIMARY KEY,"));
                        }
                        else
                        {
                            codeToDataBaseMerge.Sql.Append(_repository.DataBaseTypes == DataBaseTypes.Mssql ? "NOT NULL," : " " + dbType + "  PRIMARY KEY,");
                        }
                        continue;
                    }

                    if (foreignKey != null)
                    {
                        var key = propName + "-" + tableName.GetName(_repository.DataBaseTypes);
                        if (!str.Keys.ContainsKey(key))
                        {
                            str.Keys.Add(key, new Tuple <string, ForeignKey>(tableName.GetName(_repository.DataBaseTypes), foreignKey));
                        }
                    }

                    codeToDataBaseMerge.Sql.Append((Nullable.GetUnderlyingType(prop.PropertyType) != null || prop.PropertyType == typeof(string)) && !prop.ContainAttribute <NotNullable>() ? " NULL," : " NOT NULL,");
                }

                if (str.Keys.Any() && _repository.DataBaseTypes == DataBaseTypes.Sqllight)
                {
                    while (str.Keys.Any(x => x.Value.Item1 == tableName.Name))
                    {
                        var key        = str.Keys.FirstOrDefault(x => x.Value.Item1 == tableName.Name);
                        var type       = key.Value.Item2.Type.GetActualType();
                        var keyPrimary = type.GetPrimaryKey().GetPropertyName();
                        var tb         = type.TableName();
                        codeToDataBaseMerge.Sql.Append("FOREIGN KEY(" + key.Key.Split('-')[0] + ") REFERENCES " + tb.GetName(_repository.DataBaseTypes) + "(" + keyPrimary + "),");
                        str.Keys.Remove(key.Key);
                    }
                }

                if (!string.IsNullOrEmpty(isPrimaryKey) && _repository.DataBaseTypes == DataBaseTypes.Mssql)
                {
                    codeToDataBaseMerge.Sql.Append(" CONSTRAINT [PK_" + tableName.Name + "] PRIMARY KEY CLUSTERED");
                    codeToDataBaseMerge.Sql.Append(" ([" + isPrimaryKey + "] ASC");
                    codeToDataBaseMerge.Sql.Append(")");
                    codeToDataBaseMerge.Sql.Append("WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]");
                    codeToDataBaseMerge.Sql.Append(") ON [PRIMARY]");
                }
                else
                {
                    if (_repository.DataBaseTypes == DataBaseTypes.Sqllight)
                    {
                        codeToDataBaseMerge.Sql = new StringBuilder(codeToDataBaseMerge.Sql.ToString().TrimEnd(','));
                    }
                    codeToDataBaseMerge.Sql.Append(")");
                }

                str.Add(codeToDataBaseMerge);
            }
            else
            {
                foreach (var prop in props.Where(x => (x.GetDbTypeByType(_repository.DataBaseTypes) != null || !x.IsInternalType) && !x.ContainAttribute <ExcludeFromAbstract>()).GroupBy(x => x.Name).Select(x => x.First())
                         .OrderBy(x => x.ContainAttribute <PrimaryKey>() ? null : x.Name))
                {
                    if (prop.ContainAttribute <ForeignKey>())
                    {
                        GetDatabase_Diff(prop.GetCustomAttribute <ForeignKey>().Type, str, createdTables);
                    }
                    var propType = prop.PropertyType;
                    if (prop.ContainAttribute <Stringify>() ||
                        prop.ContainAttribute <DataEncode>() ||
                        prop.ContainAttribute <ToBase64String>() ||
                        prop.ContainAttribute <JsonDocument>() ||
                        prop.ContainAttribute <XmlDocument>())
                    {
                        propType = typeof(string);
                    }

                    var modify = prop.IsInternalType || prop.ContainAttribute <JsonDocument>() || prop.ContainAttribute <XmlDocument>() ? (_repository.DataBaseTypes == DataBaseTypes.PostgreSql ? table.Get(prop.GetPropertyName().ToLower()) : table.Get(prop.GetPropertyName())) : null;
                    if (modify != null)
                    {
                        if (_repository.DataBaseTypes != DataBaseTypes.Sqllight && !(prop.GetDbTypeListByType(_repository.DataBaseTypes).Any(x => x.ToLower().Contains(modify.DataType.ToLower()))) && _repository.DataBaseTypes != DataBaseTypes.PostgreSql)
                        {
                            var constraine = Properties.Resources.DropContraine
                                             .Replace("@tb", $"'{tableName.Name}'").Replace("@col", $"'{prop.GetPropertyName()}'")
                                             .Replace("@schema", $"'{tableName.Schema ?? ""}'")
                                             .Replace("@TableName", "@" + counter++)
                                             .Replace("@ColumnName", "@" + counter++)
                                             .Replace("@fullName", "@" + counter++)
                                             .Replace("@DROP_COMMAND", "@" + counter++)
                                             .Replace("@FOREIGN_KEY_NAME", "@" + counter++);
                            codeToDataBaseMerge.Sql.Append(constraine);
                            codeToDataBaseMerge.Sql.Append($"\nALTER TABLE {tableName.GetName(_repository.DataBaseTypes)} ALTER COLUMN [{prop.GetPropertyName()}] {prop.GetDbTypeByType(_repository.DataBaseTypes)} {((Nullable.GetUnderlyingType(propType) != null || propType == typeof(string)) && !prop.ContainAttribute<NotNullable>() ? " NULL" : " NOT NULL")}");
                        }
                        else
                        {
                            if (!(prop.GetDbTypeListByType(_repository.DataBaseTypes).Any(x => x.ToLower().Contains(modify.DataType.ToLower()))) && _repository.DataBaseTypes == DataBaseTypes.PostgreSql)
                            {
                                codeToDataBaseMerge.Sql.Append($"\nALTER TABLE {tableName.GetName(_repository.DataBaseTypes)} ALTER COLUMN [{prop.GetPropertyName()}] TYPE {prop.GetDbTypeByType(_repository.DataBaseTypes)}, ALTER COLUMN [{prop.GetPropertyName()}] SET DEFAULT {Querys.GetValueByTypeSTRING(MethodHelper.ConvertValue(null, propType), _repository.DataBaseTypes)};");
                            }
                        }
                    }
                    else if (!prop.IsInternalType && !prop.ContainAttribute <JsonDocument>() && !prop.ContainAttribute <XmlDocument>())
                    {
                        GetDatabase_Diff(prop.PropertyType, str, createdTables);
                    }
                    else
                    {
                        codeToDataBaseMerge.Sql.Append(string.Format("\nALTER TABLE {0} ADD " + (_repository.DataBaseTypes == DataBaseTypes.PostgreSql ? "COLUMN" : "") + " [{1}] {2} {3} DEFAULT {4};", tableName.GetName(_repository.DataBaseTypes), prop.GetPropertyName(), prop.GetDbTypeByType(_repository.DataBaseTypes),
                                                                     (Nullable.GetUnderlyingType(propType) != null || propType == typeof(string)) && !prop.ContainAttribute <NotNullable>() ? " NULL" : " NOT NULL", Querys.GetValueByTypeSTRING(MethodHelper.ConvertValue(null, propType), _repository.DataBaseTypes)));
                    }
                }
            }

            var colRemove = new CodeToDataBaseMerge()
            {
                Object_Type = tableType
            };

            // Now lets clean the table and remove unused columns
            foreach (var col in table.Values.Where(x =>
                                                   !props.Any(a => string.Equals(x.ColumnName, a.GetPropertyName(), StringComparison.CurrentCultureIgnoreCase) &&
                                                              (a.GetDbTypeByType(_repository.DataBaseTypes) != null || (!a.IsInternalType || a.ContainAttribute <JsonDocument>() || a.ContainAttribute <XmlDocument>())) &&
                                                              !a.ContainAttribute <ExcludeFromAbstract>())))
            {
                if (_repository.DataBaseTypes != DataBaseTypes.Sqllight)
                {
                    if (_repository.DataBaseTypes == DataBaseTypes.Mssql)
                    {
                        var constraine = Properties.Resources.DropContraine
                                         .Replace("@tb", $"'{tableName.Name}'")
                                         .Replace("@col", $"'{col.ColumnName}'")
                                         .Replace("@schema", $"'{tableName.Schema ?? ""}'")
                                         .Replace("@TableName", "@" + counter++)
                                         .Replace("@ColumnName", "@" + counter++)
                                         .Replace("@fullName", "@" + counter++)
                                         .Replace("@DROP_COMMAND", "@" + counter++)
                                         .Replace("@FOREIGN_KEY_NAME", "@" + counter++);

                        colRemove.Sql.Append(constraine);
                    }
                    colRemove.Sql.Append(string.Format("\nALTER TABLE {0} DROP COLUMN IF EXISTS [{1}];", tableName.GetName(_repository.DataBaseTypes), col.ColumnName));
                }
                else
                {
                    colRemove.Sql.Append(string.Format("DROP TABLE IF exists [{0}_temp];\nCREATE TABLE [{0}_temp] AS SELECT {1} FROM [{0}];", tableName.Name, string.Join(",", table.Values.ToList().FindAll(x =>
                                                                                                                                                                                                             props.Any(a => string.Equals(x.ColumnName, a.GetPropertyName(), StringComparison.CurrentCultureIgnoreCase) &&
                                                                                                                                                                                                                       (a.GetDbTypeByType(_repository.DataBaseTypes) != null || !a.IsInternalType) &&
                                                                                                                                                                                                                       !a.ContainAttribute <ExcludeFromAbstract>())).Select(x => x.ColumnName))));
                    colRemove.Sql.Append(string.Format("DROP TABLE [{0}];\n", tableName.Name));
                    colRemove.Sql.Append(string.Format("ALTER TABLE [{0}_temp] RENAME TO [{0}]; ", tableName.Name));
                }
                colRemove.DataLoss = true;
            }
            str.Add(colRemove);

            foreach (var prop in props.Where(x => !x.IsInternalType && !x.ContainAttribute <JsonDocument>() && !x.ContainAttribute <XmlDocument>() && !x.ContainAttribute <XmlDocument>() && !x.ContainAttribute <ExcludeFromAbstract>()).GroupBy(x => x.Name).Select(x => x.First()))
            {
                var type = prop.PropertyType.GetActualType();
                if (type.GetPrimaryKey() != null)
                {
                    GetDatabase_Diff(type, str, createdTables);
                }
            }

            str.Add(codeToDataBaseMerge);
            return(str);
        }