Signum Framework Logo
"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!"
Login
RSS

Search

»



Main
Index
Map
Videos
Download
Source code
Tutorials
Forum
FAQ



Image



PoweredBy

Introduction

The main task of every framework is to reduce all the technical difficulties that make it hard to express your intentions: your business data and logic.

The Scope pattern is intensively used by Signum Framework to accomplish that. ConnectionScope, Transaction, ObjectCache are good examples.

The Problem

Usually, what starts looking like a simple idea, like transferring money from one account to other,

 //Intended code
 void Transfer(string fromAccount, string toAccount, decimal amount)
 {   
   //Retrieve accounts
   //Test from account balance
   //Withdraw from fromAccount
   //Deposit to toAccount
 }

ends up getting dirty with transactions, connections, sql commands, etc... Very often, this artefact reaches your methods prototype or the structure of your business logic lasses.

The problem gets worse when you try to reuse the code, all these artefacts create potential problems:

  • Does the method expect a connectionString or Connection?
  • Should the connection be open?
  • Is the method transactional?
  • Does it commit the transaction, or leave it open?
  • What timeout does it use for SqlCommands?
  • Does it take accountNumber, account ids, or Account objects?

And any time one of these questions has an answer that doesn't meet your requirements, you have two options:

  • Bad: Do some overloading of each method, each of them adapting to the main one, if possible --> Loots of methods that do nothing.
  • Bad: Do some trick (like adding a parameter to control if the transaction is committed or not) --> Increasing complexity of code, and make it more intricate.
  • Even Worse: Copy paste the old method and make custom changes --> Code duplication.

//'Real Life' sample
void Transfer(string fromAccount, string toAccount, decimal amount, int timeOut)
{
    using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ConnectionString))
    {
        using (SqlTransaction tran = con.BeginTransaction())
        {
            try
            {
                Account fromAcc = RetriveAccount(con, tran, fromAccount, timeOut);

Account toAcc = RetriveAccount(con, tran, toAccount, timeOut);

Widthdraw(fromAcc, con, tran, amount, timeOut, timeOut, false);

Deposit(toAcc, con, tran, amount, timeOut, timeOut, false);

tran.Commit(); } catch(Exception e) { tran.Rollback(); throw; } } } }

private Account RetriveAccount(string accountNumber){...} private Account RetriveAccount(SqlConnection con, SqlTransaction tran, string accountNumber, int timeout) {...}

private void Deposit(string accountNumber, decimal amount){...} private void Deposit(Account accountNumber, SqlConnection con, SqlTransaction tran, decimal amount, int timeOut, bool commit){...}

private void Widthdraw(string accountNumber,decimal amount){...} private void Widthdraw(Account fromAcc, SqlConnection con, SqlTransaction tran, decimal amount, int timeOut, bool commit){...}
.

The solution

An old school solution to this problem is to store a lot of this information in static variables (a global variable with object-oriented accent).

Global variables, however, do not play well with multi-threading applications and, since Signum Engine is all about writing the server code, multi-threading is Must!, so we use ThreadStaticAttribute that allows us to declare static variables that have different values for different threads. This Attribute is so useful it deserves it's own keyword!

Scope pattern, finally, is just a pattern that simplifies cleaning and overriding the value of this ThreadStatic variable in a scope of your code.

You can think of a Scope as a variable that lives in your stack (in your thread) but that can be accessed by child methods without you having to pass it as a parameter all the time.

Simple Example

Signum Engine uses SqlCommand and SqlConnections internally to communicate with the database, but it doesn't ask you for a command time-out value every time it needs to make one. You can easily set the default time-out in Connection class, but if you want some time-consuming region of your code to have a larger time-out, just use CommandTimeoutScope class:

  using (new CommandTimeoutScope(20))
  {
      Transfer(fromAccount, toAccount, amount); 
  }

With this simple thing, you save the ugly time-out parameter that you have to pass before to all children affected, more importantly, you save yourself from refactoring the code once you have to include different time-out capabilities.

You will find similar behaviour in:
Also, Signum.Utilities defines some base classes for implementing the Scope pattern in simple scenarios.

Creative Commons License 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.