Click or drag to resize

BCF Documentation

This topic contains the following sections:

  • The Mission
  • The Layers
The Mission

Coding smart object-models begins easy and becomes more and more difficult. Most you have to add some techniques hard to combine. Named:

  • Computed properties
  • Validation
  • Change notification
  • Undo and redo
  • Transaction and sub-transaction processing
Over the time I used different approaches in several projects.
  • "Computed properties will be computed in getter." - This means on each property get the result will be computed. Having large models or complex structures like trees you will encounter performance issues and possibly stackoverflows. This means also there is no changed-event to update bindings. It works in many cases - accidentally. For example: computed columns in System.Data.DataTable computes and raises changed-event in getter. So bindings will not update UI unless any other value of same row is updated.

    "Caching values and forwarding changes". Caching will not prevent stackoverflow caused by large or complex models. Forwarding changes causes a new performance issue. Example: your invoice-model has a DiscountPercent and a Total (SUM(invoice-items.Total)) property and invoice-item-model has DefaultPrice and Total(DefaultPrice*(100-invoice.DiscountPercent)/100) properties. So updating invoice.DiscountPercent will cause recalculate each invoice-item.Total and each invoice-item.Total will cause recalculate invoice.Total. So invoice.Total is calculated COUNT(invoice-items)-times. This becomes really funny when each change will force an UI-update.

    It would be nice to have something like a BcfTransaction that manages calculation order so each value is computed when it has to be computed and only once - maximum.

  • INotifyPropertyChanged change notification is the way WPF/XAML databinding works and it works great. Simple apps with data-model == business-model == view-model can be build very fast. When a simple app becomes a complex one you may encounter the 2nd issue above and have an ugly calculation-UI-update-loop task. Different requirements on change-notification is one reason for using a distinct model layers.

    It would be nice to have something like a BcfTransactionCreateChangeSet where I can get the changes made. And a pony. And an automatic updating BcfViewManager for WPF/XAML and one BcfViewManager for winforms.

  • "Validation is not hard." Validation is hard as calculation. There are the same requirements on when and how often validation is performed like on calculation. Managing calculation dependencies is a BCF key feature. One of the most underrated issues is redundancy in calculation parameter access and change notification. A simple model property like Total (SUM(invoice-items.Total)) needs change notification from invoice-items collection and from collection items Total property. When adding change handlers in code or implement explicit calculation calls you define the dependencies twice. Maintaining those models becomes more and more expensive.

    It would be nice to have dictinct function (see Column: Settings/Function; Rule: Settings/Function) and parameter declaration (see Parameter: Settings/Access Path) for automatic value access and change notification.

  • "Let's start without undo." Probably your model will get functions like create and restore snapshots. It becomes ugly when the model grows and restoring a snapshot takes more than one second. The key to undo/redo function basing on changes is transaction processing.

    It would be nice to simply enable undo/redo (BcfDataSetEnableMemoryUndo).

My conclusion: A smart object-model without transaction processing becomes expensive. Building object-models supporting transaction processing, change and parameter management from scratch is also expensive. That's why I built BCF.

The Layers

Business-Model

Building relational models is proven in practice. Dot-net framework 1.0 introduced the DataSet as relational model. Many developers knows it and so BCF uses similar names and features. A BCF business-model is represented by BcfDataSet and contains Tables and Relations. In opposition to DataSet the BcfDataSet supports transaction processing, undo/redo and other features. Some DataSet features like serialization, database-mapping and databinding are not implemented; but can be added (e.g. for databinding see next section).

BCF-Editor generates two code-files for the business-model. The DataSet - like a typed DataSet and the DataSetSetup. The dataset file contains the typed BcfDataSet, typed BcfTables, typed BcffRows and other classes for comfortable coding. The datasetsetup file contains a BcfDataSetSetup defining the schema and helper classes (e.g. for serialization).

At runtime data updates are implicit wrapped in transactions. On BcfTransactionCommit the computed columns and rules will be updated. To pack multiple updates into a single transaction simply create a transaction (BeginTransaction), perform the updates and then commit it. Having an open transaction defers calculation. To update calculated values in an open transaction call BcfTransactionCompute.

View-Model

Because of different databinding requirements and dependencies BCF-Editor can generate two different viewmanagers. One for WPF/XAML (BcfViewManager) and one for winforms (BcfViewManager). The generated code file contains a typed viewmanager, typed table-views (equivalent to System.Data.DataView) and typed row-views (equivalent to System.Data.DataRowView).

At runtime a viewmanager manages a copy of its BcfDataSets data. When a transaction (not subtransaction) commits the copies are updated and the associated change-events are raised.

Data-Model

BCF-Editor supports mapping to and from typed data-models by generating Data-Transfer-Object (DTO) interfaces and load/update methods. When using an untyped data-layer like SQL-command generators or runtime generated mappers the generic built-in methods are sufficient and there is no benefit creating the DTO-Interfaces.