I'm using GlassMapperin my SC7 project. It configures Castle Windsor IoC container "out of the box", allowing to add a custom configuration. I added some dependencies to my controller constructor and overwrote the default SitecoreControllerFactory with one using Castle's container to resolve them. I added the controller as Controller Rendering, but it doesn't seem to work.
Diving into it, I realized that the default controller factory is not being called at all, though it's configured correctly. Instead, Sitecore seems to go for an alternative pipeline, calling a Sitecore.Mvc.Controllers.ControllerRunner which uses reflection to load the controller and create an instance of it, which throws an exception since the constructor needs the dependencies.
Here's the code.
Initialize:
public class Initialize
{
public void Process(PipelineArgs args)
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new WebFormViewEngine());
ViewEngines.Engines.Add(new ThemedRazorEngine());
GlassMapperSc.Start();
SetupControllerFactory(args);
}
public virtual void SetupControllerFactory(PipelineArgs args)
{
var container = Utils.Ioc.Ioc.CurrentContainer;
if (container == null)
{
return;
}
var controllerFactory = new WindsorControllerFactory(container);
var sitecoreControllerFactory = new SitecoreWindsorControllerFactory(controllerFactory);
ControllerBuilder.Current.SetControllerFactory(sitecoreControllerFactory);
}
}
Sitecore Controller Factory:
public class SitecoreWindsorControllerFactory : SitecoreControllerFactory
{
public SitecoreWindsorControllerFactory(IControllerFactory innerFactory) : base(innerFactory)
{
}
protected override IController CreateControllerInstance(RequestContext requestContext, string controllerName)
{
return controllerName.EqualsText(SitecoreControllerName) ? CreateSitecoreController(requestContext, controllerName) : InnerFactory.CreateController(requestContext, controllerName);
}
}
Default Controller factory:
public class WindsorControllerFactory : DefaultControllerFactory
{
private const int PageNotFoundStatus = 404;
private readonly IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container)
{
_container = container;
}
public override IController CreateController(RequestContext requestContext, string controllerName)
{
// todo: wrap this in the final implementation
if (!TypeHelper.LooksLikeTypeName(controllerName))
{
return base.CreateController(requestContext, controllerName);
}
var type = TypeHelper.GetType(controllerName);
return type != null ? GetControllerInstance(requestContext, type) : null;
}
public override void ReleaseController(IController controller)
{
_container.Release(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
// todo: the request context should be wrapped in final implementation.
throw new HttpException(
PageNotFoundStatus,
string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return _container.Resolve(controllerType) as IController;
}
}
And the controller:
ublic class LanguagePickerController : Controller
{
private readonly ISitecoreContext _context;
public LanguagePickerController()
{
_context = Ioc.CurrentContainer.Resolve<ISitecoreContext>();
}
public ActionResult LanguagePickerIndex()
{
//var context = new SitecoreContext();
var dataSource = RenderingContext.Current.Rendering.DataSource;
var model = _context.GetItem<LanguagePicker>(dataSource);
// ReSharper disable once Mvc.ViewNotResolved
return View("LanguagePicker",model);
}
}
You can't see the dependency in the constructor since now I'm resolving it calling the container directly.
Well, the solution took a while but finally I got it: I overwrote 5 classes: GetControllerRenderer, ControllerRenderer, ControllerRunner, ControllerFactory and SitecoreControllerFactory.
In GetControllerRenderer, method GetRenderer calls the ControllerRenderer, in which method Render calls ControllerRunner. In ControllerRunner I overrode method CreateController to bypass Sitecore code which load the controller with reflection and use a new controller factory based on Castle Windsor.