29 May 2015

Sitecore MVC and Castle.Windsor

I just wanted to add do a quick post today about Castle.Windsor, Sitecore MVC and your own project solution. I am usually using Castle.Windsor as IOC. I am quite sure a few of us encountered the following issue when running Sitecore some application on the Sitecore Client:




4976 16:45:28 ERROR Application error.
Exception: Sitecore.Mvc.Diagnostics.ControllerCreationException
Message: Could not create controller: 'Media'. 
The current route url is: 'api/sitecore/{controller}/{action}'. 
Source: Sitecore.Mvc
   at Sitecore.Mvc.Controllers.SitecoreControllerFactory.CreateController(RequestContext requestContext, String controllerName)
   at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory)
   at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
 
Nested Exception
 
Exception: Castle.MicroKernel.ComponentNotFoundException
Message: No component for supporting the service Sitecore.Controllers.MediaController was found
Source: Castle.Windsor
   at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
   at DPE.Business.DI.WindsorControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) in r:\Projects\XXX\Trunk\XXX.Business\DI\WindsorControllerFactory.cs:line 34
   at System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName)
   at Sitecore.Mvc.Controllers.SitecoreControllerFactory.CreateController(RequestContext requestContext, String controllerName)

The issue there is if you are defining your IOC, you may need to ensure that This is resolving your Sitecore controllers correctly as well. here is the few controller I needed on my Initialisation:

            container.Register(Classes.FromAssemblyNamed("Sitecore.Speak.Client").BasedOn().LifestylePerWebRequest());
            container.Register(Classes.FromAssemblyNamed("Sitecore.Mvc").BasedOn().LifestylePerWebRequest());
            container.Register(Classes.FromAssemblyNamed("Sitecore.Mvc.DeviceSimulator").BasedOn().LifestylePerWebRequest());
            container.Register(Classes.FromAssemblyNamed("Sitecore.Marketing.Client").BasedOn().LifestylePerWebRequest());
            container.Register(Classes.FromAssemblyNamed("Sitecore.Client.LicenseOptions").BasedOn().LifestylePerWebRequest());

5 May 2015

Passing Invalid ID in the Rendering Datasource Break your page

Using Sitecore 8 MVC and GlassMapper has been awesome (Thanks to Mike Edwards great work). This is one of the must have for Sitecore MVC. I usually have my Rendering Datasource field pointing at a "Data" Item on the tree:


On the View code I can then bind my view with the Model using GlassMapper as per the following Code:

@inherits Glass.Mapper.Sc.Web.Mvc.GlassView<DPE.Data.Interfaces.SiteSettings.ISiteSettings>

I like to use Interfaces when defining the Models...

While this is great and working correctly when all is setup correctly, I have had a small issue on a latest project where editors deleted the datasource item without removing the links. Which left the Presentation of the item with a broken link:



Although this was quite simple to fix and instruct the client to select the option to either remove the links and/or editing the links manually. This was pointing at another case scenario:

what if you forgot to publish the data source item... Indeed in this situation you will end up with the same broken link in your datasource field on the web database and it will break the page with the following error:



The issue there is that sitecore is trying to create the default model during the pipeline action:

Sitecore.Mvc.Pipelines.Response.GetModel.CreateDefaultRenderingModel, Sitecore.Mvc

While trying to implement a custom action to replace the above pipeline, I found this great post from Hiral Desai. Really great source of information to implement the way around the issue:

The work around was quite simple: replacing the GetViewRenderer pipeline action. In order to do that we need to add the following config entry in our custom patch config files:


  < sitecore>
    
      
        
             
    
  

Then on the code side, we need to implement our new pipeline acttion with the following code:
public class GetViewRendererWithItemValidation : GetViewRenderer
    {        
        protected override Renderer GetRenderer(Rendering rendering, GetRendererArgs args)
        {           
            var viewRenderer = base.GetRenderer(rendering, args) as ViewRenderer;
            if (viewRenderer == null)
                return null;

            // Ignore item check when in page editor
            // Also this will break if the item for the datasource has been deleted without removing the link.
            if (Context.PageMode.IsPageEditor || Context.PageMode.IsPageEditorEditing)
                return viewRenderer;

            // Override renderer to null when there is an unpublished item refererenced by underlying view
            return viewRenderer.Rendering.Item != null && viewRenderer.Rendering.RenderingItem.InnerItem != null
                ? viewRenderer
                : null;
        }
    }

Hoping that will help anyone.