I have dropped in an assembly containing an MVC Controller I would like to use as the target for my MvcPlayer function.
When I try to render the page though the function output states that my controller could not be found.
Is there a special technique to get Composite to register Controllers available within assemblies I add to the bin directory?
So my suspicions of being able to use StructureMap to solve this problem were well founded.
StructureMap is a DI/IoC tool for .NET. You could use any other DI/IoC tool (including hand-cranking your own) to solve this problem in a similar fashion though.
If you have no experience with StructureMap I would recommend reading some documentation about it. Here is a good video tutorial you could use:
The following article introduces StructureMap as well as talks about the custom controller factory solution that I used for my problem:
And if you don't know much about custom controller factories, I would read the following article. This article also gives you some good clues on how you would do your own hand cranked IoC (instead of using StructureMap etc).
A word of warning! You may run into issues when referencing libraries that have explicit references to previous versions of MVC3. As far as I can tell Composite C1 v4.0 beta2 and the MvcPlayer function have dependencies on MVC4 and it's related libraries. To safeguard against version conflicts add the following section to your web.config in order to ensure that references are always get targeted to the correct versions:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers"
publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc"
publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="4.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages"
publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
Ok, so now that you are up to speed, below is all the code needed to take the StructureMap approach.
I have a StructureMap controller convention:
public class ControllerConvention : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (typeof(IController).IsAssignableFrom(type) && !type.IsAbstract && !type.IsGenericType)
{
graph.AddType(typeof(IController), type, type.Name.Replace("Controller", "").ToLower());
}
}
}
The controller convention tells StructureMap how to find my MVC Controller classes, which is easy to do as I need only look for classes implementing the IController interface.
For your StructureMap intialisation, typically done on app start up (see structuremap docu for examples), you can pass through the assemblies to scan for Controllers as well as the Controller convention:
var controllerAssemblies = new List<Assembly>()
{
typeof(YourController).Assembly,
typeof(AnotherController).Assembly
};
ObjectFactory.Initialize(initialization =>
{
initialization.Scan(scan =>
{
foreach (var assembly in controllerAssemblies)
{
scan.Assembly(assembly);
}
scan.With<ControllerConvention>();
});
});
Now we will need a custom controller factory:
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
try
{
return ObjectFactory.GetInstance(controllerType) as Controller;
}
catch (StructureMapException)
{
System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
throw;
}
}
}
And finally, we need to register the use of our custom controller factory in the App_Code/Composite/AspNet/MvcPlayer/Route.cs class:
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());