diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs index e69b3e114a..d574ea4b25 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs @@ -41,14 +41,14 @@ namespace Umbraco.Tests.Web.Mvc [Test] public void BindModel_Null_Source_Returns_Null() { - Assert.IsNull(RenderModelBinder.BindModel(null, typeof(MyContent), CultureInfo.CurrentCulture)); + Assert.IsNull(RenderModelBinder.Instance.BindModel(null, typeof(MyContent), CultureInfo.CurrentCulture)); } [Test] public void BindModel_Returns_If_Same_Type() { var content = new MyContent(Mock.Of()); - var bound = RenderModelBinder.BindModel(content, typeof (IPublishedContent), CultureInfo.CurrentCulture); + var bound = RenderModelBinder.Instance.BindModel(content, typeof (IPublishedContent), CultureInfo.CurrentCulture); Assert.AreSame(content, bound); } @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Web.Mvc { var content = new MyContent(Mock.Of()); var renderModel = new RenderModel(content, CultureInfo.CurrentCulture); - var bound = RenderModelBinder.BindModel(renderModel, typeof(IPublishedContent), CultureInfo.CurrentCulture); + var bound = RenderModelBinder.Instance.BindModel(renderModel, typeof(IPublishedContent), CultureInfo.CurrentCulture); Assert.AreSame(content, bound); } @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Web.Mvc public void BindModel_IPublishedContent_To_RenderModel() { var content = new MyContent(Mock.Of()); - var bound = (IRenderModel)RenderModelBinder.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture); + var bound = (IRenderModel)RenderModelBinder.Instance.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture); Assert.AreSame(content, bound.Content); } @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Web.Mvc public void BindModel_IPublishedContent_To_Generic_RenderModel() { var content = new MyContent(Mock.Of()); - var bound = (IRenderModel)RenderModelBinder.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture); + var bound = (IRenderModel)RenderModelBinder.Instance.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture); Assert.AreSame(content, bound.Content); } diff --git a/src/Umbraco.Web/Mvc/RenderModelBinder.cs b/src/Umbraco.Web/Mvc/RenderModelBinder.cs index 18d9879cac..7ce4320baf 100644 --- a/src/Umbraco.Web/Mvc/RenderModelBinder.cs +++ b/src/Umbraco.Web/Mvc/RenderModelBinder.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; @@ -13,8 +12,10 @@ namespace Umbraco.Web.Mvc /// /// Allows for Model Binding any IPublishedContent or IRenderModel /// - public class RenderModelBinder : DefaultModelBinder, IModelBinder, IModelBinderProvider + public class RenderModelBinder : DefaultModelBinder, IModelBinderProvider { + public static RenderModelBinder Instance = new RenderModelBinder(); + /// /// Binds the model to a value by using the specified controller context and binding context. /// @@ -66,7 +67,7 @@ namespace Umbraco.Web.Mvc // to // { RenderModel, RenderModel, IPublishedContent } // - public static object BindModel(object source, Type modelType, CultureInfo culture) + public object BindModel(object source, Type modelType, CultureInfo culture) { // null model, return if (source == null) return null; @@ -132,10 +133,30 @@ namespace Umbraco.Web.Mvc return null; } - private static void ThrowModelBindingException(bool sourceContent, bool modelContent, Type sourceType, Type modelType) + public class ModelBindingArgs : EventArgs + { + public ModelBindingArgs(Type sourceType, Type modelType, StringBuilder message) + { + SourceType = sourceType; + ModelType = modelType; + Message = message; + } + + public bool SourceIsContent; + public bool ViewModelIsContent; + public Type SourceType { get; set; } + public Type ModelType { get; set; } + public StringBuilder Message { get; private set; } + public bool Restart { get; set; } + } + + public static event EventHandler ModelBindingException; + + private void ThrowModelBindingException(bool sourceContent, bool modelContent, Type sourceType, Type modelType) { var msg = new StringBuilder(); - + + // prepare message msg.Append("Cannot bind source"); if (sourceContent) msg.Append(" content"); msg.Append(" type "); @@ -145,29 +166,25 @@ namespace Umbraco.Web.Mvc msg.Append(" type "); msg.Append(modelType.FullName); msg.Append("."); + + // raise event, to give model factories a chance at reporting + // the error with more details, and optionally request that + // the application restarts. - // compare FullName for the time being because when upgrading ModelsBuilder, - // Umbraco does not know about the new attribute type - later on, can compare - // on type directly (ie after v7.4.2). - var sourceAttr = sourceType.Assembly.CustomAttributes.FirstOrDefault(x => - x.AttributeType.FullName == "Umbraco.ModelsBuilder.PureLiveAssemblyAttribute"); - var modelAttr = modelType.Assembly.CustomAttributes.FirstOrDefault(x => - x.AttributeType.FullName == "Umbraco.ModelsBuilder.PureLiveAssemblyAttribute"); + var args = new ModelBindingArgs(sourceType, modelType, msg); + if (ModelBindingException != null) + ModelBindingException(this, args); - // bah.. names are App_Web_all.generated.cs.8f9494c4.jjuvxz55 so they ARE different, fuck! - // we cannot compare purely on type.FullName 'cos we might be trying to map Sub to Main = fails! - if (sourceAttr != null && modelAttr != null - && sourceType.Assembly.GetName().Version.Revision != modelType.Assembly.GetName().Version.Revision) + if (args.Restart) { - msg.Append(" Types come from two PureLive assemblies with different versions,"); - msg.Append(" this usually indicates that the application is in an unstable state."); - msg.Append(" The application is restarting now, reload the page and it should work."); - var context = HttpContext.Current; - if (context == null) - AppDomain.Unload(AppDomain.CurrentDomain); - else - ApplicationContext.Current.RestartApplicationPool(new HttpContextWrapper(context)); - } + msg.Append(" The application is restarting now."); + + var context = HttpContext.Current; + if (context == null) + AppDomain.Unload(AppDomain.CurrentDomain); + else + ApplicationContext.Current.RestartApplicationPool(new HttpContextWrapper(context)); + } throw new ModelBindingException(msg.ToString()); } diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 3d1a41d289..b364a76628 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -144,7 +144,7 @@ namespace Umbraco.Web.Mvc // bind the model (use context culture as default, if available) if (UmbracoContext.PublishedContentRequest != null && UmbracoContext.PublishedContentRequest.Culture != null) culture = UmbracoContext.PublishedContentRequest.Culture; - viewData.Model = RenderModelBinder.BindModel(viewDataModel, typeof (TModel), culture); + viewData.Model = RenderModelBinder.Instance.BindModel(viewDataModel, typeof (TModel), culture); // set the view data base.SetViewData(viewData); diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index aea0d73d4d..d77fc3e6bb 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -118,7 +118,7 @@ namespace Umbraco.Web ViewEngines.Engines.Add(new PluginViewEngine()); //set model binder - ModelBinderProviders.BinderProviders.Add(new RenderModelBinder()); // is a provider + ModelBinderProviders.BinderProviders.Add(RenderModelBinder.Instance); // is a provider ////add the profiling action filter //GlobalFilters.Filters.Add(new ProfilingActionFilter());