An other approach is using Serviced component mechanism that enables COM+ service. I feels a bit 2000'ish.
At last I decided to write my onwn code... And I feel quite prowd doing so...
I use Linq to SQL. Giving me some limitations. But this is my first shoot.
public class DbTools<T> where T : DataContext
{
public static void ExecuteWithRollback(string connectionName, Action<T> rollbackDelegate)
{
Recorder.Record(rollbackDelegate).Connection(connectionName).Rollback();
}
public static void ExecuteWithCommit(string connectionName, Action<T> rollbackDelegate)
{
Recorder.Connection(connectionName).Record(rollbackDelegate).Commit();
}
public static Recorder<T> Recorder
{
get
{
return new Recorder<T>();
}
}
}
{
public static void ExecuteWithRollback(string connectionName, Action<T> rollbackDelegate)
{
Recorder.Record(rollbackDelegate).Connection(connectionName).Rollback();
}
public static void ExecuteWithCommit(string connectionName, Action<T> rollbackDelegate)
{
Recorder.Connection(connectionName).Record(rollbackDelegate).Commit();
}
public static Recorder<T> Recorder
{
get
{
return new Recorder<T>();
}
}
}
This is used as an entrypoint for my fluid syntax. logig is in the Recorder class
public class Recorder<T> where T : DataContext
{
private string _connectionName;
private IDbConnection _connection;
private Action<T> _executeDelegate;
internal Recorder()
{
}
public Recorder<T> Connection(IDbConnection connection)
{
_connection = connection;
return this;
}
public Recorder<T> Connection(string connectionName)
{
_connectionName = connectionName;
return this;
}
public Recorder<T> Record(Action<T> executeDelegate)
{
_executeDelegate = executeDelegate;
return this;
}
private object GetConnectionObject()
{
if (_connection != null)
return _connection;
return ConfigurationManager.ConnectionStrings[_connectionName].ConnectionString;
}
private void Execute(bool commit)
{
if (_executeDelegate == null)
throw new RecorderException("ExecuteDelegate not set");
if (_connection == null && string.IsNullOrEmpty(_connectionName))
throw new RecorderException("Connection not set");
// Hack to make new-call Work with parameters. Constraints don't cover this case
var dataContext = (T)Activator.CreateInstance(typeof(T), GetConnectionObject());
dataContext.Connection.Open();
try
{
using (var trans = dataContext.Connection.BeginTransaction())
{
dataContext.Transaction = trans;
{
_executeDelegate(dataContext);
if (commit)
trans.Commit();
else
trans.Rollback();
}
}
}
catch (Exception e)
{
throw new Exception("Transaction might be disposed. Try to set <TEntity>.Options.TrackingMode = TrackingMode.Connected. If this don't resove the probblme theres properbly a part of the code that calls either Transaction.Dispose/Commit/Rollback or Connection.Dispose/Close", e);
}
}
public void Commit()
{
Execute(true);
}
public void Rollback()
{
Execute(false);
}
}
{
private string _connectionName;
private IDbConnection _connection;
private Action<T> _executeDelegate;
internal Recorder()
{
}
public Recorder<T> Connection(IDbConnection connection)
{
_connection = connection;
return this;
}
public Recorder<T> Connection(string connectionName)
{
_connectionName = connectionName;
return this;
}
public Recorder<T> Record(Action<T> executeDelegate)
{
_executeDelegate = executeDelegate;
return this;
}
private object GetConnectionObject()
{
if (_connection != null)
return _connection;
return ConfigurationManager.ConnectionStrings[_connectionName].ConnectionString;
}
private void Execute(bool commit)
{
if (_executeDelegate == null)
throw new RecorderException("ExecuteDelegate not set");
if (_connection == null && string.IsNullOrEmpty(_connectionName))
throw new RecorderException("Connection not set");
// Hack to make new-call Work with parameters. Constraints don't cover this case
var dataContext = (T)Activator.CreateInstance(typeof(T), GetConnectionObject());
dataContext.Connection.Open();
try
{
using (var trans = dataContext.Connection.BeginTransaction())
{
dataContext.Transaction = trans;
{
_executeDelegate(dataContext);
if (commit)
trans.Commit();
else
trans.Rollback();
}
}
}
catch (Exception e)
{
throw new Exception("Transaction might be disposed. Try to set <TEntity>.Options.TrackingMode = TrackingMode.Connected. If this don't resove the probblme theres properbly a part of the code that calls either Transaction.Dispose/Commit/Rollback or Connection.Dispose/Close", e);
}
}
public void Commit()
{
Execute(true);
}
public void Rollback()
{
Execute(false);
}
}
Now I can test my code.
[TestFixture]
public class DbToolsTest
{
[Test]
public void TestRollback()
{
DbTools<BNSystemDataContext>.ExecuteWithRollback("BNSystemConnectionString", MethodBodyToRollback);
}
[Test]
public void TestRecordWithComplete()
{
DbTools<BNSystemDataContext>.Recorder.Connection("BNSystemConnectionString").Record(MethodBodyToRollback).Rollback();
}
private static void MethodBodyToRollback(BNSystemDataContext context)
{
const string socialSecurityNumber = "24129912345";
var boPerson = new BOPerson(context) {Options = {TrackingMode = TrackingMode.Connected}};
var p = boPerson.LoadSecurityNumber(socialSecurityNumber);
boPerson.Update(p, "1234567");
}
}