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

@ -2,3 +2,5 @@
@model User @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) var viewContext = new ViewContext(_serviceProvider, actionContext.HttpContext, actionContext.RouteValues)
{ {
Url = urlHelper,
ViewData = ViewData, ViewData = ViewData,
Writer = writer, Writer = writer,
}; };
viewContext.Component = new DefaultViewComponentHelper(
_serviceProvider.GetService<IViewComponentSelector>(),
_serviceProvider.GetService<IViewComponentInvokerFactory>(),
viewContext);
return viewContext; return viewContext;
} }
} }

View File

@ -28,16 +28,23 @@ namespace Microsoft.AspNet.Mvc.Rendering
private const string HiddenListItem = @"<li style=""display:none""></li>"; private const string HiddenListItem = @"<li style=""display:none""></li>";
private readonly IUrlHelper _urlHelper;
private readonly IViewEngine _viewEngine;
private ViewContext _viewContext; private ViewContext _viewContext;
private IViewEngine _viewEngine;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HtmlHelper"/> class. /// Initializes a new instance of the <see cref="HtmlHelper"/> class.
/// </summary> /// </summary>
public HtmlHelper([NotNull] IViewEngine viewEngine, [NotNull] IModelMetadataProvider metadataProvider) public HtmlHelper(
[NotNull] IViewEngine viewEngine,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper)
{ {
_viewEngine = viewEngine; _viewEngine = viewEngine;
MetadataProvider = metadataProvider; MetadataProvider = metadataProvider;
_urlHelper = urlHelper;
// Underscores are fine characters in id's. // Underscores are fine characters in id's.
IdAttributeDotReplacement = "_"; IdAttributeDotReplacement = "_";
@ -445,8 +452,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
protected virtual MvcForm GenerateForm(string actionName, string controllerName, object routeValues, protected virtual MvcForm GenerateForm(string actionName, string controllerName, object routeValues,
FormMethod method, IDictionary<string, object> htmlAttributes) 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"); var tagBuilder = new TagBuilder("form");
tagBuilder.MergeAttributes(htmlAttributes); tagBuilder.MergeAttributes(htmlAttributes);

View File

@ -13,8 +13,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HtmlHelper{TModel}"/> class. /// Initializes a new instance of the <see cref="HtmlHelper{TModel}"/> class.
/// </summary> /// </summary>
public HtmlHelper([NotNull] IViewEngine viewEngine, [NotNull] IModelMetadataProvider metadataProvider) public HtmlHelper(
: base(viewEngine, metadataProvider) [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 namespace Microsoft.AspNet.Mvc
{ {
public class DefaultViewComponentHelper : IViewComponentHelper public class DefaultViewComponentHelper : IViewComponentHelper, ICanHasViewContext
{ {
private readonly IViewComponentInvokerFactory _invokerFactory; private readonly IViewComponentInvokerFactory _invokerFactory;
private readonly IViewComponentSelector _selector; private readonly IViewComponentSelector _selector;
private readonly ViewContext _viewContext; private ViewContext _viewContext;
public DefaultViewComponentHelper( public DefaultViewComponentHelper(
[NotNull] IViewComponentSelector selector, [NotNull] IViewComponentSelector selector,
[NotNull] IViewComponentInvokerFactory invokerFactory, [NotNull] IViewComponentInvokerFactory invokerFactory)
[NotNull] ViewContext viewContext)
{ {
_selector = selector; _selector = selector;
_invokerFactory = invokerFactory; _invokerFactory = invokerFactory;
}
public void Contextualize([NotNull] ViewContext viewContext)
{
_viewContext = viewContext; _viewContext = viewContext;
} }

View File

@ -36,8 +36,6 @@ namespace Microsoft.AspNet.Mvc
context.ViewContext.HttpContext, context.ViewContext.HttpContext,
context.ViewContext.ViewEngineContext) context.ViewContext.ViewEngineContext)
{ {
Component = context.ViewContext.Component,
Url = context.ViewContext.Url,
ViewData = _viewData ?? context.ViewContext.ViewData, ViewData = _viewData ?? context.ViewContext.ViewData,
Writer = context.Writer, Writer = context.Writer,
}; };

View File

@ -33,8 +33,6 @@ namespace Microsoft.AspNet.Mvc
ClientValidationEnabled = true; ClientValidationEnabled = true;
} }
public IViewComponentHelper Component { get; set; }
public virtual FormContext FormContext public virtual FormContext FormContext
{ {
get get
@ -52,8 +50,6 @@ namespace Microsoft.AspNet.Mvc
public IServiceProvider ServiceProvider { get; private set; } public IServiceProvider ServiceProvider { get; private set; }
public IUrlHelper Url { get; set; }
public bool UnobtrusiveJavaScriptEnabled { get; set; } public bool UnobtrusiveJavaScriptEnabled { get; set; }
public bool ClientValidationEnabled { 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 readonly HashSet<string> _renderedSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private bool _renderedBody; private bool _renderedBody;
public IViewComponentHelper Component
{
get { return Context == null ? null : Context.Component; }
}
public ViewContext Context { get; set; } public ViewContext Context { get; set; }
public string Layout { get; set; } public string Layout { get; set; }
protected TextWriter Output { get; set; } protected TextWriter Output { get; set; }
public IUrlHelper Url public IUrlHelper Url { get; private set; }
{
get { return Context == null ? null : Context.Url; }
}
public dynamic ViewBag public dynamic ViewBag
{ {
@ -50,6 +42,8 @@ namespace Microsoft.AspNet.Mvc.Razor
SectionWriters = new Dictionary<string, HelperResult>(StringComparer.OrdinalIgnoreCase); SectionWriters = new Dictionary<string, HelperResult>(StringComparer.OrdinalIgnoreCase);
Context = context; Context = context;
Url = context.ServiceProvider.GetService<IUrlHelper>();
var contentBuilder = new StringBuilder(1024); var contentBuilder = new StringBuilder(1024);
using (var bodyWriter = new StringWriter(contentBuilder)) 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 ViewDataDictionary<TModel> ViewData { get; private set; }
public IViewComponentHelper Component { get; private set; }
public IHtmlHelper<TModel> Html { get; set; } public IHtmlHelper<TModel> Html { get; set; }
public override Task RenderAsync([NotNull] ViewContext context) public override Task RenderAsync([NotNull] ViewContext context)
@ -53,6 +55,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
contextable.Contextualize(context); 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<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
yield return describe.Transient<INestedProvider<ViewComponentInvokerProviderContext>, DefaultViewComponentInvokerProvider>(); yield return describe.Transient<INestedProvider<ViewComponentInvokerProviderContext>, DefaultViewComponentInvokerProvider>();
yield return describe.Transient<IViewComponentResultHelper, DefaultViewComponentResultHelper>(); yield return describe.Transient<IViewComponentResultHelper, DefaultViewComponentResultHelper>();
yield return describe.Transient<IViewComponentHelper, DefaultViewComponentHelper>();
yield return yield return
describe.Describe( describe.Describe(