3

I'm in the middle of a significant effort to introduce NHibernate into our code base. I figured I would have to use some kind of a DI container, so I can inject dependencies into the entities I load from the database. I chose Unity as that container.

I'm considering using Unity's interception mechanism to add a transaction aspect to my code, so I can do e.g. the following:

class SomeService
{
    [Transaction]
    public void DoSomething(CustomerId id)
    {
        Customer c = CustomerRepository.LoadCustomer(id);
        c.DoSomething();
    }
}

and the [Transaction] handler will take care of creating a session and a transaction, committing the transaction (or rolling back on exception), etc.

I'm concerned that using this kind of interception will bind me to using Unity pretty much everywhere in the code. If I introduce aspects in this manner, then I must never, ever call new SomeService(), or I will get a service that doesn't have transactions. While this is acceptable in production code, it seems too much overhead in tests. For example, I would have to convert this:

void TestMethod()
{
    MockDependency dependency = new MockDependency();
    dependency.SetupForTest();
    var service = SomeService(dependency);
    service.DoSomething();
}

into this:

void TestMethod()
{
    unityContainer.RegisterType<MockDependency>();
    unityContainer.RegisterType<IDependency, MockDependency>();

    MockDependency dependency = unityContainer.Resolve<MockDependency>();
    dependency.SetupForTest();
    var service = unityContainer.Resolve<SomeService>();
    service.DoSomething();
}

This adds 2 lines for each mock object that I'm using, which leads to quite a bit of code (our tests use a lot of stateful mocks, so it is not uncommon for a test class to have 5-8 mock objects, and sometimes more.)

I don't think standalone injection would help here: I have to set up injection for every class that I use in the tests, because it's possible for aspects to be added to a class after the test is written.

Now, if I drop the use of interception I'll end up with:

class SomeService
{
    public void DoSomething(CustomerId id)
    {
        Transaction.Run(
            () => {
                Customer c = CustomerRepository.LoadCustomer(id);
                c.DoSomething();
             });
    }
}

which is admittedly not as nice, but doesn't seem that bad either.

I can even set up my own poor man's interception:

class SomeService
{
    [Transaction]
    public void DoSomething(CustomerId id)
    {
        Interceptor.Intercept(
            MethodInfo.GetCurrentMethod(),
            () => {
                Customer c = CustomerRepository.LoadCustomer(id);
                c.DoSomething();
             });
    }
}

and then my interceptor can process the attributes for the class, but I can still instantiate the class using new and not worry about losing functionality.

Is there a better way of using Unity interception, that doesn't force me to always use it for instantiating my objects?

telewin
  • 1,102
  • 8
  • 17
  • @Mat, I would disagree with that. I can see some similarities between this and some of the "what not to ask" examples, this is still a well written and valid question. He's not asking "What's our favorite AOP tool" but saying "I'm looking at using Unity but have some concerns, what experiences do people have from using it that can help me decide if it's worthwhile". Perhaps it's borderline, but IMO it's good enough to have merit and not be closed. – Samuel Neff May 18 '11 at 13:01
  • @Mat The question is about a specific concern I have with using Unity, not a general question of whether AOP is worthwhile. I edited the last line of the question to reflect that. – telewin May 18 '11 at 13:05

1 Answers1

0

If you want to use AOP but are concerned abut Unity then I would recommend you check out PostSharp. That implements AOP as a post-compile check but has no changes on how you use the code at runtime.

http://www.sharpcrafters.com/

They have a free community edition that has a good feature set, as well as professional and enterprise versions that have significantly enhanced feature sets.

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182