/// <summary> /// Apply the changes in this diff to the given target /// </summary> public ApplyAction ApplyTo(DatonDef datondef, Persiston target) { if (MainTable.Count == 0) { return(ApplyAction.NoChanges); } if (datondef.MultipleMainRows) { bool anyChanges = false; var field = target.GetType().GetField(datondef.MainTableDef.Name); var targetList = Utils.CreateOrGetFieldValue <IList>(target, field); if (targetList == null) { throw new Exception($"Row class {target.GetType().Name} must include field member {datondef.MainTableDef.Name}"); } foreach (var source in MainTable) { anyChanges |= ApplyDiffRowToList(datondef.MainTableDef, source, targetList); } return(anyChanges ? ApplyAction.Changes : ApplyAction.NoChanges); } else { //handle top level edge cases to ensure the key matches the new/modified/delete status of the top row if (MainTable.Count != 1) { throw new Exception("For single-main-table persistons the diff may only include the single main row"); } bool diffIsNewRow = MainTable[0].Kind == DiffKind.NewRow; if (diffIsNewRow != target.Key.IsNew) { throw new Exception("The key specifies a new row but the diff does not indicate a new row; or the key specifies modified/delete but the diff indicates a new row"); } var source = MainTable[0]; if (source.Kind == DiffKind.DeletedRow) { return(ApplyAction.PersistonDeleted); } //reached here, so its a plain update of the single row; primary key ignored bool anyChanges = false; foreach (string colName in source.Columns.Keys) { anyChanges |= SetValue(datondef.MainTableDef, source, colName, target); } //child tables anyChanges |= ApplyChildTables(source, target, target.GetType()); return(anyChanges ? ApplyAction.Changes : ApplyAction.NoChanges); } }
/// <summary> /// Validate the persiston and populate Errors in this instance with the problems found. /// </summary> public async Task ValidatePersiston(DatonDef datondef, Persiston daton) { Errors = new List <string>(); //built-in validation var r = RecurPoint.FromDaton(datondef, daton); if (r is RowRecurPoint rr) { Validate(rr); } else if (r is TableRecurPoint rt) { Validate(rt); } //custom validation await daton.Validate(User, message => Errors.Add(message)); }
/// <summary> /// Save persiston to database /// </summary> /// <param name="pristineDaton">null or the version before the diff was applied</param> /// <param name="modifiedDaton">the validated final version</param> /// <param name="diff">the difference between pristine and modified, which is what this method inspects to make the changes</param> public virtual async Task Save(IDbConnection db, IUser user, Persiston pristineDaton, Persiston modifiedDaton, PersistonDiff diff) { //called for each row in traversal; return true to recurse over children async Task <(object, bool)> rowCallback(RowChangingData cdata) { if (cdata.DiffRow.Kind == DiffKind.DeletedRow) { await DeleteRowWithCascade(db, cdata.TableDef, cdata.PristineRow); return(null, false); //don't recur to children of deleted row } object newpk = await InsertUpdateRow(db, cdata); return(newpk, true); } var tdata = new TraversalData { ParentKey = null, TableDef = diff.DatonDef.MainTableDef, DiffRowList = diff.MainTable, PristineList = null, ModifiedList = null, ProcessRowF = rowCallback }; if (diff.DatonDef.MultipleMainRows) { var mainListField = diff.DatonDef.Type.GetField(tdata.TableDef.Name); tdata.PristineList = mainListField.GetValue(pristineDaton) as IList; tdata.ModifiedList = mainListField.GetValue(modifiedDaton) as IList; await TraverseDiffList(tdata, null, null); } else { await TraverseDiffList(tdata, pristineDaton, modifiedDaton); } }