diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewDataDictionary.cs b/src/Microsoft.AspNet.Mvc.Core/ViewDataDictionary.cs index 061952922d..6b940b6e94 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewDataDictionary.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewDataDictionary.cs @@ -51,7 +51,12 @@ namespace Microsoft.AspNet.Mvc new TemplateInfo(source.TemplateInfo)) { _modelMetadata = source.ModelMetadata; - SetModel(model); + // If we're constructing a derived ViewDataDictionary (or any other value type), + // SetModel will throw if we try to set it to null. We should not throw in that case. + if (model != null) + { + SetModel(model); + } } private ViewDataDictionary(IModelMetadataProvider metadataProvider, diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewDataDictionaryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewDataDictionaryTest.cs index 093a0a4105..4198c1ba7e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewDataDictionaryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewDataDictionaryTest.cs @@ -116,6 +116,46 @@ namespace Microsoft.AspNet.Mvc.Core Assert.IsType>(viewData.Data); } + [Fact] + public void CopyConstructorDoesNotThrowOnNullModel() + { + // Arrange + var metadataProvider = new EmptyModelMetadataProvider(); + var source = new ViewDataDictionary(metadataProvider); + source["key1"] = "value1"; + + // Act + var viewData = new ViewDataDictionary(source, null); + + // Assert + Assert.NotNull(viewData.ModelState); + Assert.NotNull(viewData.TemplateInfo); + Assert.Null(viewData.Model); + Assert.Null(viewData.ModelMetadata); + Assert.Equal("value1", viewData["key1"]); + Assert.IsType>(viewData.Data); + } + + [Fact] + public void CopyConstructorDoesNotThrowOnNullModel_WithValueTypeTModel() + { + // Arrange + var metadataProvider = new EmptyModelMetadataProvider(); + var source = new ViewDataDictionary(metadataProvider); + source["key1"] = "value1"; + + // Act + var viewData = new ViewDataDictionary(source, null); + + // Assert + Assert.NotNull(viewData.ModelState); + Assert.NotNull(viewData.TemplateInfo); + Assert.Throws(() => viewData.Model); + Assert.NotNull(viewData.ModelMetadata); + Assert.Equal("value1", viewData["key1"]); + Assert.IsType>(viewData.Data); + } + private class TestModel { } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs index cc98dc60d9..dcd94a11fe 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs @@ -51,5 +51,18 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // Assert Assert.Equal(expected, body.Trim()); } + + [Fact] + public async Task ViewComponents_SupportsValueType() + { + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var body = await client.GetStringAsync("http://localhost/Home/ViewWithIntegerViewComponent"); + + // Assert + Assert.Equal("10", body.Trim()); + } } } diff --git a/test/WebSites/ViewComponentWebSite/HomeController.cs b/test/WebSites/ViewComponentWebSite/HomeController.cs index 4d38595739..3e7b75a6e9 100644 --- a/test/WebSites/ViewComponentWebSite/HomeController.cs +++ b/test/WebSites/ViewComponentWebSite/HomeController.cs @@ -16,5 +16,10 @@ namespace ViewComponentWebSite { return new ViewResult(); } + + public ViewResult ViewWithIntegerViewComponent() + { + return new ViewResult(); + } } } \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/IntegerViewComponent.cs b/test/WebSites/ViewComponentWebSite/IntegerViewComponent.cs new file mode 100644 index 0000000000..d44ffb4349 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/IntegerViewComponent.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc; + +namespace ViewComponentWebSite +{ + public class IntegerViewComponent : ViewComponent + { + public IViewComponentResult Invoke(int valueFromView) + { + return View(valueFromView); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Views/Shared/Components/Integer/Default.cshtml b/test/WebSites/ViewComponentWebSite/Views/Shared/Components/Integer/Default.cshtml new file mode 100644 index 0000000000..2ee120b02b --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/Shared/Components/Integer/Default.cshtml @@ -0,0 +1,2 @@ +@model int +@Model \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithIntegerViewComponent.cshtml b/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithIntegerViewComponent.cshtml new file mode 100644 index 0000000000..b09c442b8e --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithIntegerViewComponent.cshtml @@ -0,0 +1 @@ +@Component.Invoke("Integer", 10) \ No newline at end of file