Question

I am working on a MVC3 application in which I would like to handle some code blocks asynchronously. These code blocks need to perform some db operations and they need access to my DAL repositories which are initiated trough my dependency injection setup. I have set the lifetime of the repository to an "instance per http request" lifetime and I am looking for a way to extend that lifetime for the async operation.

public class AsyncController : Controller
{
    private readonly ICommandBus commandBus;
    private readonly IObjectRepository objectRepository;

    public ActionResult DoThings()
    {
        DoThingsAsync();
        return View();
    }

    private delegate void DoThingsDelegate();
    private void DoThingsAsync()
    {
        var handler = new DoThingsDelegate(DoThingsNow);
        handler.BeginInvoke(null, null);
    }

    private void DoThingsNow()
    {
        var objects = objectRepository.getAll();
        foreach (Thing thing in objects)
        {
            thing.modifiedOn = DateTime.Now;
            objectRepository.Update(thing);
        }
    }
}  

The objectRepository is initiated for the lifetime of the request and I would like to skip the garbage collection for this object for one method in one controller only. I have tried to pass the repository as a parameter to the method and the delegate but that did not extend its lifetime.

Était-ce utile?

La solution

When starting a new thread, it is wise to let the Container compose a totally new object graph for you. When you reuse dependencies that are created in the context of a HTTP request (or thread context or what have you), this can result in race conditions and other sort of failures and strange behavior.

This of course doesn’t have to be the case when you know that those dependencies are thread-safe (or created with a per-request lifetime and are not used by the request after triggering the async operation), but this is not something a consumer of these dependencies should know or should depend on, because it is the responsibility of the part of the application that wires everything together (The Composition Root). Doing this, would also make it harder to change that dependency configuration later on.

Instead, you should refactor your DoThings method into its own class and let your Controller depend on some sort of IDoThings interface. This way you can postpone the decision about handling things asynchronously until the moment you wire everything together. When reusing that logic in another type of application (Windows Service for instance), it even allows you to change the way this operation is executed asynchronously (or simply execute it synchronously).

To go even one step further, the actual DoThings business logic and the part that executes that logic asynchronously are two different concerns: two separate responsibilities. You should separate them into different classes.

Here's an example of what I advise you to do:

Define an interface:

public interface IDoThings
{
    void DoThings();
}

Let your Controller depend on that interface:

public class SomeController : Controller
{
    private readonly IDoThings thingsDoer;

    public SomeController(IDoThings thingsDoer)
    {
         this.thingsDoer = thingsDoer;
    }

    public ActionResult DoThings()
    {
        this.thingsDoer.DoThings();
        return View();
    }
}

Define an implementation that contains the business logic:

public class DoingThings : IDoThings
{
    private readonly ICommandBus commandBus;
    private readonly IObjectRepository objectRepository;

    public DoingThings(ICommandBus commandBus,
        IObjectRepository objectRepository)
    {
        this.commandBus = commandBus;
        this.objectRepository = objectRepository;
    }

    public void DoThings()
    {
        var objects = objectRepository.getAll();

        foreach (Thing thing in objects)
        {
            thing.modifiedOn = DateTime.Now;
            objectRepository.Update(thing);
        }
    }
}

Define a proxy that knows how to handle a DoingThings asynchronously:

public class DoingThingsAsync : IDoThings
{
    private readonly Container container;

    public DoingThingsAsync(Container container)
    {
        this.container = container;
    }

    public void DoThings()
    {
        Action handler = () => DoThingsNow();
        handler.BeginInvoke(null, null);        
    }

    private void DoThingsNow()
    {
        // Here we run in a new thread and HERE we ask
        // the container for a new DoingThings instance.
        // This way we will be sure that all its
        // dependencies are safe to use. Never move
        // dependencies from thread to thread.
        IDoThings doer =
            this.container.GetInstance<DoingThings>();

        doer.DoThings();
    }
}

Now, instead of injecting a DoingThings into the SomeController, you inject a DoingThingsAsync into the controller. The controller does not know whether the operation is executed synchronously or not, and it doesn't care. Besides that, this way you are also separating your business logic from your presentation logic, which is important for lots of good reasons.

You might want to consider using the command pattern as basis around business operations that mutate state (if you're not already using such a thing, considering the ICommandBus interface). Take for instance a look at this article. With this pattern you can more easily configure certain commands to run asynchronously or batch them to an external transactional queue, for later processing, without having to change any of the consumers of those commands.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top