"Open framework that encourages
convention
over
configuration
, using C# code,
not
XML files
, to model at the
right level of abstraction
and achieve
deadlines
.
...but also has a full Linq provider, and syncs the schema for you!"
Search
»
Back
Transaction class
Modified on 2009/02/16 00:53
by helen
Categorized as
SignumEngine
== Introduction == Transaction class is the most common class using [Scopes|Scope pattern] to handle transactions in a clean and easy nestable way. It uses the same strategy as Microsoft's [http://msdn.microsoft.com/es-es/library/system.transactions.transactionscope(VS.80).aspx|TransactionScope] from the client code point of view: Implicit transaction in the transaction scope defined by the [http://msdn.microsoft.com/en-us/library/yh598w02.aspx|using statement]. Internally, however, it has some differences, let's take a look: == How to use it == Some time ago this code tended to be very cumbersome. Implicit transactions, however, made a big step forward, creating a easy model to handle transactions writing less code: <code lang="cs"> private static void FixBugs() { var wrongBugs = from b in ¬Database.Query<¬BugDN>() where b.Status == Status.Fixed && !b.End.HasValue select b.ToLazy(); foreach (var lazyBug in wrongBugs) { ¬BugDN bug = lazyBug.Retrieve(); bug.Description += "- Fix it!"; bug.Save(); } } </code> This simple code contains a lot of intensive work with the database: the query, retrieving each Bug, and saving it. Currently all of these are independent operations, so we could have some problems: * If some exception is thrown in the middle of the process we end up having some bugs fixed and some remaing. This could have some problems, like duplicating ''- Fix It!'' for some bugs once you r-run the process. * It could take some time from the time the query gets executed, to the moment the BugDN is actually retrieved (we are using lazy objects). Someone could have modified the Bug in the meantime. In Signum Engine, every atomic operation, like retrieving an object, executing a query, or saving an object graph is transactional without you having to do anything. Sometimes, however, you want to glue together operations in the same transaction, so you have a consistent view of the database (not influenced by concurrency) and you don't commit partial modifications if something goes wrong in the middle. This is as easy as surrounding the code with a suing Transaction block, like this: <code lang="cs"> private static void FixBugs() { using (¬Transaction tr = new ¬Transaction()) { var wrongBugs = from b in ¬Database.Query<¬BugDN>() where b.Status == Status.Fixed && !b.End.HasValue select b.ToLazy(); foreach (var lazyBug in wrongBugs) { ¬BugDN bug = lazyBug.Retrieve(); bug.End = bug.Start.AddDays(7); bug.Save(); } tr.Commit(); } } </code> What we do is to create a transaction, do our work, and at the end Commit it. When the transaction is disposed, if it hasn't been committed, it gets roll backed automatically, so the code gets much more simple to read. {s:Note| Be careful with return statements, remember to commit the transaction before exit the method!} The nice thing is that all the old code doesn't need to be updated with the new declared Transaction tr, because transactions are handled implicitly. Also, if you call FixBigs from a method like this: <code lang="cs"> private static void FixTheWorld() { FixBugs(); FixCustomers(); FixDevelopers(); } </code> You cold also glue together all the inner transactions creating a new, wider one: <code lang="cs"> private static void FixTheWorld() { using (¬Transaction tr = new ¬Transaction()) { FixBugs(); FixCustomers(); FixDevelopers(); tr.Commit(); } } </code> The end result is that the inner transactions become silent without you having to change anything, so the code is __much more easy to combine__. There are some options for useful overloadings of Transaction constructor: <code lang="cs"> //Creates a nestable transaction public Transaction() //If forceNew == true, the new transaction is independent from the parent public Transaction(bool forceNew) //Allows to change the IsolationLevel of the transaction (only if ends up being the parent trnasaction) public Transaction(¬IsolationLevel isolationLevel) //The two above combined public Transaction(bool forceNew, ¬IsolationLevel? isolationLevel) </code>. == How it works (AdvancedTopic) == There are some things that are important to know in advanced scenarios: * Each time you force a new transaction, a RealTransaction is pushed into the transaction stack. There are different stacks per Thread and Connection. * Transaction knows the start time of the transaction, that is used to update the [BaseEntities|Entity] Ticks column for concurrency control. * Transactions are completely silent on MockConnections * Transaction takes control of SqlClient [http://msdn.microsoft.com/es-es/library/system.data.sqlclient.sqltransaction%28VS.80%29.aspx|SqlTransaction] and [http://msdn.microsoft.com/es-es/library/system.data.sqlclient.sqlconnection(VS.80).aspx|SqlConnection]. You can access them on {{Transaction.CurrentConnection}} and {{Transaction.CurrentTransaccion}} (they will be created if not created yet). {s:Note| Probably you where expecting [Connection] to handle SqlConnection, but Connection is more like a potential connection than an actual one, and it's shared between threads. Transaction has better knowledge about when to start the connection and how long to keep it open. }
Meta Keywords:
Meta Description:
Change Comment:
Signum Framework Site by
Signum Software
is licensed under a
Creative Commons Attribution 3.0 License
.
Powered by
ScrewTurn Wiki
version 3.0.5.600.