Register non-Depedency

Aug 15, 2012 at 6:27 PM

I have a SavingChangesEventHandler class that listens for a few events on another class that is also under my control. This listener class is not injected into anything, but it does have injected dependencies. How should I register this class so that a new instance is created per web request?

Coordinator
Aug 15, 2012 at 7:39 PM

There is an RegisterPerWebRequest method in the SimpleInjector.Integration.Web.dll that allows you to register instances per web request. This dll is part of the default CodePlex download and available as separate NuGet package (and its part of the NuGet MVC Integration package).

Aug 16, 2012 at 11:17 PM

Yes, but using that method will result in this class only being instantiated once; a break-point in its constructor will only be hit once. The class will still be instantiated throughout the lifetime of the application, as well. Unfortunately this is not sufficient in my case, as it has a member variable that should be reset per web request. Bear in mind that this class is not injected into anything, and nothing needs to instantiate it. 

Perhaps a DI container is not the tool for this job?

Aug 17, 2012 at 3:52 PM
Edited Aug 17, 2012 at 3:54 PM

In my App_Start\SimpleInjectorInitializer.cs:

private static void InitializeContainer(Container container)
{
    container.RegisterPerWebRequest<SavingChangesEventHandler, SavingChangesEventHandler>();
    container.RegisterPerWebRequest<IChangeLogManager, ChangeLogManager>();
}

First, my event handler does not consume an interface, which is the first indication to me that I'm using a DI container for something other than its intended purpose, but moving on, here is some of its code:

public class SavingChangesEventHandler
{
    private List<ChangeLog> ChangeLogs;

    private readonly IChangeLogManager ChangeLogManager;

    public SavingChangesEventHandler(IChangeLogManager changeLogManager)
    {
        this.ChangeLogManager = changeLogManager;
        MyDerivedDataContext.OnContextCreated += new MyDerivedDataContext.ContextCreatedHandler(MyDerivedDataContext_OnContextCreated);
    }

    private void ShowRoomDataContext_OnContextCreated(object sender)
    {
        ((IObjectContextAdapter)sender).ObjectContext.SavingChanges += new EventHandler(SavingChangesHandler);
        ((MyDerivedDataContext)sender).OnChangesSaved += new MyDerivedDataContext.ChangesSavedHandler(MyDerivedDataContext_OnChangesSaved);
    }

    private void SavingChangesHandler(object sender, EventArgs e)
    {
        ...do magic, add to ChangeLogs...
    }

    private void MyDerivedDataContext_OnChangesSaved(object sender)
    {
        ...save everything in ChangeLogs...
    }

...where OnChangesSaved is a custom event I on MyDerivedDataContext, simply raised if the base.SaveChanges() returns a rowcount. I'm using EF 4.3 Code-First. 

My ultimate goal is to have a generic change log for my entities. Everything works other than SavingChangesEventHandler only being instantiated once. I'm starting to think I just need to do my own create and dispose of this class in global.asax. Alternatively I could just put this all into MyDerivedContext, which is already correctly instantiated per web request...

Coordinator
Aug 17, 2012 at 6:28 PM
Edited Aug 23, 2012 at 2:40 PM

Your registration is correct. Both types are correctly registered as per-web-request.

There is nothing wrong with registering the SavingChangesEventHandler by itself, especially when this type is just an infrastructure component (part of the composition root) and the rest of the application does not depend on it.

The MyDerivedDataContext.OnContextCreated event looks like if it is a static event. However, you are hooking an event handler to it from within the constructor of the SavingChangesEventHandler. SavingChangesEventHandler has the lifetime of a single request and I don't see any code where you unregister this event. This could mean you have a memory leak, all SavingChangesEventHandler instances keep referenced, and when the ContextCreated event is raised, all SavingChangesEventHandler instances will get notified (which can be quite a lot when your application is running for some time).

Besides this, I don't see anything odd here. Can you show me the code the resolves and uses the SavingChangesEventHandler?

If your goal is to have a generic (transaction) log of all mutations in your system, perhaps you should take a different approach (although I must admit it's hard to judge, by the code you've shown me). For instance, you can try to model all use cases / business transactions as commands. This has worked very well for me and many others. When you model those commands as data transfer objects (DTO), you can easily serialize them and store the serialized commands in the log. Or when such a log is too coarse-grained, you can write a decorator that processes writing the changelog at the end of a business transaction. You should definitely read this article: Meanwhile… on the command side of my architecture.

Using this approach you could for instance write a decorator as follows that you wrap around any command handler that executes some business operation:

public class ChangeLogCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decorated;
    private readonly IChangeLogManager changeLogManager;
    private readonly MyDerivedDataContext dataContext;
    private readonly IUserContext userContext;

    public SavingChangesEventHandler(
        ICommandHandler<TCommand> decorated,
        IChangeLogManager changeLogManager, 
        MyDerivedDataContext dataContext,
        IUserContext userContext)
    {
        this.decorated = decorated;
        this.changeLogManager = changeLogManager;
        this.dataContext = dataContext;
        this.userContext = userContext;
    }

    public void Handle(TCommand command)
    {
        this.decorated.Handle(command);

        var addedEntities = this.dataContext.ObjectStateManager
            .GetObjectStateEntries(EntityState.Added)
            .Select(state => state.Entity)
            .Where(entity => entity != null);

        var changeLogs =
            from entity in addedEntities
            select new ChangeLog
            {
                Entity = entity.GetType().Name,
                User = this.userContext.CurrentUser,
                Change = "Added"
            };
        
        this.dataContext.ChangeLogs.SaveAllOnSubmit(changeLogs);
    }
}
Marked as answer by dot_NET_Junkie on 11/4/2013 at 1:59 AM
Aug 17, 2012 at 9:45 PM

Thank you for your time and guidance, Junkie. That's a good catch on the memory leak.

I had coincidentally already read your post on the command pattern the other day as I was searching for how to make my architecture more command-based. I do agree that that's a better way to go. Unfortunately I didn't realize that until I was "one line of code away" from finishing this current implementation, so I wanted to see if there was a quick way to just wrap up what I had to get the feature done and move on. However, upon revisiting the actual business requirements, I realize now that I was unnecessarily ambitious; there is no need to log every change on every entity. All that is needed is to create a human-readable log for specific fields that changed from the UI on two specific screens, spanning only two or three entities. No need to keep a perfect audit history. I have (for now) written some ugly code to just make that work for those two cases.

I will definitely look into refactoring to a command style, though. It's been on my mind lately as each screen in this application performs more functions. Thanks for the help!