views:

588

answers:

2

I am using the generic repository pattern to persist my data. On the PageLoad, I am creating a new Repository (from IRepository) object, and on PageUnload, I dispose of it.

Should the MasterPage/Page be in charge of instantiating the objects to pass to the presenter or should the presenter be in charge of this? I'm more concerned with testing the presenter than the page (View) since it's easier to mock the interfaces passed to the presenter.

Example Page

public partial class _Default : System.Web.UI.Page
{
    private IRepository _repo;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (_repo == null)
            _repo = new Repository();
        ConnectPresenter();
    }

    private void ConnectPresenter()
    {
        _DefaultPresenter presenter = new _DefaultPresenter(_repo);
    }

    private void Page_Unload(object sender, EventArgs e)
    {
        if (_repo != null)
            _repo.Dispose();
    }
}

Would a DI Framework such as StructureMap or Ninject help in this case? Would it be in charge of disposing objects such as this?

A: 

Yes, it would be well worth you investigating one of the walkthroughs out there of using DI with ASP.NET.

Yes, Disposing of per-Request Behavior objects at the appropriate point is generally managed by the Container's integration with ASP.NET.

The typical arrangement is that object creation flows in from the Page and Application/Modules inward. Generally you mark properties [Inject] on your Page class, but it depends on how you've arranged your triad. The Presenter generally can use Constructo Injection to declare what it needs regardless of whether its test or ASP.NET cotext. Then at runtime, the dependencies will be satisfied by the DI. At test time, you can still use DI, though in other cases it might be more natural to just create a bunch of Fakes along with the SUT and pass those to the Presenter.

Regarding triead arrangements wrt testing, I found this MSDN Mag article on using Ninject with xUnit.net by Justin Etheredge very useful, even though its targetted at ASP.NET MVC.

Ruben Bartelink
Sorry for not looking at the code or tags in your question! Have reworked it now - hope that improves matters! Will delete this if you delete yours...
Ruben Bartelink
+2  A: 

Neither the Page class nor the presenters should have to deal directly with managing the construction or lifecycle of any of its dependencies - that should all be handled by your container. Since constructor injection does not work with WebForms, you will need to expose any needed dependencies as properties on the class. For example, you could change your class to:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }

    public _DefaultPresenter Presenter { get; set; }
}

The page should not need any reference to the repository, as it will be injected into the presenter.

The rest of this answer is specific to StructureMap - details may differ for other containers.

To enable setter injection, you need to tell StructureMap which properties to populate. One way is to apply the [SetterProperty] attribute to the property itself. However, this can feel a bit invasive to have StructureMap details within your classes. Another way is to configure StructureMap so that it knows which property types to inject. For example:

protected void Application_Start(object sender, EventArgs e)
{
    ObjectFactory.Initialize(x =>
    {
        x.Scan(scan =>
        {
            scan.TheCallingAssembly();
            scan.WithDefaultConventions();
        });
        x.ForRequestedType<IRepository>().TheDefaultIsConcreteType<Repository>().CacheBy(InstanceScope.Hybrid);
        x.SetAllProperties(set => set.WithAnyTypeFromNamespaceContainingType<IRepository>());
    });
}

The SetAllProperties method allows you to tell StructureMap how to recognize the properties it should populate. In this case, I'm telling StructureMap to inject all presenters (assuming they are all in the same namespace).

You still need to perform the setter injection on each request. With StructureMap, you use the BuildUp() method to inject dependencies into an existing instance. You could do it in the Init or Load events of each page or a page base class, but again, that feels invasive. To keep the container out of your page classes completely, you can use the PreRequestHandlerExecute event of the application (in global.asax or an IHttpModule):

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
    var application = (HttpApplication)sender;
    var page = application.Context.CurrentHandler as Page;
    if (page == null) return;
    ObjectFactory.BuildUp(page);
}

Finally, if you want to explicitly Dispose of your IRepository, you could handle that in the EndRequest event:

protected void Application_EndRequest(object sender, EventArgs e)
{
    var disposable = ObjectFactory.GetInstance<IRepository>() as IDisposable;
    if (disposable != null) disposable.Dispose();
}

Note that this works properly because in the initialization we told StructureMap to cache IRepository by Hybrid, which means "give me the same instance for each HTTP Request (or thread, if not running within a website)". When you retrieve the IRepository in EndRequest, you will receive the same one used throughout the request, and you can dispose it.

Joshua Flanagan