Unable to work with different database in the orchard modules using Entity Model

Topics: Core, Writing modules
Nov 16, 2011 at 9:19 PM
Edited Nov 17, 2011 at 9:40 AM

Hi, I am currently trying to integrate a new database driven module into orchard cms 1.3.9. I have got it successfully working as long as i do not reference any data from the database.

It seems to be related to the Transaction.Demand() part of Orchard, i tried replace the ITransactionManager.Demand() method within Orchard, and changed it from TransactionScopeOption.Required to Suppress and that worked fine for my application, but unfortunately not for Orchard.

I am using Entities model and i know Orchard is using NHibernate, is there a way of sharing or bypassing the transactions across projects (i.e. Orchard and my project)

This is what i'm doing:-

--Initialization

private MyData _myDb;

private MyData myDb

{

get {

using (new TransactionScope(TransactionScopeOption.Suppress)) {

if (_myDb == null) _myDb = new MyData();

return _myDb;

 }

}

 }

then calling myDb from within a method, As I would normally call my database connection to retrieve a table through Linq.

myDb.myUsers

Is there a way of not firing the TransactionScope when it is my module? I have tried to suppress transactions within my module for the initialization call but when i call the methods that retrieve data, it still fails with the following error:-

The underlying provider failed on Open.

and inner exception is

The partner transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D025).

Does anyone have any clues as to how to resolve this issue.

I am aware of this discussion but it seems to be related to opening a connection retrieving the data and then closing it whereas i am initializing the entity model and then calling it per method.

http://orchard.codeplex.com/discussions/244312?ProjectName=orchard

Any help would be greatly appreciated.

Mar 15, 2012 at 9:08 AM
Edited Mar 15, 2012 at 9:17 AM

Did you solve this? I'm running into the same issue when trying to communicate with another database.

edit: nm, the url he posted included the solution for me ^^

Mar 15, 2012 at 11:58 AM

This the change i made to the orchard framework within the transactionmanager.cs

using System;
using System.Transactions;
using System.Web.Mvc;
using Orchard.Logging;
using Orchard.Mvc.Filters;

namespace Orchard.Data
{
    public interface ITransactionManager : IDependency
    {
        //Added a transactionScopeOption interface to be able to change it
        TransactionScopeOption TransactionScopeOption { get; set; }        

        void Demand();

        void Cancel();
    }

    public class TransactionManager : ITransactionManager, IDisposable
    {
        private TransactionScope _scope;
        private bool _cancelled;

        public TransactionManager()
        {
            Logger = NullLogger.Instance;
        }

        //Added a transactionScopeOption that then gave me the facility to specify the scope option for the controller, I'm working in.
        public TransactionScopeOption TransactionScopeOption { get; set; }
        
        public ILogger Logger { get; set; }

        void ITransactionManager.Demand()
        {
            if (_scope == null)
            {
                Logger.Debug("Creating transaction on Demand");
                if (TransactionScopeOption == TransactionScopeOption.Suppress)
                { 
                    //Suppressed the transactionscope if suppress option chosen
                    _scope = new TransactionScope(
                        TransactionScopeOption.Suppress);
                }
                else
                {
                    _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");
                _scope.Dispose();
                Logger.Debug("Transaction disposed");
            }
        }
    }

    public class TransactionFilter : FilterProvider, IExceptionFilter
    {
        private readonly ITransactionManager _transactionManager;

        public TransactionFilter(ITransactionManager transactionManager)
        {
            _transactionManager = transactionManager;
        }

        public void OnException(ExceptionContext filterContext)
        {
            _transactionManager.Cancel();
        }
    }
}

This part was contained within my controller, so that i can suppress the transactionscope without affecting the rest of orchard

_services.TransactionManager.TransactionScopeOption = TransactionScopeOption.Suppress;

_services being part of the IOrchardService
Good luck I hope it helps
Mar 15, 2012 at 9:42 PM

Is all that necessary? Couldn't you achieve similar effect more safely by wrapping your data access code with a using block like this? 

using (new TransactionScope(TransactionScopeOption.Suppress)) {
    // Your data access code here 
}
With the code you pasted, what happens after your own code runs, do you have to remember to set the TransactionScopeOption back to the original value? If so, what if your code encounters an exception before it gets a chance to set the value back to original? Wouldn't this have potential negative impacts on Orchard code that runs within the same HttpRequest context?