|
||
This chapter explains how to work with transactions in Kentico CMS API. Transactions are usually used to ensure database consistency.
Kentico CMS uses SQL transactions to ensure the system database consistency when performing complex operations consisting of multiple steps. In such cases the whole operation forms a single block of SQL commands that can be rolled back when an error occurs or committed if the complete operation was successful. Transaction handling is provided by CMS.DataEngine library, in the GeneralConnection class.
Please note that you should always use a single connection for every operation within the open transaction to avoid deadlocks.
The transactions flow in Kentico CMS looks like this:
Please note: Always pass the existing connection as a parameter if you call a method that accesses the database within the transaction.
Please use the following example as a template in all the methods that work with transactions:
[C#]
using System.Data; using CMS.DocumentEngine; using CMS.FileManager; using CMS.WorkflowEngine;
/// <summary> /// Sample method /// </summary> /// <param name="conn">Data connection to use</param>
public void DoSomethingInTransaction(GeneralConnection conn) {
// Process within transaction using (CMSTransactionScope tr = new CMSTransactionScope()) { // --- HERE YOU CAN USE YOUR DATABASE ACCESS CODE LIKE: ---- DataSet ds = conn.ExecuteQuery("cms.user.selectall", null, null, null);
// --- IF YOU NEED TO USE TREEPROVIDER, ALWAYS INSTANTIATE IT WITH EXISTING CONNECTION TreeProvider tree = new TreeProvider(null, conn);
// --- ALWAYS USE METHOD OVERRIDES THAT USE CONNECTION (TREEPROVIDER) PARAMETER --- AttachmentInfo ai = DocumentHelper.GetAttachment(Guid.NewGuid(), 1, tree);
// Commit transaction, you can also use the Complete() method to do the same tr.Commit(); }
} |
You may also work with the TreeProvider class instead of connection objects. In that case, use its Connection property the same way like the GeneralConnection in this example and use the same connection object in all operations inside the transaction.
You can also ensure that all operations performed by a piece of code use the same connection. This can be done similarly as with the transactions above. Please use the code extract below as a template for such code:
using CMS.DataEngine;
private void DoSomethingWithinTheSameConnection() { using (CMSConnectionScope cs = new CMSConnectionScope()) { // perform your actions } } |
The connection of the scope is put to the context, making all nested connections use the same backend connection. If you do not specify a connection using the constructor parameter, one will be created automatically. The overridden constructor gets the existing connection and also a parameter which says if the connection should be open within this scope. In case it is, it is also closed automatically:
new CMSConnectionScope(IDataConnection conn, bool openConnection) |
|
Connections within threads
When a new thread is created, the connection is not passed to the thread to maintain thread safety. So you should create a dedicated within the thread CMSConnectionScope for its operations.
|
Nesting connection and transaction scopes
It is possible to nest connection and transaction scopes, because basically there is always a CMSConnectionScope around CMSTransactionScope so the outermost code handles the connection. So you may do following:
•Use CMSConnectionScope inside another CMSConnectionScope - this does exactly the same as not having the inner one. The outer one says "use this connection for everything within", so the inner one doesn't make sense in this context and is skipped. There is no need to do this at all (but this happens if you use some of our methods within your scope, yours (the outer one) will always take priority).
•Use CMSTransactionScope inside CMSConnectionScope - the transaction just uses the existing connection from the scope. This can be used if you do some reading and then use the same connection for writing within the transaction. A single connection will always be used.
•Use CMSTransactionScope inside another CMSTransactionScope - it acts the same way as for the connections. The outer one still needs to commit the change, so the inner one is not used (as if it didn't exist).
•Use CMSConnectionScope inside CMSTransactionScope - this is a situation where you use our reading code in your transaction code. A CMSTransactionScope does always have a CMSConnectionScope around it (either an existing one or a new one), so it is basically the same scenario as the first one, the inner connection is not used.