Beispiel #1
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);
        }
 public T ConvertValue <T>(object itemToConvert)
 {
     return(MethodHelper.ConvertValue <T>(itemToConvert));
 }
Beispiel #3
0
        internal static List <string> DeleteAbstract(ICustomRepository repository, object o, bool save = false)
        {
            var type            = o.GetType().GetActualType();
            var props           = FastDeepCloner.DeepCloner.GetFastDeepClonerProperties(type);
            var table           = "[" + type.GetCustomAttribute <Table>()?.Name ?? type.Name + "]";
            var primaryKey      = MethodHelper.GetPrimaryKey(o as IDbEntity);
            var primaryKeyValue = MethodHelper.ConvertValue <long>(primaryKey.GetValue(o));

            if (primaryKeyValue <= 0)
            {
                return(new List <string>());
            }
            var sql = new List <string>()
            {
                "DELETE " + table + Querys.Where(repository.GetDataBaseType() == DataBaseTypes.Sqllight).Column(primaryKey.GetPropertyName()).Equal(primaryKeyValue).Execute()
            };

            foreach (var prop in props.Where(x => !x.IsInternalType && x.GetCustomAttribute <IndependentData>() == null && x.GetCustomAttribute <ExcludeFromAbstract>() == null))
            {
                var value = prop.GetValue(o);

                if (value == null)
                {
                    continue;
                }
                var subSql       = new List <string>();
                var propType     = prop.PropertyType.GetActualType();
                var insertBefore = props.Any(x => x.GetCustomAttribute <ForeignKey>()?.Type == propType);
                if (FastDeepCloner.DeepCloner.GetFastDeepClonerProperties(propType).All(x => x.GetCustomAttribute <ForeignKey>()?.Type != type))
                {
                    if (!insertBefore)
                    {
                        continue;
                    }
                }
                if (value is IList)
                {
                    foreach (var item in value as IList)
                    {
                        subSql.AddRange(DeleteAbstract(repository, item));
                    }
                }
                else
                {
                    subSql.AddRange(DeleteAbstract(repository, value));
                }

                if (insertBefore)
                {
                    sql.InsertRange(sql.Count - 1, subSql);
                }
                else
                {
                    sql.AddRange(subSql);
                }
            }

            if (!save)
            {
                return(sql);
            }
            try
            {
                repository.CreateTransaction();
                // begin deleting all object that refer to the requasted object
                for (var i = sql.Count - 1; i >= 0; i--)
                {
                    var cmd = repository.GetSqlCommand(sql[i]);
                    cmd.ExecuteNonQuery();
                }
                repository.Commit();
            }
            catch (Exception e)
            {
                repository.Rollback();
                throw e;
            }
            return(sql);
        }
Beispiel #4
0
        internal static T LoadChildren <T>(T item, ICustomRepository repository, bool onlyFirstLevel = false, List <string> classes = null, List <string> ignoreList = null, Dictionary <long, List <string> > pathLoaded = null, string parentProb = null, long id = 0) where T : class
        {
            if (pathLoaded == null)
            {
                pathLoaded = new Dictionary <long, List <string> >();
            }
            switch (item)
            {
            case null:
                return(null);

            case IList _:
                foreach (var tItem in (IList)item)
                {
                    var entity = tItem as IDbEntity;
                    if (entity == null)
                    {
                        continue;
                    }
                    LoadChildren(entity, repository, onlyFirstLevel, classes, ignoreList, pathLoaded, parentProb, entity.Id);
                }
                break;

            default:
                if ((item as IDbEntity) == null)
                {
                    return(item);
                }
                (item as IDbEntity)?.ClearPropertChanges();
                var props = FastDeepCloner.DeepCloner.GetFastDeepClonerProperties(item.GetType());

                id = (item as IDbEntity).Id;
                foreach (var prop in props.Where(x => !x.IsInternalType && !x.ContainAttribute <ExcludeFromAbstract>()))
                {
                    var path = string.Format("{0}.{1}", parentProb ?? "", prop.Name).TrimEnd('.').TrimStart('.');
                    var propCorrectPathName = path?.Split('.').Length >= 2 ? string.Join(".", path.Split('.').Reverse().Take(2).Reverse()) : path;

                    if (classes != null && classes.All(x => x != propCorrectPathName))
                    {
                        continue;
                    }
                    if (ignoreList != null && ignoreList.Any(x => x == propCorrectPathName))
                    {
                        continue;
                    }

                    var propValue = prop.GetValue(item);
                    if (propValue != null)
                    {
                        continue;
                    }
                    if (pathLoaded.ContainsKey(id) && pathLoaded[id].Any(x => x == path))
                    {
                        continue;
                    }

                    if (!pathLoaded.ContainsKey(id))
                    {
                        pathLoaded.Add(id, new List <string>()
                        {
                            path
                        });
                    }
                    else if (pathLoaded[id].All(x => x != path))
                    {
                        pathLoaded[id].Add(path);
                    }

                    var propertyName = prop.Name;
                    if (path?.Split('.').Length >= 2)
                    {
                        propertyName = string.Join(".", path.Split('.').Reverse().Take(3).Reverse()) + "." + parentProb.Split('.').Last() + "." + propertyName;
                    }

                    var ttype = prop.PropertyType.GetActualType();

                    var key = props.FirstOrDefault(x => x.ContainAttribute <ForeignKey>() && x.GetCustomAttribute <ForeignKey>().Type == ttype);
                    if (key == null)
                    {
                        var column     = DeepCloner.GetFastDeepClonerProperties(ttype).FirstOrDefault(x => x.ContainAttribute <ForeignKey>() && x.GetCustomAttribute <ForeignKey>().Type == item.GetType());
                        var primaryKey = MethodHelper.GetPrimaryKey(item as IDbEntity);
                        if (column == null || primaryKey == null)
                        {
                            continue;
                        }
                        var keyValue = primaryKey.GetValue(item).ConvertValue <long?>();
                        if (!keyValue.HasValue)
                        {
                            continue;
                        }
                        var result = GetByColumn(keyValue.Value, column.Name, repository, prop.PropertyType);
                        prop.SetValue(item, result);
                        if (result != null && !onlyFirstLevel)
                        {
                            LoadChildren(result, repository, onlyFirstLevel, classes, ignoreList, pathLoaded, propertyName, id);
                        }
                        (item as IDbEntity)?.ClearPropertChanges();
                    }
                    else
                    {
                        var keyValue = MethodHelper.ConvertValue <long?>(key.GetValue(item));
                        if (!keyValue.HasValue)
                        {
                            continue;
                        }
                        var result = GetSqlById(keyValue.Value, repository, prop.PropertyType);
                        prop.SetValue(item, result);
                        if (result != null && !onlyFirstLevel)
                        {
                            LoadChildren(result, repository, onlyFirstLevel, classes, ignoreList, pathLoaded, propertyName, id);
                        }
                        (item as IDbEntity)?.ClearPropertChanges();
                    }
                }

                break;
            }

            return(item);
        }
Beispiel #5
0
        internal static long SaveObject(ICustomRepository repository, InterFace.IDbEntity o, bool isIndependentData = false, bool commit = false)
        {
            repository.CreateTransaction();
            try
            {
                var updateOnly = o.State == ItemState.Changed;
                o.State = ItemState.Added;// reset State
                var props            = DeepCloner.GetFastDeepClonerProperties(o.GetType());
                var primaryKey       = MethodHelper.GetPrimaryKey(o);
                var availableColumns = repository.ObjectColumns(o.GetType());
                var objectRules      = o.GetType().GetCustomAttribute <Rule>();
                var tableName        = o.GetType().GetCustomAttribute <Table>()?.Name ?? o.GetType().Name;

                object dbTrigger = null;
                if (objectRules != null && !CachedIDbRuleTrigger.ContainsKey(o.GetType()))
                {
                    dbTrigger = Activator.CreateInstance(objectRules.RuleType) as object;
                    CachedIDbRuleTrigger.Add(o.GetType(), dbTrigger);
                }
                else if (objectRules != null)
                {
                    dbTrigger = CachedIDbRuleTrigger[o.GetType()];
                }

                if (primaryKey == null)
                {
                    return(0);
                }
                var value = primaryKey.GetValue(o).ConvertValue <long?>();
                if (value <= 0)
                {
                    value = null;
                }
                else if (value.HasValue && !updateOnly)
                {
                    var data = Select(repository, o.GetType(), Querys.Where(repository.GetDataBaseType() == DataBaseTypes.Sqllight).Column(primaryKey.GetPropertyName()).Equal(value.Value).ToQueryItem).Rows.FirstOrDefault();
                    if (data != null)
                    {
                        o.Merge(data.ToObject(o.GetType()) as InterFace.IDbEntity);
                    }
                }


                if (!updateOnly)
                {
                    dbTrigger?.GetType().GetMethod("BeforeSave").Invoke(dbTrigger, new List <object>()
                    {
                        repository, o
                    }.ToArray());          // Check the Rule before save
                }
                o.State = ItemState.Added; // reset State

                var sql  = "UPDATE [" + (o.GetType().GetCustomAttribute <Table>()?.Name ?? o.GetType().Name) + "] SET ";
                var cols = props.Where(x => availableColumns.FindByPrimaryKey <bool>(x.GetPropertyName()) && x.IsInternalType && x.GetCustomAttribute <ExcludeFromAbstract>() == null && x.GetCustomAttribute <PrimaryKey>() == null);

                if (!value.HasValue)
                {
                    sql  = "INSERT INTO [" + tableName + "](" + string.Join(",", cols.Select(x => "[" + x.GetPropertyName() + "]")) + ") Values(";
                    sql += string.Join(",", cols.Select(x => "@" + x.GetPropertyName())) + ");";
                    sql += repository.GetDataBaseType() == DataBaseTypes.Sqllight ? " select last_insert_rowid();" : " SELECT IDENT_CURRENT('" + tableName + "');";
                }
                else
                {
                    sql += string.Join(",", cols.Select(x => "[" + x.GetPropertyName() + "]" + " = @" + MethodHelper.GetPropertyName(x)));
                    sql += Querys.Where(repository.GetDataBaseType() == DataBaseTypes.Sqllight).Column(primaryKey.GetPropertyName()).Equal(value).Execute();
                }

                var cmd = repository.GetSqlCommand(sql);

                foreach (var col in cols)
                {
                    var v = col.GetValue(o);
                    if (col.ContainAttribute <ForeignKey>() && MethodHelper.ConvertValue <long?>(v) == 0)
                    {
                        var ob              = props.FirstOrDefault(x => x.PropertyType == col.GetCustomAttribute <ForeignKey>().Type);
                        var obValue         = ob?.GetValue(o) as InterFace.IDbEntity;
                        var independentData = ob?.GetCustomAttribute <IndependentData>() != null;
                        if (obValue != null)
                        {
                            v = MethodHelper.ConvertValue <long>(MethodHelper.GetPrimaryKey(obValue).GetValue(obValue)) <= 0 ?
                                SaveObject(repository, obValue, independentData) :
                                MethodHelper.ConvertValue <long>(MethodHelper.GetPrimaryKey(obValue).GetValue(obValue));
                        }
                    }

                    repository.AddInnerParameter(cmd, col.GetPropertyName(), v, repository.GetSqlType(col.PropertyType));
                }

                if (!value.HasValue)
                {
                    value = MethodHelper.ConvertValue <long>(repository.ExecuteScalar(cmd));
                }
                else
                {
                    repository.ExecuteNonQuery(cmd);
                }

                if (updateOnly)
                {
                    return(value.Value);
                }
                dbTrigger?.GetType().GetMethod("AfterSave").Invoke(dbTrigger, new List <object>()
                {
                    repository, o, value.Value
                }.ToArray());                                                                                                               // Check the Rule before save

                primaryKey.SetValue(o, value);
                foreach (var prop in props.Where(x => !x.IsInternalType && x.GetCustomAttribute <ExcludeFromAbstract>() == null))
                {
                    var independentData = prop.GetCustomAttribute <IndependentData>() != null;
                    var type            = prop.PropertyType.GetActualType();
                    var oValue          = prop.GetValue(o);
                    if (oValue == null)
                    {
                        continue;
                    }

                    if (oValue is IList)
                    {
                        foreach (var item in (IList)oValue)
                        {
                            if (DeepCloner.GetFastDeepClonerProperties(item.GetType()).Any(x => x.GetCustomAttribute <ForeignKey>()?.Type == o.GetType()))
                            {
                                DeepCloner.GetFastDeepClonerProperties(item.GetType()).First(x => x.GetCustomAttribute <ForeignKey>()?.Type == o.GetType()).SetValue(item, value);
                            }
                            var res        = SaveObject(repository, item as InterFace.IDbEntity, independentData);
                            var foreignKey = FastDeepCloner.DeepCloner.GetFastDeepClonerProperties(o.GetType()).FirstOrDefault(x => x.GetCustomAttribute <ForeignKey>()?.Type == type);
                            if (foreignKey == null || MethodHelper.ConvertValue <long>(foreignKey.GetValue(o)) > 0)
                            {
                                continue;
                            }
                            foreignKey.SetValue(o, res);
                            o.State = ItemState.Changed;
                        }
                    }
                    else
                    {
                        if (DeepCloner.GetFastDeepClonerProperties(oValue.GetType()).Any(x => x.GetCustomAttribute <ForeignKey>()?.Type == o.GetType()))
                        {
                            DeepCloner.GetFastDeepClonerProperties(oValue.GetType()).First(x => x.GetCustomAttribute <ForeignKey>()?.Type == o.GetType()).SetValue(oValue, value);
                        }

                        var res        = SaveObject(repository, oValue as InterFace.IDbEntity, independentData);
                        var foreignKey = FastDeepCloner.DeepCloner.GetFastDeepClonerProperties(o.GetType()).FirstOrDefault(x => x.GetCustomAttribute <ForeignKey>()?.Type == type);
                        if (foreignKey == null || MethodHelper.ConvertValue <long>(foreignKey.GetValue(o)) > 0)
                        {
                            continue;
                        }
                        foreignKey.SetValue(o, res);
                        o.State = ItemState.Changed;
                    }
                }

                if (o.State == ItemState.Changed) // a change has been made outside the function Save
                {
                    SaveObject(repository, o, false);
                }
                if (!commit)
                {
                    return(value.Value);
                }
                repository.Commit();
                return(value.Value);
            }
            catch (Exception e)
            {
                repository.Rollback();
                throw e;
            }
        }