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; } }