Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

* Allow null ViewData and TempData #3310

Merged
merged 1 commit into from
Oct 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ public override async Task ExecuteResultAsync(ActionContext context)
viewData = new ViewDataDictionary(modelMetadataProvider, context.ModelState);
}

var tempData = TempData;
if (tempData == null)
{
tempData = services.GetRequiredService<ITempDataDictionary>();
}

var contentType = ContentType;
if (contentType != null && contentType.Encoding == null)
{
Expand All @@ -98,8 +104,8 @@ public override async Task ExecuteResultAsync(ActionContext context)
// 3. ViewExecutor.DefaultContentType (sensible default)
//
//
response.ContentType =
contentType?.ToString() ??
response.ContentType =
contentType?.ToString() ??
response.ContentType ??
ViewExecutor.DefaultContentType.ToString();

Expand All @@ -115,7 +121,7 @@ public override async Task ExecuteResultAsync(ActionContext context)
context,
NullView.Instance,
viewData,
TempData,
tempData,
writer,
htmlHelperOptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Net.Http.Headers;

Expand Down Expand Up @@ -117,14 +119,16 @@ public virtual async Task ExecuteAsync(
throw new ArgumentNullException(nameof(view));
}

var services = actionContext.HttpContext.RequestServices;
if (viewData == null)
{
throw new ArgumentNullException(nameof(viewData));
var metadataProvider = services.GetRequiredService<IModelMetadataProvider>();
viewData = new ViewDataDictionary(metadataProvider);
}

if (tempData == null)
{
throw new ArgumentNullException(nameof(tempData));
tempData = services.GetRequiredService<ITempDataDictionary>();
}

var response = actionContext.HttpContext.Response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ public class ViewComponentResultTest
private readonly ITempDataDictionary _tempDataDictionary =
new TempDataDictionary(new HttpContextAccessor(), new SessionStateTempDataProvider());

[Fact]
public async Task ExecuteAsync_ViewComponentResult_AllowsNullViewDataAndTempData()
{
// Arrange
var descriptor = new ViewComponentDescriptor()
{
FullName = "Full.Name.Text",
ShortName = "Text",
Type = typeof(TextViewComponent),
};

var actionContext = CreateActionContext(descriptor);

var viewComponentResult = new ViewComponentResult
{
Arguments = new object[] { "World!" },
ViewData = null,
TempData = null,
ViewComponentName = "Text"
};

// Act
await viewComponentResult.ExecuteResultAsync(actionContext);
// No assert, just confirm it didn't throw
}

[Fact]
public async Task ExecuteResultAsync_Throws_IfNameOrTypeIsNotSet()
{
Expand Down Expand Up @@ -76,10 +102,10 @@ public async Task ExecuteResultAsync_Throws_IfViewComponentCouldNotBeFound_ByTyp
// Arrange
var expected = $"A view component named '{typeof(TextViewComponent).FullName}' could not be found.";

var services = CreateServices();
var actionContext = CreateActionContext();
var services = CreateServices(actionContext.HttpContext);
services.AddSingleton<IViewComponentSelector>();

var actionContext = CreateActionContext();

var viewComponentResult = new ViewComponentResult
{
Expand Down Expand Up @@ -387,8 +413,11 @@ public async Task ViewComponentResult_NoContentTypeSet_PreservesResponseContentT
Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType);
}

private IServiceCollection CreateServices(params ViewComponentDescriptor[] descriptors)
{
private IServiceCollection CreateServices(HttpContext context, params ViewComponentDescriptor[] descriptors)
{
var httpContext = new HttpContextAccessor() { HttpContext = context };
var tempDataProvider = new SessionStateTempDataProvider();

var services = new ServiceCollection();
services.AddSingleton<IOptions<MvcViewOptions>, TestOptionsManager<MvcViewOptions>>();
services.AddTransient<IViewComponentHelper, DefaultViewComponentHelper>();
Expand All @@ -400,15 +429,17 @@ private IServiceCollection CreateServices(params ViewComponentDescriptor[] descr
services.AddInstance<IViewComponentDescriptorProvider>(new FixedSetViewComponentDescriptorProvider(descriptors));
services.AddSingleton<IModelMetadataProvider, EmptyModelMetadataProvider>();
services.AddInstance<ILoggerFactory>(NullLoggerFactory.Instance);
services.AddInstance<ITempDataDictionary>(new TempDataDictionary(httpContext, tempDataProvider));
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();

return services;
}

private HttpContext CreateHttpContext(params ViewComponentDescriptor[] descriptors)
{
var services = CreateServices(descriptors);

var httpContext = new DefaultHttpContext();
var services = CreateServices(httpContext, descriptors);

httpContext.Response.Body = new MemoryStream();
httpContext.RequestServices = services.BuildServiceProvider();

Expand Down Expand Up @@ -467,4 +498,4 @@ private static string ReadBody(HttpResponse response)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,11 @@ public void ObjectTemplateDisplaysSimpleDisplayTextWhenTemplateDepthGreaterThanO
public void ObjectTemplate_IgnoresPropertiesWith_ScaffoldColumnFalse()
{
// Arrange
var expected =
@"<div class=""HtmlEncode[[display-label]]"">HtmlEncode[[Property1]]</div>
<div class=""HtmlEncode[[display-field]]""></div>
<div class=""HtmlEncode[[display-label]]"">HtmlEncode[[Property3]]</div>
<div class=""HtmlEncode[[display-field]]""></div>
";
var expected = "<div class=\"HtmlEncode[[display-label]]\">HtmlEncode[[Property1]]</div>" + Environment.NewLine +
"<div class=\"HtmlEncode[[display-field]]\"></div>"+ Environment.NewLine +
"<div class=\"HtmlEncode[[display-label]]\">HtmlEncode[[Property3]]</div>"+ Environment.NewLine +
"<div class=\"HtmlEncode[[display-field]]\"></div>"+ Environment.NewLine;

var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange indentation now that you're not using a here string. Also try to get into the habit of putting spaces around binary operators even in test code. That way you won't mess up the product code.

var viewEngine = new Mock<ICompositeViewEngine>();
viewEngine.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
Expand Down Expand Up @@ -112,6 +114,62 @@ await viewExecutor.ExecuteAsync(
Assert.Equal("abcd", Encoding.UTF8.GetString(memoryStream.ToArray()));
}

private static IServiceProvider GetServiceProvider()
{
var httpContext = new HttpContextAccessor() { HttpContext = new DefaultHttpContext() };
var serviceCollection = new ServiceCollection();
serviceCollection.AddInstance<IModelMetadataProvider>(new EmptyModelMetadataProvider());
var tempDataProvider = new SessionStateTempDataProvider();
serviceCollection.AddInstance<ITempDataDictionary>(new TempDataDictionary(httpContext, tempDataProvider));

return serviceCollection.BuildServiceProvider();
}

[Fact]
public async Task ExecuteAsync_ViewResultAllowNull()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need the same unit test for a ViewComponentResult

{
// Arrange
var tempDataNull = false;
var viewDataNull = false;
var deligateHit = false;

var view = CreateView(async (v) =>
{
deligateHit = true;
tempDataNull = v.TempData == null;
viewDataNull = v.ViewData == null;

await v.Writer.WriteAsync("abcd");
});
var context = new DefaultHttpContext();

var memoryStream = new MemoryStream();
context.Response.Body = memoryStream;

var actionContext = new ActionContext(
context,
new RouteData(),
new ActionDescriptor());

context.RequestServices = GetServiceProvider();
var viewExecutor = CreateViewExecutor();

// Act
await viewExecutor.ExecuteAsync(
actionContext,
view,
null,
null,
contentType: null,
statusCode: 200);

// Assert
Assert.Equal(200, context.Response.StatusCode);
Assert.True(deligateHit);
Assert.False(viewDataNull);
Assert.False(tempDataNull);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an assertion showing the delegate calling WriteAsync() was actually invoked. Might be worth another Boolean variable.

}

[Fact]
public async Task ExecuteAsync_SetsStatusCode()
{
Expand Down