Click or drag to resize

Transactions

[This is preliminary documentation and is subject to change.]

This topic contains the following sections:

A BcfTransaction is datasets update-context. It hosts the container for rollback-items, controls the cell and rule calculation, ensures the data consistence and works as undo/redo step.

A BcfDataSet can have one CurrentTransaction at a time.

BcfTransaction Basics

Implicit Transactions

Each data-update on a BcfDataSet is tracked and rollback info is stored in a transaction. If BcfDataSetCurrentTransaction is null when update will be performed, a transaction will be created (implicit transaction). Implicit transaction will auto commit after the update is performed.

Explicit Transactions

To put many data-updates into one transaction simply create a transaction, perform the updates and than commit the transaction.

Subtransactions

BCF supports subtransactions. They are usually explicit created and can be identified using BcfTransactionIsSubTransaction property. The recommended way to create a subtransaction is calling BcfTransactionBeginSubTransaction. However with BcfDataSetCurrentTransaction is not null calling BcfDataSetBeginTransaction will also create a subtransaction.

Since BCF 4.0 creating and committing a subtransaction will not longer enforce relation and key constraints and calculation. If you want to perform calculation on every change see example on BcfMicroTransactionModeSubtransaction.

Commit

Commit works different on subtransactions and transactions. When a subtransaction commits, its rollback-info is added to its parent transaction. The subtransaction is not longer referenced. When a transaction commits, it enforces relation constraints and calculates. After commit the transaction will be moved to datasets UndoRepository.

Rollback

Rollback restores the BcfDataSet-state of the moment the transaction was created.

Compute

By default calculation is deferred until the transaction commits. If you need to update computed values within a transaction call BcfTransactionCompute.

Advanced Transaction Settings

Name

The BcfTransactionBuilderName-property should indicate the intention the transaction was created. When creating a transaction using BeginTransaction a name can be specified. For custom naming of implicit transaction see Customize Transactions.

MicroTransactionMode

Many data-update operations are compounded but have to be atomic. For example: Setting a cell-value of a column with a trigger and the trigger sets other cells values. The trigger action may fail when a value-constraint is violated. The BcfTransactionMicroTransactionMode specifies how to deal with atomic compounded updates.

Default MicroTransactionMode can be specified at BcfDataSetTransactionFactory.

EnforceConstraints

There are four relation constraints:

Enforcing relation and key constraints can deferred until transaction Commit. This is required e.g. when creating a parent-row with a relation having ChildEndMinConstraint > 0. Use EnforceConstraints-property to get and BcfTransactionSetEnforceConstraints change the behavior.

Default value can be specified at BcfDataSetTransactionFactory.

Customize Transactions

TransactionFactory

TransactionBuilder

The BcfTransactionBuilder class is used as parameter to create transactions.

You can create a custom TransactionBuilder class inheriting BcfTransactionBuilder. If you do so, ensure the custom class is used as typeparameter in your BcfTransactionFactoryBaseTTransaction, TTransactionInfo, TTransactionBuilder implementation.

TransactionInfo

Custom Transaction Class

Create a custom transaction class inheriting BcfTransaction to control its behavior and access protected members.

When created a custom transaction class, ensure it is used as typeparameter in your BcfTransactionFactoryBaseTTransaction, TTransactionInfo, TTransactionBuilder implementation.

Example

The example code implements custom:

This example illustrates how to:

  1. Add a "Comment" property to builder and transaction.
  2. Add a time-stamp containing transaction commit time.
  3. Set default EnforceConstraints = false
  4. Initializes IBcfTransactionFactory at BcfDataSetSetup
When using undo/redo and after transaction commit, you can access the new properties using myDataSet.UndoRepository.UndoItems.Cast<MyTransactionInfo>

C#
using System;
using CalculationWorks.BusinessModel;
using CalculationWorks.BusinessModel.Design;
using CalculationWorks.BusinessModel.UndoRedo;

namespace MyNamespace 
{
    public class MyTransactionBuilder : BcfTransactionBuilder 
    {
        /// <summary>
        /// An additional comment
        /// </summary>
        public string Comment { get; set; }
    }
    public class MyTransactionInfo : IBcfTransactionInfo 
    {
        /// <summary>
        /// The <see cref="IBcfTransactionInfo.Name"/> implementation.
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// The <see cref="MyTransactionBuilder.Comment">additional comment from builder</see>.
        /// </summary>
        public string Comment { get; set; }
        /// <summary>
        /// Date and time when transaction was committed.
        /// </summary>
        public DateTime? CommittedAt { get; set; }
    }
    public class MyTransaction : BcfTransaction 
    {
        public MyTransaction([NotNull] BcfDataSet dataSet, [NotNull] BcfTransactionBuilder builder) : base(dataSet, builder) 
        {
            // Info is not initialized for subtransactions
            if(IsSubTransaction) Info = DataSet.TransactionFactory.CreateTransactionInfoObject();
            Comment = ((MyTransactionBuilder)builder).Comment;
        }
        public MyTransactionInfo MyInfo 
        {
            get { return (MyTransactionInfo)Info; }
        }
        /// <summary>
        /// The <see cref="MyTransactionBuilder.Comment">additional comment from builder</see>.
        /// </summary>
        public string Comment 
        {
            get { return MyInfo.Comment; }
            set { MyInfo.Comment = value; }
        }
        /// <summary>
        /// Date and time when transaction was committed.
        /// </summary>
        public DateTime? CommittedAt 
        {
            get { return MyInfo.CommittedAt; }
        }
        /// <summary>
        /// If using file-based undo here is the last chance for updates on <see cref="MyInfo"/> before it is serialized.
        /// </summary>
        protected override void OnBeforeCommitComplete() 
        { 
            MyInfo.CommittedAt = DateTime.Now;
        }
    }
    public class MyTransactionFactory : BcfTransactionFactoryBase<MyTransaction, MyTransactionInfo, MyTransactionBuilder> {
        public MyTransactionFactory([NotNull] BcfDataSet dataSet) : base(dataSet) 
        {
            // changing default because it sucks
            EnforceConstraints = false;
        }
        public override MyTransaction CreateTransaction(BcfTransactionBuilder transactionBuilder) 
        {
            return new MyTransaction(DataSet, transactionBuilder);
        }
    }
    // Handwritten part for BCF-Editor generated partial DataSetSetup-class.
    partial class MyDataSetSetup 
    {
        // BCF-Editor generates "partial void Initialized();" we can implement in same cs-project the generated DataSetSetup-class resides.
        partial void Initialized() 
        {
            CreateTransactionFactory = dataSet => new MyTransactionFactory(dataSet);
        }
    }
}