The following provides some guidelines for writing Responder submit rules:
General Guidelines
- Order of rules is significant.
- It is preferable to modify the DataSet instead of writing to the database directly. Submit rules trigger on changes in a DataSet. Therefore, it is highly recommended that if a submit rule needs to modify records, it should do so by editing the rows in the DataSet. This allows subsequent rules to react to these changes. A submit rule can write to the database directly, but these changes will not be detected by other submit rules and therefore is not the recommended practice. Submit rules can query the database; however, there may be differences between "pending" changes in the DataSet and the database.
- Exceptions thrown by submit rules are ignored and logged as errors. There is one exception: a submit rule can fire a SubmitRuleException (or RuleException) with the Cancel property set to true - this terminates processing, causes the database transaction to be aborted, and the exception returns to the calling application.
- The entire submit rule processing logic is surrounded by a single database transaction A submit rule must not commit or rollback this transaction - the transaction is available so that the submit rule can participate in and query within the context of the same transaction.
- Narrow the conditions under which the submit rule fires as much as possible in the ConstructCondition logic.
- Use (Miner.Data.Access) DbConvert instead of System.Convert to convert (boxed) DataRow columns values in actual types (int, double, etc) as DbConvert has better null/DBNull handling.
- Use (Miner.Data.Access) DbCompare to compare DataRow column values. These comparison methods are null/DBNull aware and type-agnostic and provides more resilient code. For example, different ADO.NET providers use different types to represent values. System.Data.Oracle client always uses type decimal to represent number columns. This means that incident["status"] == 1 will always return false because incident["status"] is decimal and 1 is integer which is not equivalent. However, DbCompare.IsEqual(incident["status"], 1) will work as expected. There are many other useful comparison methods - including DBNull handling. See Responder documentation for more details.
Advanced Guidelines
- A submit rule should execute as fast as possible. Slow operations, such as querying (or interacting with) external systems should be avoided as this may negatively affect performance.
- Avoid calling IDataServices.Submit from a submit rule. If you think this is necessary then please contact us and we can discuss the alternatives. That said, calling IDataServices.SubmitAndForget (asynchronous) may be useful - although it shouldn't be necessary. It is preferable to modifying the records in the "pending" DataSet.
- A submit rule can execute a DataRequest object. It is recommended to do so using IDataServices.ExecuteRequest. The request is invoked locally and does not exit the remoting boundary.
- This is an advanced scenario (and is rarely necessary), but a submit rule can call session.BeginTransaction (and therefore is responsible for calling the corresponding Commit/RollbackTransaction) in order to isolate itself from the rest of the processing. Note: This establishes a second connection and a new transaction (since ADO.NET only supports 1 tx per connection) - this is *not* a nested transaction and has no connection to the original transaction. We use this for our (pessimistic) network locking scheme where the transaction lifetime is independent from other processing.