Undo and Redo |
[This is preliminary documentation and is subject to change.]
This topic contains the following sections:
When undo is enabled by calling BcfDataSetEnableMemoryUndo or EnableFileUndo or EnableFileUndoSession new transactions will serve as undo/redo step after BcfTransactionCommit. See:
Undo/Redo works based on CalculationWorks.BusinessModelBcfTransaction rollback-info. BCF allows to exclude columns from undo/redo. This can be done by setting IncludeInUndo = false.
Note |
---|
BcfColumnSetupIncludeInUndo has no effect on Rollback. |
A transaction without items will be dropped after commit. Undo- and redo-stacks remain unchanged.
When updating undo-excluded cells only, the transaction will be empty if columns have set ShouldPersist = false; otherwise transaction will contain an update-CurrentDataStateId-item.
Those cells will be recomputed when their row is restored (undo delete | redo add) or when a cell-value it depends on, is changed by undo/redo.
There are three overloads to enable undo/redo.
Using BcfDataSet.EnableMemoryUndo() initializes memory based undo. Memory undo causes BcfUndoRepository to keep references on transaction-objects until the stacks are cleared.
Memory undo is easy to use and very fast.
Caution |
---|
Having many large transactions in repository over a long time may cause garbage collection issue: application blocks while GC collects. For example having 100.000 transactions on undo-stack and each of them contains 10.000 items, GC has to check at least (depends on item-type) 1.000.000.000 object-references when it collects 2nd generation objects. |
Using BcfDataSet.EnableFileUno(Stream, Stream) initializes file based undo. File based undo requires some additional code for serialization. To cause BCF-Editor to generate the code check "Support Undo Persistence" on "Project-Tab / DataSetSetup".
Use file based undo if you cannot limit MaxItems or when having columns containing large strings or byte-arrays.
Using BcfDataSet.EnableFileUndoSession(Stream, Stream, byte[]) initializes a file based undo session. File based undo session requires the same additional code like File Based Undo.
In opposition to File Based Undo a session is recoverable from the files. The dataset-schema the files were created must match the schema the files will be restored. Use the 3rd parameter to place a custom header inside a file to perform a compatibility-check (e.g. assembly version). Before restoring a session using RestoreUndoRepository check the compatibility using GetCustomHeaderAsync.
Caution |
---|
Do not use "File Based Undo Session"-feature as "Save As" - file-format. File migration after dataset-schema update is not supported. |
Special settings may cause different values when comparing results using memory and file undo. This is because lifetime of BcfRow depends on undo. Using memory-undo a deleted row will still be referenced. When undo delete the row-object will be restored as row. Using file-undo deleting a row will store cell-values in file and the row will be discarded. When undo delete, a new row-object is created and cell-values will be restored from file. So far so good. Having a not computed column with IncludeInUndo = false, after undo delete, using memory-undo the cell-value will be unchanged; using file-undo cell-value is columns default value. The same issue appears when adding fields to a row.
Note |
---|
BCF-Editor shows a warning when "Support Undo Persistence" is enabled and a column is not computed and excluded from undo. |
Note |
---|
Use always same undo/redo approach in product-code and unit-test. When using a file based approach, consider using memory-streams in unit-tests. |
File undo does not manage object-references. For example: having a string-array on main-row and on child-row a computed column simply references the parent-string-array. Every time a new string-array is set on parent row, all child-rows string-array cells are updated and for each cell the old and new string-arrays are serialized into file. When undo, parent and child string-arrays will be deserialized to distinct locations in memory. So all string-array cells have the same content but different references. Excluding the computed column from undo will result in reduced file-size, better performance and reference equality of string-arrays.
Check "Allow Partial Undo" on "Project-Tab / DataSetSetup".
Which columns should be excluded from undo?All computed columns, except calculation is expensive.
Since faults are not serializable all rules are excluded from undo when using file-undo. Memory undo without excluded columns causes dataset to accept values from undo-items without checking dependencies of computed columns and rules. All values will be restored and calculation will be omitted.
I have columns with non-serializable type. How to use file-undo?If the columns are computed, consider exclude from undo; otherwise define a surrogate-converter and configure it in BCF-Editor "Menu - View - TypeSettings".
I have a column (not computed) of a disposable type and use DiscardAction to dispose values. Using memory-undo will dispose the values not until Clear. How can I switch to file-undo? Will DiscardAction work with file-undo?DiscardAction works using file-undo. As like as for non-serializable types: create a not disposable and serializable surrogate-type and add surrogate-conversion.
I used List<int> as column type. After adding a column of int[] I get compiler warnings and or exceptions with "invalid DataContract".For the reason: read the messages and refer to .net documentation. It is an known limitation using DataContracts. To work around use same list-type anywhere or use a surrogate-converter for one type.
I use an interface as column type. When setting a cell-value I get an exception with "DataContract".Same issue appears with base-type columns and instance-types. You have to register all possible instance-types. BCF-Editor generates a partial class "{dataset-name}UndoItems" at the end of generated DataSetSetup-file. Create another part of the class and add the instance-types using the KnownType-Attribute (like other types in generated code).
// When having a column of IDictionary<int, string> not excluded from undo we have to configure a serialization surrogate to use file-based undo. // The surrogate type (the serializable type) is: KeyValuePair<TKey, TValue>[] // Place the surrogate converter code below info the project you referenced in BCF-Editor/Project tab/DataSetSetup/C# Project File // Add an entry in BCF-Editor/Edit/TypeSettings with Type=IDictionary<int, string> and SurrogateConverter=new DictionaryInterfaceSurrogate<int, string>() // Hint: Do not forget reanalyze (F5) after adding the code. Adjust namespaces. public class DictionaryInterfaceSurrogate<TKey, TValue> : BcfSurrogateConverter<IDictionary<TKey, TValue>, KeyValuePair<TKey, TValue>[]> { public override KeyValuePair<TKey, TValue>[] ConvertToSurrogate(IDictionary<TKey, TValue> value) { return value?.ToArray(); } public override IDictionary<TKey, TValue> ConvertToValue(KeyValuePair<TKey, TValue>[] dataContractObject) { return dataContractObject?.ToDictionary(e => e.Key, e => e.Value); } }