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
This commit is contained in:
Ryan Nowak 2014-04-08 11:39:55 -07:00
parent 749789c486
commit ab605ef935
10 changed files with 39 additions and 32 deletions

View File

@ -1,4 +1,6 @@
@using MvcSample.Web.Models
@model User
<strong>Hello @Model.Name from Partial</strong>
<strong>Hello @Model.Name from Partial</strong>
<a href="@Url.Action("Edit", "Home")">Edit Something!</a>

View File

@ -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<IViewComponentSelector>(),
_serviceProvider.GetService<IViewComponentInvokerFactory>(),
viewContext);
return viewContext;
}
}

View File

@ -28,16 +28,23 @@ namespace Microsoft.AspNet.Mvc.Rendering
private const string HiddenListItem = @"<li style=""display:none""></li>";
private readonly IUrlHelper _urlHelper;
private readonly IViewEngine _viewEngine;
private ViewContext _viewContext;
private IViewEngine _viewEngine;
/// <summary>
/// Initializes a new instance of the <see cref="HtmlHelper"/> class.
/// </summary>
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<string, object> 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);

View File

@ -13,8 +13,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <summary>
/// Initializes a new instance of the <see cref="HtmlHelper{TModel}"/> class.
/// </summary>
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)
{
}

View File

@ -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;
}

View File

@ -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,
};

View File

@ -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; }

View File

@ -15,21 +15,13 @@ namespace Microsoft.AspNet.Mvc.Razor
private readonly HashSet<string> _renderedSections = new HashSet<string>(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<string, HelperResult>(StringComparer.OrdinalIgnoreCase);
Context = context;
Url = context.ServiceProvider.GetService<IUrlHelper>();
var contentBuilder = new StringBuilder(1024);
using (var bodyWriter = new StringWriter(contentBuilder))
{

View File

@ -18,6 +18,8 @@ namespace Microsoft.AspNet.Mvc.Razor
public ViewDataDictionary<TModel> ViewData { get; private set; }
public IViewComponentHelper Component { get; private set; }
public IHtmlHelper<TModel> 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<IViewComponentHelper>();
contextable = Component as ICanHasViewContext;
if (contextable != null)
{
contextable.Contextualize(context);
}
}
}
}

View File

@ -74,6 +74,7 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
yield return describe.Transient<INestedProvider<ViewComponentInvokerProviderContext>, DefaultViewComponentInvokerProvider>();
yield return describe.Transient<IViewComponentResultHelper, DefaultViewComponentResultHelper>();
yield return describe.Transient<IViewComponentHelper, DefaultViewComponentHelper>();
yield return
describe.Describe(