Suppressing the ambient transaction using Actionfilter

Topics: Customizing Orchard, Writing modules
Aug 26, 2011 at 3:54 PM

Hi

 

We are starting out with writing modules for Orchard, and our modules will only connect to external databases.

 

Since the method described in posts earlier suggests new TransactionScope(TransactionScopeOption.Suppress, which we felt was unneccesarily code clutter, since the entire assembly (one dll, multiple modules) we wanted to add an assembly level attribute.

Here is the code for anyone else wanting to use it (we apply it in AssemblyInfo.cs):

[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    public class TransactionSuppressAttribute : FilterAttribute,IActionFilter, IDisposable
    {
        private TransactionScope _scope;
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (_scope != null)
            {
                _scope.Complete();
                _scope.Dispose();
                _scope = null;
            }
         
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            _scope = new TransactionScope(TransactionScopeOption.Suppress);
        }

        void IDisposable.Dispose()
        {
            if (_scope != null)
            {
                _scope.Complete();
                _scope.Dispose();
                _scope = null;

            }
        }
    }

Any comments on codeimprovements are welcome.

 

Also, another quick question: We liked having our modules in a single assembly, and stuctured in subdirectories (like Orchard.Core). We copied the Core assembly loader and modified some variables and works nice. Is that the correct extensionpoint to use, or did we miss a more obvious path? We deploy the bin to root/bin, and copy the module.txt and associated content files to the module directory.

M

Coordinator
Aug 26, 2011 at 5:48 PM

The attribute looks nice.

I do not understand your second question. Why would you need to copy the core assembly loader?  What exactly are you trying to do?

Aug 26, 2011 at 6:15 PM

What we do, is having one project, with several modules, precompiled, and just copy the modules.txt to the right place. It did not seem to load.

I read that the Orchard.Core has several modules in one precompiled assembly, so we checked it out and did what we wanted to do (one bin in ~/bin/ with several modules in ~/Modules/CompanyName.XXX)

The CoreExtensionLoader does exactly what we wanted but is hardcoded to ~/Core directory and Orchard.Core assembly. We changed it so that the loader checks all modules with the name of CompanyName.XXX gets checked (so it is not really reusable as of now). 

 

It does work very nice, I was just wondering if it was the right approach.

 

Congrats on an excellent framework by the way. We used autofac and MVC3 before, but wanted a richer framework to work with, and Orchard fits the bill perfectly and makes coding much more enjoyable.

Coordinator
Aug 26, 2011 at 6:20 PM

I would recommend building one single module, with separate features. I built the Vandelay module along those lines for the same reason (single assembly). I did not separate the features in neat folders within the module but you could/should. http://vandelay.codeplex.com/

Sep 27, 2012 at 3:30 AM

I have used this (thank-you) with a modification and I'm interested to get feedback on how I'm doing it.

First, you said you apply the attribute in AssemblyInfo.cs - how exactly do you do that?

Anyhow, I found that I was getting problems with errors when the OnActionExecuting call was on a different thread to OnActionExecuted. That causes an error "A TransactionScope must be disposed on the same thread that it was created."

I found that storing the TransactionScope in the HttpContext Items dictionary works, like this:

    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    public class TransactionSuppressAttribute : FilterAttribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            TransactionScope scope = (TransactionScope)filterContext.HttpContext.Items["ReqTransactionScope"];
            if (scope != null)
            {
                scope.Complete();
                scope.Dispose();
                scope = null;
            }

        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress);
            filterContext.HttpContext.Items.Add("ReqTransactionScope", scope);
        }
    }

Coordinator
Sep 30, 2012 at 9:38 PM

Please, don't do that.

Nov 22, 2012 at 4:16 PM

Perhaps you'd care to suggest an alternative, because if I don't do that my app is unusable and I haven't found any viable alternatives.

 

Thanks

Sean

Coordinator
Nov 22, 2012 at 6:34 PM

Well, I know absolutely nothing about your application, what you're trying to do and how it's failing so it's impossible to suggest an alternative. Also, that was almost two months ago, and to be frank I'm not quite sure what was bothering me in there at the time. It seems a little weird that you would have removed the IDisposable code but other than that, putting it on the request context doesn't seem like a bad thing to do...