StrutctureMap is one of the most popular IoC containers in .NET but I had never tried it out. You can learn all about StructureMap from the documentation and the tutorials already available so I won't try to explain what it is and how it works. Instead I'll show you my experience on how to use it with ASP.NET WebForms.
To test StructureMap I decided to use it in my blog which is a simple WebForms application that is now using NHibernate. The objective is use in the code only the interfaces for my Services and DAOs. Their implementation should be injected.
The first step is to create a class that will be responsible for initializing the IoC. I decided to create this class at the WebSite level in the App_Code folder because this class has dependencies on all the other assemblies. Since the WebSite has to reference all these assemblies I figured this would be the best place. Here is the code for the class:
public class IocConfigurator
{
public static void Configure()
{
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<IPostDao>().TheDefault.Is.
OfConcreteType<PostDao>();
x.ForRequestedType<ICategoryDao>().TheDefault.Is.
OfConcreteType<CategoryDao>();
x.ForRequestedType<ICommentDao>().TheDefault.Is.
OfConcreteType<CommentDao>();
x.ForRequestedType<IAuthorDao>().TheDefault.Is.
OfConcreteType<AuthorDao>();
x.ForRequestedType<ITagDao>().TheDefault.Is.
OfConcreteType<TagDao>();
x.ForRequestedType<IFrontEndService>().TheDefault.Is.
OfConcreteType<FrontEndService>();
x.ForRequestedType<IAdminService>().TheDefault.Is.
OfConcreteType<AdminService>();
x.SetAllProperties(y =>
{
y.OfType<IFrontEndService>();
y.OfType<IAdminService>();
});
});
}
}
Now we have to class this IocConfiguration class in order to make all the initialization. The best place to do this is in the Global.asax in the Application_Start event.
<%@ Application Language="C#" %>
<%@ Import Namespace="StructureMap"%>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
IocConfigurator.Configure();
}
...
</script>
Top-Down resolution
Ok, now let's deal with how to inject the services in the Pages. Let's continue using the IFrontEndService as our example. We could do something simple like:
IFrontEndService service = ObjectFactory.GetInstance<IFrontEndService>();
But this is not a recommend approach as you can see in the StructureMap Quickstart. You should minimize the GetInstance calls and let auto wiring do the job for you. Unfortunately WebForms doesn't make that easy. For that reason the team created the BuildUp feature that let's you inject the dependencies into an object that was already built.
In order to do the BuildUp into a single place I posted the code into the BasePage constructor. This class derives from the Page class and all the pages in my application derive from it instead of from the Page as would be usual.
namespace SpeakOut.Lib.Web
{
public class BasePage : Page
{
public BasePage()
{
ObjectFactory.BuildUp(this);
}
}
}
Here is a page using the BasePage:
public partial class _Default : BasePage
{
public IFrontEndService Service { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
RepeaterPosts.DataSource = Service.GetRecentPosts(10);
RepeaterPosts.DataBind();
}
}
protected void RepeaterPosts_ItemCreated(object sender, RepeaterItemEventArgs e)
{
Post post = (Post) e.Item.DataItem;
PostDetailsControl control = (PostDetailsControl) e.Item.FindControl("PostDetailsControl1");
control.Post = post;
}
}
The BuildUp method will look for properties of the type I have registered in the IoC Configuration and will inject the properties with the correct implementation. The registration of the type of properties that should be injected is a special part of the IoC setup. In our configuration class it is at the very end of the Configure method with this syntax:
x.SetAllProperties(y =>
{
y.OfType<IFrontEndService>();
y.OfType<IAdminService>();
});
With this StructureMap will take care of all the dependency injection for you. Including injecting the DAOs into the Service. In order for the DAOs to be injected all you need to do is to define a constructor that receives all the values you want to have injected. When StructureMap creates the Service it will look for the constructor with most parameters (the greediest). If it sees that it has all the parameters in the configuration it will automatically create the necessary objects and pass them to the constructor.
public class FrontEndService : IFrontEndService
{
private readonly IPostDao _postDao;
private readonly ICategoryDao _categoryDao;
public FrontEndService(IPostDao postDao, ICategoryDao categoryDao)
{
_postDao = postDao;
_categoryDao = categoryDao;
}
...
That makes the creation of a tree of objects really simple and your code doesn't need to know anything about the interface implementations.
You can get the whole implementation from the SpeakOut Codeplex Project.