From ab605ef935698182c65433fb4cefc79b31dbf7f2 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 8 Apr 2014 11:39:55 -0700 Subject: [PATCH] Fix for #215 - nullref trying to generate link in a partial The fix here is to do some cleanup we've been planning to do for a while, rather than flowing IUrlHelper and IComponentHelper as part of the ViewContext, they now are just grabbed from the service provider. This simplifies the code for invoking a view, and gets us closer to the desired API surface of ViewContext --- .../Views/Shared/HelloWorldPartial.cshtml | 4 +++- .../ActionResults/ViewResult.cs | 6 ------ .../Rendering/Html/HtmlHelper.cs | 14 ++++++++++---- .../Rendering/Html/HtmlHelperOfT.cs | 7 +++++-- .../ViewComponents/DefaultViewComponentHelper.cs | 11 +++++++---- .../ViewComponents/ViewViewComponentResult.cs | 2 -- src/Microsoft.AspNet.Mvc.Core/ViewContext.cs | 4 ---- src/Microsoft.AspNet.Mvc.Razor/RazorView.cs | 12 +++--------- src/Microsoft.AspNet.Mvc.Razor/RazorViewOfT.cs | 10 ++++++++++ src/Microsoft.AspNet.Mvc/MvcServices.cs | 1 + 10 files changed, 39 insertions(+), 32 deletions(-) diff --git a/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml b/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml index e58af0cf87..e211035aab 100644 --- a/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml +++ b/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml @@ -1,4 +1,6 @@ @using MvcSample.Web.Models @model User -Hello @Model.Name from Partial \ No newline at end of file +Hello @Model.Name from Partial + +Edit Something! \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs index 2f01519b1f..f7efaee70f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs @@ -51,16 +51,10 @@ namespace Microsoft.AspNet.Mvc var viewContext = new ViewContext(_serviceProvider, actionContext.HttpContext, actionContext.RouteValues) { - Url = urlHelper, ViewData = ViewData, Writer = writer, }; - viewContext.Component = new DefaultViewComponentHelper( - _serviceProvider.GetService(), - _serviceProvider.GetService(), - viewContext); - return viewContext; } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index a315191283..2d94971822 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -28,16 +28,23 @@ namespace Microsoft.AspNet.Mvc.Rendering private const string HiddenListItem = @"
  • "; + private readonly IUrlHelper _urlHelper; + private readonly IViewEngine _viewEngine; + private ViewContext _viewContext; - private IViewEngine _viewEngine; + /// /// Initializes a new instance of the class. /// - public HtmlHelper([NotNull] IViewEngine viewEngine, [NotNull] IModelMetadataProvider metadataProvider) + public HtmlHelper( + [NotNull] IViewEngine viewEngine, + [NotNull] IModelMetadataProvider metadataProvider, + [NotNull] IUrlHelper urlHelper) { _viewEngine = viewEngine; MetadataProvider = metadataProvider; + _urlHelper = urlHelper; // Underscores are fine characters in id's. IdAttributeDotReplacement = "_"; @@ -445,8 +452,7 @@ namespace Microsoft.AspNet.Mvc.Rendering protected virtual MvcForm GenerateForm(string actionName, string controllerName, object routeValues, FormMethod method, IDictionary htmlAttributes) { - var urlHelper = ViewContext.Url; - var formAction = urlHelper.Action(action: actionName, controller: controllerName, values: routeValues); + var formAction = _urlHelper.Action(action: actionName, controller: controllerName, values: routeValues); var tagBuilder = new TagBuilder("form"); tagBuilder.MergeAttributes(htmlAttributes); diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs index 75aefba752..9fd01c2d98 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs @@ -13,8 +13,11 @@ namespace Microsoft.AspNet.Mvc.Rendering /// /// Initializes a new instance of the class. /// - public HtmlHelper([NotNull] IViewEngine viewEngine, [NotNull] IModelMetadataProvider metadataProvider) - : base(viewEngine, metadataProvider) + public HtmlHelper( + [NotNull] IViewEngine viewEngine, + [NotNull] IModelMetadataProvider metadataProvider, + [NotNull] IUrlHelper urlHelper) + : base(viewEngine, metadataProvider, urlHelper) { } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs index 365364f009..7b95107203 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/DefaultViewComponentHelper.cs @@ -8,19 +8,22 @@ using Microsoft.AspNet.Mvc.Rendering; namespace Microsoft.AspNet.Mvc { - public class DefaultViewComponentHelper : IViewComponentHelper + public class DefaultViewComponentHelper : IViewComponentHelper, ICanHasViewContext { private readonly IViewComponentInvokerFactory _invokerFactory; private readonly IViewComponentSelector _selector; - private readonly ViewContext _viewContext; + private ViewContext _viewContext; public DefaultViewComponentHelper( [NotNull] IViewComponentSelector selector, - [NotNull] IViewComponentInvokerFactory invokerFactory, - [NotNull] ViewContext viewContext) + [NotNull] IViewComponentInvokerFactory invokerFactory) { _selector = selector; _invokerFactory = invokerFactory; + } + + public void Contextualize([NotNull] ViewContext viewContext) + { _viewContext = viewContext; } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs index 0d4cfe0d6b..c5f91d961c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs @@ -36,8 +36,6 @@ namespace Microsoft.AspNet.Mvc context.ViewContext.HttpContext, context.ViewContext.ViewEngineContext) { - Component = context.ViewContext.Component, - Url = context.ViewContext.Url, ViewData = _viewData ?? context.ViewContext.ViewData, Writer = context.Writer, }; diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs b/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs index c7d11be7c6..5dd54696f6 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs @@ -33,8 +33,6 @@ namespace Microsoft.AspNet.Mvc ClientValidationEnabled = true; } - public IViewComponentHelper Component { get; set; } - public virtual FormContext FormContext { get @@ -52,8 +50,6 @@ namespace Microsoft.AspNet.Mvc public IServiceProvider ServiceProvider { get; private set; } - public IUrlHelper Url { get; set; } - public bool UnobtrusiveJavaScriptEnabled { get; set; } public bool ClientValidationEnabled { get; set; } diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs index c66ec1752f..2bb52ccac5 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs @@ -15,21 +15,13 @@ namespace Microsoft.AspNet.Mvc.Razor private readonly HashSet _renderedSections = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _renderedBody; - public IViewComponentHelper Component - { - get { return Context == null ? null : Context.Component; } - } - public ViewContext Context { get; set; } public string Layout { get; set; } protected TextWriter Output { get; set; } - public IUrlHelper Url - { - get { return Context == null ? null : Context.Url; } - } + public IUrlHelper Url { get; private set; } public dynamic ViewBag { @@ -50,6 +42,8 @@ namespace Microsoft.AspNet.Mvc.Razor SectionWriters = new Dictionary(StringComparer.OrdinalIgnoreCase); Context = context; + Url = context.ServiceProvider.GetService(); + var contentBuilder = new StringBuilder(1024); using (var bodyWriter = new StringWriter(contentBuilder)) { diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewOfT.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewOfT.cs index 3cc5af08fe..232eb6ac58 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewOfT.cs @@ -18,6 +18,8 @@ namespace Microsoft.AspNet.Mvc.Razor public ViewDataDictionary ViewData { get; private set; } + public IViewComponentHelper Component { get; private set; } + public IHtmlHelper Html { get; set; } public override Task RenderAsync([NotNull] ViewContext context) @@ -53,6 +55,14 @@ namespace Microsoft.AspNet.Mvc.Razor { contextable.Contextualize(context); } + + Component = context.ServiceProvider.GetService(); + + contextable = Component as ICanHasViewContext; + if (contextable != null) + { + contextable.Contextualize(context); + } } } } diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index f4654d1198..36557a98d8 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -74,6 +74,7 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient(); yield return describe.Transient, DefaultViewComponentInvokerProvider>(); yield return describe.Transient(); + yield return describe.Transient(); yield return describe.Describe(