Asp – Dependency injection: injecting user control in aspx page

asp.netdependency-injection

I need to build a 'customizable' asp.net web application (not asp.net mvc).

I was thinking to use an IoC container to inject user control into aspx pages.

Has anybody ever tried that ?

In brief here is how I think this can be achieved:

  • I use Scott Guthrie's method to build a reusable 'user control library' (see his article "Creating and Using User Control Libraries").

  • Controls and pages in that library should have places to receive user controls dynamically (e.g., in an asp PlaceHolder).

  • In my custom web application, built on top of that library, I create specific user controls.

  • I put them in some way in a IoC container so that they can be injected in the controls/pages of the 'user control library' (e.g., in the PlaceHolders).

That's it.

This is basically what can be done with Spring.Net as discussed here http://blogger.forgottenskies.com/?p=70 but to inject user controls.

Has anybody some experience on such stuff ? Or does it sound silly ? Alternatives ?

The idea is to have the possibility to build different web applications on top of my 'user control library' without touching that library.

For instance: in my library I have a page with 4 text boxes and for one specific application I need to add one text box, without changing the code of the page. The idea is to put a place holder in that page and inject dynamically my custom text box in this place holder.

Best Answer

It's not exactly the same as your situation, but I had a need to dynamically inject an editor control depending on the runtime type of the entity being edited. I had all my controls inherit from a common interface like this:

public interface IEditor
{
    bool CanHandle(EntityBase entity);
    void Display(EntityBase entity);
    void Save(EntityBase entity);
}

public partial class AddressEditor : UserControl, IEditor
{
    public bool CanHandle(EntityBase entity)
    { 
        return (entity is Address);
    }

    public void Display(EntityBase entity)
    {
        var address = (Address)entity;
        addressLine1Textbox.Text = address.Line1;
        // etc...
    }

    public void Save(EntityBase entity)
    {
        var address = (Address)entity;
        address.Line1 = addressLine1Textbox.Text;
        // etc...
    }
}

Then using an IoC container (StructureMap in this case) I can get the correct user control with something like

var editorControl = ObjectFactory.GetAllInstancesOf<IEditor>().First(x => x.CanHandle(myEntity));

This is where it gets tricky though, you can't add a user control to a page if you create an instance of it like this. You need to know the location of it's .ascx file. You could expose it through the editor interface so that each control returns it:

public string AscxFile { get { return "~/UserControls/AddressEditor.ascx"; } } // Implements IEditor.AscxFile

Then in the calling page, use LoadControl:

var actualControl = LoadControl(editorControl.AscxFile);
editorPlaceholder.Controls.Add(actualControl);
editorControl.Display(myEntity);

I preferred using Composite Controls, they take a bit more effort to create but no need to worry about the .ascx file, I can just add it directly to the page:

var editorControl = ObjectFactory.GetAllInstancesOf<IEditor>().First(x => x.CanHandle(myEntity));
editorPlaceholder.Controls.Add((Control)editorControl);
editorControl.Display(myEntity);

Make sure you're adding the control at the Page.OnInit stage so that it can partake in ViewState, and it won't get remembered between postbacks so you have to do it every time.

The last step is wiring things up with your IoC tool of choice, I use StructureMap to automatically load all instances of IEditor but I could vary this at any point in the application code or in the xml config.