I was working on a version of my MSDN magazine Wiki sample (updated code for the April 16 drop coming soon) that used Unity instead of the one-off custom controller factory that is currently in there. I've posted on this before, and my previous conclusions were that it was pretty easy. Of course, I ran into a snag.
The one-off controller factory had this code in it:
001ISpaceRepository GetConfiguredRepository(HttpRequestBase request)
002{
003 return new FileBasedSpaceRepository(request.MapPath("~/WikiPages"));
004}
Basically, the FileBasedSpaceRepository takes a path to the directory where the files are stored. Of course, in a web app we need to use the server to map from a virtual-directory relative path to a physical file system path. Nothing new or unusual here.
But then I got into replacing this code with Unity, and immediately ran into the question: how do I get the container to call that MapPath method? Unity doesn't know anything about the web. And how does it get the current request?
So, I figured I'd use a child container, shove the request in there, and then use the static factory extension to resolve my string. My controller factory looks like this now:
001public class UnityTypeBasedControllerFactory : DefaultControllerFactory
002{
003 IUnityContainer container;
004 public UnityTypeBasedControllerFactory(IUnityContainer container)
005 {
006 this.container = container;
007 }
008 protected override IController GetControllerInstance(Type controllerType)
009 {
010 IUnityContainer requestContainer = container.CreateChildContainer()
011 .RegisterInstance<RequestContext>(
012 this.RequestContext,
013 new ExternallyControlledLifetimeManager());
014 return (IController)(requestContainer.Resolve(controllerType));
015 }
016}
So basically, what I'm doing here is that for each request, I spin up a child container, stuff the request context into the container, and then resolve the requested controller type. Pretty straightforward.
Next up is to configure the container.
001public static IUnityContainer GetAPIConfiguredContainer()
002{
003 IUnityContainer container = new UnityContainer()
004 .RegisterType<ISpaceRepository, FileBasedSpaceRepository>()
005 .Configure<InjectedMembers>()
006 .ConfigureInjectionFor<FileBasedSpaceRepository>(
007 new InjectionConstructor(
008 new ResolvedParameter<string>("PathToRepositoryFiles")))
009 .Container
010 .Configure<IStaticFactoryConfiguration>()
011 .RegisterFactory<string>("PathToRepositoryFiles",
012 c => c.Resolve().HttpContext.Request.MapPath("~/WikiPages"))
013 .Container;
014 return container;
015}
This is a little grotesque. Instead of just configuring the string to be injected into the constructor, I have to resolve it through the container. I registered the factory delegate (on line 13) to grab the current RequestContext, then use it to resolve the string.
Lots of things wrong here. You can't put this into the config file becuase of that factory method delegate. The actual string is buried in the delegate, so it's not obvious where you're actually getting the path from. And to top it off, due to an unfortunate design decision on my part, the static factory ends up getting the parent container passed into the delegate rather than the child. So the Resolve<RequestContext> call fails.
I found something that works better. I created a new class, MappedPathFileBasedSpaceRepository, which takes the RequestContext and the path to be mapped in the constructor. It inherits from the original FileBasedSpaceRepository, and does the MapPath call before passing the resulting file system path down to the base class. This results in a much, MUCH shorter configuration of the container:
001public static IUnityContainer GetAPIConfiguredContainer()
002{
003 IUnityContainer container = new UnityContainer()
004 .RegisterType<ISpaceRepository, MappedPathFileBasedSpaceRepository>()
005 .Configure<InjectedMembers>()
006 .ConfigureInjectionFor<MappedPathFileBasedSpaceRepository>(
007 new InjectionConstructor(typeof(RequestContext), "~/WikiPages"))
008 .Container;
009 return container;
010}
I'm pretty happy with where I am now, except for one thing: MappedPathFileBasedSpaceRepository wouldn't exist if I hadn't plugged in the container. Is this just a case of finding a new dependency that I hadn't realized before? Or is it that the presence of the DI container is intruding on my domain model?
I'd love to get some feedback from folks on what you've done to handle issues like this. What's your opinion here?
(I think I like the "child container shove the request context in" approach, but I'd love to get opinions on that too.)
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5