diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index f3b6e0973a..5361d6ae4b 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -102,8 +102,4 @@ - - - - diff --git a/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs index f67a9e22c9..764ae9ab60 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs @@ -12,6 +12,7 @@ using Umbraco.Core.DependencyInjection; using Umbraco.Core.Events; using Umbraco.Core.Models.PublishedContent; using Umbraco.ModelsBuilder.Embedded.Building; +using Umbraco.Web.Common.ModelBinders; using Umbraco.Web.WebAssets; /* @@ -91,6 +92,7 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection // would automatically just register for all implemented INotificationHandler{T}? builder.AddNotificationHandler(); builder.AddNotificationHandler(); + builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); diff --git a/src/Umbraco.ModelsBuilder.Embedded/ModelsBuilderNotificationHandler.cs b/src/Umbraco.ModelsBuilder.Embedded/ModelsBuilderNotificationHandler.cs index 9e11a9808d..db2a76abcb 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/ModelsBuilderNotificationHandler.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/ModelsBuilderNotificationHandler.cs @@ -22,24 +22,21 @@ namespace Umbraco.ModelsBuilder.Embedded /// /// Handles and notifications to initialize MB /// - internal class ModelsBuilderNotificationHandler : INotificationHandler, INotificationHandler + internal class ModelsBuilderNotificationHandler : INotificationHandler, INotificationHandler, INotificationHandler { private readonly ModelsBuilderSettings _config; private readonly IShortStringHelper _shortStringHelper; private readonly LinkGenerator _linkGenerator; - private readonly ContentModelBinder _modelBinder; public ModelsBuilderNotificationHandler( IOptions config, IShortStringHelper shortStringHelper, - LinkGenerator linkGenerator, - ContentModelBinder modelBinder) + LinkGenerator linkGenerator) { _config = config.Value; _shortStringHelper = shortStringHelper; _shortStringHelper = shortStringHelper; _linkGenerator = linkGenerator; - _modelBinder = modelBinder; } /// @@ -49,8 +46,6 @@ namespace Umbraco.ModelsBuilder.Embedded { // always setup the dashboard // note: UmbracoApiController instances are automatically registered - _modelBinder.ModelBindingException += ContentModelBinder_ModelBindingException; - if (_config.ModelsMode != ModelsMode.Nothing) { FileService.SavingTemplate += FileService_SavingTemplate; @@ -149,10 +144,13 @@ namespace Umbraco.ModelsBuilder.Embedded } } - private void ContentModelBinder_ModelBindingException(object sender, ContentModelBinder.ModelBindingArgs args) + /// + /// Handles when a model binding error occurs + /// + public void Handle(ModelBindingError notification) { - ModelsBuilderAssemblyAttribute sourceAttr = args.SourceType.Assembly.GetCustomAttribute(); - ModelsBuilderAssemblyAttribute modelAttr = args.ModelType.Assembly.GetCustomAttribute(); + ModelsBuilderAssemblyAttribute sourceAttr = notification.SourceType.Assembly.GetCustomAttribute(); + ModelsBuilderAssemblyAttribute modelAttr = notification.ModelType.Assembly.GetCustomAttribute(); // if source or model is not a ModelsBuider type... if (sourceAttr == null || modelAttr == null) @@ -164,11 +162,11 @@ namespace Umbraco.ModelsBuilder.Embedded } // else report, but better not restart (loops?) - args.Message.Append(" The "); - args.Message.Append(sourceAttr == null ? "view model" : "source"); - args.Message.Append(" is a ModelsBuilder type, but the "); - args.Message.Append(sourceAttr != null ? "view model" : "source"); - args.Message.Append(" is not. The application is in an unstable state and should be restarted."); + notification.Message.Append(" The "); + notification.Message.Append(sourceAttr == null ? "view model" : "source"); + notification.Message.Append(" is a ModelsBuilder type, but the "); + notification.Message.Append(sourceAttr != null ? "view model" : "source"); + notification.Message.Append(" is not. The application is in an unstable state and should be restarted."); return; } @@ -181,22 +179,22 @@ namespace Umbraco.ModelsBuilder.Embedded if (pureSource == false || pureModel == false) { // only one is pure - report, but better not restart (loops?) - args.Message.Append(pureSource + notification.Message.Append(pureSource ? " The content model is PureLive, but the view model is not." : " The view model is PureLive, but the content model is not."); - args.Message.Append(" The application is in an unstable state and should be restarted."); + notification.Message.Append(" The application is in an unstable state and should be restarted."); } else { // both are pure - report, and if different versions, restart // if same version... makes no sense... and better not restart (loops?) - var sourceVersion = args.SourceType.Assembly.GetName().Version; - var modelVersion = args.ModelType.Assembly.GetName().Version; - args.Message.Append(" Both view and content models are PureLive, with "); - args.Message.Append(sourceVersion == modelVersion + var sourceVersion = notification.SourceType.Assembly.GetName().Version; + var modelVersion = notification.ModelType.Assembly.GetName().Version; + notification.Message.Append(" Both view and content models are PureLive, with "); + notification.Message.Append(sourceVersion == modelVersion ? "same version. The application is in an unstable state and should be restarted." : "different versions. The application is in an unstable state and is going to be restarted."); - args.Restart = sourceVersion != modelVersion; + notification.Restart = sourceVersion != modelVersion; } } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs index d62497b173..01b6032c36 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ModelBinders/ContentModelBinderTests.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Routing; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Events; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Web.Common.ModelBinders; @@ -26,7 +27,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.ModelBinders private ContentModelBinder _contentModelBinder; [SetUp] - public void SetUp() => _contentModelBinder = new ContentModelBinder(); + public void SetUp() => _contentModelBinder = new ContentModelBinder(Mock.Of()); [Test] [TestCase(typeof(IPublishedContent), false)] diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs index f6f7eaa2d5..b227d8a903 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Views/UmbracoViewPageTests.cs @@ -6,7 +6,9 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Moq; using NUnit.Framework; +using Umbraco.Core.Events; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.ModelBinders; @@ -311,7 +313,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Views public class TestPage : UmbracoViewPage { - private readonly ContentModelBinder _modelBinder = new ContentModelBinder(); + private readonly ContentModelBinder _modelBinder = new ContentModelBinder(Mock.Of()); public override Task ExecuteAsync() => throw new NotImplementedException(); diff --git a/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs b/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs index 47ca49f014..cc6678abfc 100644 --- a/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs +++ b/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs @@ -3,21 +3,25 @@ using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ModelBinding; using Umbraco.Core; +using Umbraco.Core.Events; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Common.Routing; using Umbraco.Web.Models; namespace Umbraco.Web.Common.ModelBinders { + /// /// Maps view models, supporting mapping to and from any or . /// public class ContentModelBinder : IModelBinder { + private readonly IEventAggregator _eventAggregator; + /// - /// Occurs on model binding exceptions. + /// Initializes a new instance of the class. /// - public event EventHandler ModelBindingException; // TODO: This cannot use IEventAggregator currently because it cannot be async + public ContentModelBinder(IEventAggregator eventAggregator) => _eventAggregator = eventAggregator; /// public Task BindModelAsync(ModelBindingContext bindingContext) @@ -156,47 +160,10 @@ namespace Umbraco.Web.Common.ModelBinders // raise event, to give model factories a chance at reporting // the error with more details, and optionally request that // the application restarts. - var args = new ModelBindingArgs(sourceType, modelType, msg); - ModelBindingException?.Invoke(this, args); + var args = new ModelBindingError(sourceType, modelType, msg); + _eventAggregator.Publish(args); throw new ModelBindingException(msg.ToString()); } - - /// - /// Contains event data for the event. - /// - public class ModelBindingArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - public ModelBindingArgs(Type sourceType, Type modelType, StringBuilder message) - { - SourceType = sourceType; - ModelType = modelType; - Message = message; - } - - /// - /// Gets the type of the source object. - /// - public Type SourceType { get; set; } - - /// - /// Gets the type of the view model. - /// - public Type ModelType { get; set; } - - /// - /// Gets the message string builder. - /// - /// Handlers of the event can append text to the message. - public StringBuilder Message { get; } - - /// - /// Gets or sets a value indicating whether the application should restart. - /// - public bool Restart { get; set; } - } } } diff --git a/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs b/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs new file mode 100644 index 0000000000..3da698df5f --- /dev/null +++ b/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs @@ -0,0 +1,43 @@ +using System; +using System.Text; +using Umbraco.Core.Events; + +namespace Umbraco.Web.Common.ModelBinders +{ + /// + /// Contains event data for the event. + /// + public class ModelBindingError : INotification + { + /// + /// Initializes a new instance of the class. + /// + public ModelBindingError(Type sourceType, Type modelType, StringBuilder message) + { + SourceType = sourceType; + ModelType = modelType; + Message = message; + } + + /// + /// Gets the type of the source object. + /// + public Type SourceType { get; set; } + + /// + /// Gets the type of the view model. + /// + public Type ModelType { get; set; } + + /// + /// Gets the message string builder. + /// + /// Handlers of the event can append text to the message. + public StringBuilder Message { get; } + + /// + /// Gets or sets a value indicating whether the application should restart. + /// + public bool Restart { get; set; } + } +}