Activation & Deactivation Events

Jul 15, 2013 at 9:21 AM
Edited Jul 15, 2013 at 9:22 AM
I am a NInject user, i am trying to use simple injector in place of ninject. i was wondering if there is a way to do the following with simple injector. (pasted code is what i did with ninject).
basically i want to hook into the events of activation and deactivation of the objects according to their lifetime (scope)


Bind<ISession>().InScope(SomeScope)
                .OnActivation((ctx, session) =>
                    {
                        Log("Activating...");
                        session.FlushMode = FlushMode.Auto;
                        session.BeginTransaction(IsolationLevel.ReadCommitted);
                })
                .OnDeactivation((ctx, session) =>
                    {
                        Log("Deactivating...");
                        session.Commit();
                    });    
Thanks
Coordinator
Jul 15, 2013 at 9:52 AM
The equivalent of Ninject's OnActivation method would be RegisterInitializer. The equivalent of Ninject's OnDeactivation would be one of Simple Injector's RegisterForDisposal methods depending on which scoped lifestyle you use (those methods can be found in SimpleInjectorWebExtensions, LifetimeScope, or WcfOperationScope). Those methods accept an IDisposable instance, so you would have to wrap the session.Commit() method in a class that implements IDisposable.

One very big warning though! Calling Commit at deactivation is a REALLY BAD idea. That session will always be deactivated/disposed, even when the application threw an exception. Committing the session when an exception has occurred however, will lead data to be saved to the database accidentally, even while the operation aborted prematurely. This breaks the atomic behavior of your code and transactions and can result in a database with invalid data. Instead you should explicitly call Commit only when the operation has succeeded, but there is no way to safely verify whether this is the case when you're deactivating/disposing. This holds for all containers, and even when not using a DI container at all. For a more elaborate discussion, please read this Stackoverflow answer.
Coordinator
Jul 15, 2013 at 10:56 AM
Edited Feb 26, 2014 at 8:56 PM
NOTE: This answer has been updated for Simple Injector 2.5.

The following code would be the equivalent of your Ninject configuration (assuming you run an ASP.NET application):
ScopedLifestyle scopedLifestyle = new WebRequestLifestyle();

container.Register<ISession>(() => sessionFactory.Create(), scopedLifestyle);

container.RegisterInitializer<ISession>(session =>
{
    Log("Activating...");
    session.FlushMode = FlushMode.Auto;
    session.BeginTransaction(IsolationLevel.ReadCommitted);
    
    // session will automatically get disposed when the request ends
    scopedLifestyle.WhenScopeEnds(() =>
    {
        Log("Deactivating...");
        session.Commit();
    });
});
When using any of the ScopedLifestyle implementations (such as the LifetimeScopeLifestyle, WebRequestLifestyle, WebApiRequestLifestyle or WcfOperationLifestyle) the code will be identical; you only have to change the lifestyle.

But again, in your particular case calling commit at that point is a very bad idea.
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:56 PM
Aug 16, 2013 at 3:21 PM
Thanks for clearing this up. I was trying to figure out how to close WCF channels when the object was disposed and couldn't figure out what the equivalent of OnDeactivation was. The other approach I could have taken was to extend the partial classes that are generated for the WCF proxy classes and override Dispose() and handle the closing there, but doing it within the DI setup means that I only have to do this once and not for every service that we use.

I ended up using your sample DisposeAction and it seems to be working well.
     channel.Faulted += (sender, args) => channel.Abort();

                if (HttpContext.Current != null)
                {
                    SimpleInjectorWebExtensions.RegisterForDisposal(
                        new DisposeAction(
                            () =>
                            {
                                if (channel.State == CommunicationState.Opened)
                                {
                                    channel.Close();
                                }
                                else
                                {
                                    channel.Abort();
                                }
                            }));
                }