Refactoring ITransactionManager to remove usage of TransationScope

Adds a new RequireNew() method to start a new transactions.

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros 2013-02-25 15:10:50 -08:00
parent da6fd5fac6
commit c5aa1382e7
6 changed files with 99 additions and 95 deletions

View File

@ -27,6 +27,7 @@ using Orchard.Messaging.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
using Orchard.Security.Providers;
using Orchard.Tests.ContentManagement;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
using Orchard.Users.Controllers;
@ -65,7 +66,8 @@ namespace Orchard.Tests.Modules.Users.Controllers {
builder.RegisterType<UserService>().As<IUserService>();
builder.RegisterType<UserPartHandler>().As<IContentHandler>();
builder.RegisterType<OrchardServices>().As<IOrchardServices>();
builder.RegisterType<TransactionManager>().As<ITransactionManager>();
builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ITransactionManager>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();

View File

@ -84,7 +84,7 @@ namespace Orchard.Tests.ContentManagement {
_manager = _container.Resolve<IContentManager>();
}
public class TestSessionLocator : ISessionLocator {
public class TestSessionLocator : ISessionLocator, ITransactionManager {
private readonly ISession _session;
public TestSessionLocator(ISession session) {
@ -94,6 +94,15 @@ namespace Orchard.Tests.ContentManagement {
public ISession For(Type entityType) {
return _session;
}
public void Demand() {
}
public void RequireNew() {
}
public void Cancel() {
}
}
[Test]

View File

@ -1,21 +1,20 @@
using System;
using System.Collections;
using System.Data;
using NHibernate;
using NHibernate.SqlCommand;
using NHibernate.Type;
using Orchard.Logging;
namespace Orchard.Data {
public class SessionLocator : ISessionLocator {
public class SessionLocator : ISessionLocator, ITransactionManager, IDisposable {
private readonly ISessionFactoryHolder _sessionFactoryHolder;
private readonly ITransactionManager _transactionManager;
private ISession _session;
private ITransaction _transaction;
private bool _cancelled;
public SessionLocator(
ISessionFactoryHolder sessionFactoryHolder,
ITransactionManager transactionManager) {
public SessionLocator(ISessionFactoryHolder sessionFactoryHolder) {
_sessionFactoryHolder = sessionFactoryHolder;
_transactionManager = transactionManager;
Logger = NullLogger.Instance;
}
@ -24,18 +23,76 @@ namespace Orchard.Data {
public ISession For(Type entityType) {
Logger.Debug("Acquiring session for {0}", entityType);
if (_session == null) {
((ITransactionManager)this).Demand();
var sessionFactory = _sessionFactoryHolder.GetSessionFactory();
_transactionManager.Demand();
Logger.Information("Openning database session");
_session = sessionFactory.OpenSession(new SessionInterceptor());
}
return _session;
}
void ITransactionManager.Demand() {
EnsureSession();
if (_transaction == null) {
Logger.Debug("Creating transaction on Demand");
_transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted);
}
}
void ITransactionManager.RequireNew() {
EnsureSession();
if (_cancelled) {
_transaction.Rollback();
_transaction.Dispose();
_transaction = null;
}
else {
if (_transaction != null) {
_transaction.Commit();
}
}
Logger.Debug("Creating transaction on RequireNew");
_transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted);
}
void ITransactionManager.Cancel() {
Logger.Debug("Transaction cancelled flag set");
if (_transaction != null && !_transaction.WasRolledBack) {
_transaction.Rollback();
}
_cancelled = true;
}
void IDisposable.Dispose() {
if (_transaction != null) {
if (!_cancelled) {
Logger.Debug("Marking transaction as complete");
_transaction.Commit();
}
else {
Logger.Debug("Reverting operations from transaction");
_transaction.Rollback();
}
_transaction.Dispose();
Logger.Debug("Transaction disposed");
_cancelled = false;
}
}
private void EnsureSession() {
if (_session != null) {
return;
}
var sessionFactory = _sessionFactoryHolder.GetSessionFactory();
Logger.Information("Openning database session");
_session = sessionFactory.OpenSession(new SessionInterceptor());
}
class SessionInterceptor : IInterceptor {
private ISession _session;

View File

@ -1,72 +1,13 @@
using System;
using System.Transactions;
using System.Web.Mvc;
using Orchard.Logging;
using System.Web.Mvc;
using Orchard.Mvc.Filters;
namespace Orchard.Data {
public interface ITransactionManager : IDependency {
void Demand();
void RequireNew();
void Cancel();
}
public class TransactionManager : ITransactionManager, IDisposable {
private TransactionScope _scope;
private bool _cancelled;
public TransactionManager() {
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
void ITransactionManager.Demand() {
if(_cancelled) {
try {
_scope.Dispose();
}
catch {
// swallowing the exception
}
_scope = null;
}
if (_scope == null) {
Logger.Debug("Creating transaction on Demand");
_scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = IsolationLevel.ReadCommitted
});
}
}
void ITransactionManager.Cancel() {
Logger.Debug("Transaction cancelled flag set");
_cancelled = true;
}
void IDisposable.Dispose() {
if (_scope != null) {
if (!_cancelled) {
Logger.Debug("Marking transaction as complete");
_scope.Complete();
}
Logger.Debug("Final work for transaction being performed");
try {
_scope.Dispose();
}
catch {
// swallowing the exception
}
Logger.Debug("Transaction disposed");
}
}
}
public class TransactionFilter : FilterProvider, IExceptionFilter {
private readonly ITransactionManager _transactionManager;

View File

@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using JetBrains.Annotations;
using Orchard.Data;
using Orchard.Events;
using Orchard.Logging;
namespace Orchard.Tasks {
@ -15,26 +12,28 @@ namespace Orchard.Tasks {
[UsedImplicitly]
public class BackgroundService : IBackgroundService {
private readonly IEnumerable<IEventHandler> _tasks;
private readonly IEnumerable<IBackgroundTask> _tasks;
private readonly ITransactionManager _transactionManager;
public BackgroundService(IEnumerable<IEventHandler> tasks) {
public BackgroundService(IEnumerable<IBackgroundTask> tasks, ITransactionManager transactionManager) {
_tasks = tasks;
_transactionManager = transactionManager;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void Sweep() {
foreach(var task in _tasks.OfType<IBackgroundTask>()) {
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew)) {
try {
task.Sweep();
scope.Complete();
}
catch (Exception e) {
Logger.Error(e, "Error while processing background task");
}
foreach(var task in _tasks) {
try {
_transactionManager.RequireNew();
task.Sweep();
}
catch (Exception e) {
_transactionManager.Cancel();
Logger.Error(e, "Error while processing background task");
}
}
}
}

View File

@ -1,6 +1,5 @@
using System;
using System.Timers;
using Orchard.Data;
using Orchard.Logging;
namespace Orchard.Tasks {
@ -61,9 +60,6 @@ namespace Orchard.Tasks {
public void DoWork() {
using (var scope = _workContextAccessor.CreateWorkContextScope()) {
var transactionManager = scope.Resolve<ITransactionManager>();
transactionManager.Demand();
// resolve the manager and invoke it
var manager = scope.Resolve<IBackgroundService>();
manager.Sweep();