From 5119d16b64170af0df6a857cdde097e01a756445 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 16 Oct 2014 11:18:10 -0700 Subject: [PATCH] ViewComponent.Invoke() should be able to invoke views Fixes #285 --- Mvc.sln | 13 ++++ .../ViewComponents/IViewComponentResult.cs | 13 ++++ .../ViewComponents/ViewViewComponentResult.cs | 14 +++- .../ViewViewComponentResultTest.cs | 71 +++++++++++++++++++ .../ViewComponentTests.cs | 55 ++++++++++++++ .../project.json | 7 +- .../ViewComponentWebSite/HomeController.cs | 20 ++++++ .../ViewComponentWebSite/SampleModel.cs | 19 +++++ test/WebSites/ViewComponentWebSite/Startup.cs | 21 ++++++ .../ViewComponentWebSite/TestViewComponent.cs | 20 ++++++ .../ViewComponentWebSite.kproj | 20 ++++++ .../ViewComponentWebSite/ViewDataComponent.cs | 25 +++++++ .../Shared/Components/Test/Default.cshtml | 2 + .../ComponentThatReadsViewData.cshtml | 1 + .../Shared/ViewWithAsyncComponents.cshtml | 5 ++ .../Shared/ViewWithSyncComponents.cshtml | 5 ++ .../Views/_viewstart.cshtml | 1 + .../ViewComponentWebSite/project.json | 11 +++ 18 files changed, 319 insertions(+), 4 deletions(-) create mode 100644 test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs create mode 100644 test/WebSites/ViewComponentWebSite/HomeController.cs create mode 100644 test/WebSites/ViewComponentWebSite/SampleModel.cs create mode 100644 test/WebSites/ViewComponentWebSite/Startup.cs create mode 100644 test/WebSites/ViewComponentWebSite/TestViewComponent.cs create mode 100644 test/WebSites/ViewComponentWebSite/ViewComponentWebSite.kproj create mode 100644 test/WebSites/ViewComponentWebSite/ViewDataComponent.cs create mode 100644 test/WebSites/ViewComponentWebSite/Views/Shared/Components/Test/Default.cshtml create mode 100644 test/WebSites/ViewComponentWebSite/Views/Shared/Components/ViewData/ComponentThatReadsViewData.cshtml create mode 100644 test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithAsyncComponents.cshtml create mode 100644 test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithSyncComponents.cshtml create mode 100644 test/WebSites/ViewComponentWebSite/Views/_viewstart.cshtml create mode 100644 test/WebSites/ViewComponentWebSite/project.json diff --git a/Mvc.sln b/Mvc.sln index 9bd980f531..e6a4c9cd93 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -98,6 +98,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "WebApiCompatShimWebSite", " EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.WebApiCompatShimTest", "test\Microsoft.AspNet.Mvc.WebApiCompatShimTest\Microsoft.AspNet.Mvc.WebApiCompatShimTest.kproj", "{5DE8E4D9-AACD-4B5F-819F-F091383FB996}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ViewComponentWebSite", "test\WebSites\ViewComponentWebSite\ViewComponentWebSite.kproj", "{24B59501-5F37-4129-96E6-F02EC34C7E2C}" +EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TagHelperSample.Web", "samples\TagHelperSample.Web\TagHelperSample.Web.kproj", "{2223120F-D675-40DA-8CD8-11DC14A0B2C7}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TagHelpers", "src\Microsoft.AspNet.Mvc.TagHelpers\Microsoft.AspNet.Mvc.TagHelpers.kproj", "{B2347320-308E-4D2B-AEC8-005DFA68B0C9}" @@ -524,6 +526,16 @@ Global {5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Mixed Platforms.Build.0 = Release|Any CPU {5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|x86.ActiveCfg = Release|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Debug|x86.ActiveCfg = Debug|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Release|Any CPU.Build.0 = Release|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {24B59501-5F37-4129-96E6-F02EC34C7E2C}.Release|x86.ActiveCfg = Release|Any CPU {2223120F-D675-40DA-8CD8-11DC14A0B2C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2223120F-D675-40DA-8CD8-11DC14A0B2C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {2223120F-D675-40DA-8CD8-11DC14A0B2C7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -601,6 +613,7 @@ Global {23D30B8C-04B1-4577-A604-ED27EA1E4A0E} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E} {B2B7BC91-688E-4C1E-A71F-CE948D958DDF} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {5DE8E4D9-AACD-4B5F-819F-F091383FB996} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} + {24B59501-5F37-4129-96E6-F02EC34C7E2C} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {2223120F-D675-40DA-8CD8-11DC14A0B2C7} = {DAAE4C74-D06F-4874-A166-33305D2643CE} {B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E} {860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentResult.cs index 1cc4cdb36c..3244cbbcc5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/IViewComponentResult.cs @@ -5,10 +5,23 @@ using System.Threading.Tasks; namespace Microsoft.AspNet.Mvc { + /// + /// Result type of a . + /// public interface IViewComponentResult { + /// + /// Executes the result of a using the specified . + /// + /// The for the current component execution. void Execute([NotNull] ViewComponentContext context); + /// + /// Asynchronously executes the result of a using the specified + /// . + /// + /// The for the current component execution. + /// A that represents the asynchronous execution. Task ExecuteAsync([NotNull] ViewComponentContext context); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs index 5cb32e5ec6..dc587b091a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs @@ -9,6 +9,9 @@ using Microsoft.AspNet.Mvc.Rendering; namespace Microsoft.AspNet.Mvc { + /// + /// A that renders a partial view when executed. + /// public class ViewViewComponentResult : IViewComponentResult { // {0} is the component name, {1} is the view name. @@ -27,11 +30,20 @@ namespace Microsoft.AspNet.Mvc public ViewDataDictionary ViewData { get; private set; } + /// + /// Locates and renders a view specified by . + /// + /// The for the current component execution. + /// + /// This method synchronously calls and blocks on . + /// public void Execute([NotNull] ViewComponentContext context) { - throw new NotImplementedException("There's no support for syncronous views right now."); + var task = ExecuteAsync(context); + TaskHelper.WaitAndThrowIfFaulted(task); } + /// public async Task ExecuteAsync([NotNull] ViewComponentContext context) { string qualifiedViewName; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs index a3da98501a..f62f864cdc 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs @@ -16,6 +16,77 @@ namespace Microsoft.AspNet.Mvc { public class ViewViewComponentResultTest { + [Fact] + public void Execute_RendersPartialViews() + { + // Arrange + var view = new Mock(); + view.Setup(v => v.RenderAsync(It.IsAny())) + .Returns(Task.FromResult(result: true)) + .Verifiable(); + var viewEngine = new Mock(MockBehavior.Strict); + viewEngine.Setup(e => e.FindPartialView(It.IsAny(), It.IsAny())) + .Returns(ViewEngineResult.Found("some-view", view.Object)) + .Verifiable(); + var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); + var result = new ViewViewComponentResult(viewEngine.Object, "some-view", viewData); + var viewComponentContext = GetViewComponentContext(view.Object, viewData); + + // Act + result.Execute(viewComponentContext); + + // Assert + viewEngine.Verify(); + view.Verify(); + } + + [Fact] + public void Execute_ThrowsIfPartialViewCannotBeFound() + { + // Arrange + var expected = string.Join(Environment.NewLine, + "The view 'Components/Object/some-view' was not found. The following locations were searched:", + "location1", + "location2."); + var view = Mock.Of(); + var viewEngine = new Mock(MockBehavior.Strict); + viewEngine.Setup(e => e.FindPartialView(It.IsAny(), It.IsAny())) + .Returns(ViewEngineResult.NotFound("some-view", new[] { "location1", "location2" })) + .Verifiable(); + var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); + var result = new ViewViewComponentResult(viewEngine.Object, "some-view", viewData); + var viewComponentContext = GetViewComponentContext(view, viewData); + + // Act and Assert + var ex = Assert.Throws(() => result.Execute(viewComponentContext)); + Assert.Equal(expected, ex.Message); + } + + [Fact] + public void Execute_DoesNotWrapThrownExceptionsInAggregateExceptions() + { + // Arrange + var expected = new IndexOutOfRangeException(); + var view = new Mock(); + view.Setup(v => v.RenderAsync(It.IsAny())) + .Throws(expected) + .Verifiable(); + var viewEngine = new Mock(MockBehavior.Strict); + viewEngine.Setup(e => e.FindPartialView(It.IsAny(), It.IsAny())) + .Returns(ViewEngineResult.Found("some-view", view.Object)) + .Verifiable(); + var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); + var result = new ViewViewComponentResult(viewEngine.Object, "some-view", viewData); + var viewComponentContext = GetViewComponentContext(view.Object, viewData); + + // Act + var actual = Record.Exception(() => result.Execute(viewComponentContext)); + + // Assert + Assert.Same(expected, actual); + view.Verify(); + } + [Fact] public async Task ExecuteAsync_RendersPartialViews() { diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs new file mode 100644 index 0000000000..cc98dc60d9 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ViewComponentTests.cs @@ -0,0 +1,55 @@ +// 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 System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using ViewComponentWebSite; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class ViewComponentTests + { + private readonly IServiceProvider _provider = TestHelper.CreateServices("ViewComponentWebSite"); + private readonly Action _app = new Startup().Configure; + + public static IEnumerable ViewViewComponents_AreRenderedCorrectlyData + { + get + { + yield return new[] + { + "ViewWithAsyncComponents", + string.Join(Environment.NewLine, + "value-from-component value-from-view", + "ViewWithAsyncComponents InvokeAsync: hello from viewdatacomponent") + }; + + yield return new[] + { + "ViewWithSyncComponents", + string.Join(Environment.NewLine, + "value-from-component value-from-view", + "ViewWithSyncComponents Invoke: hello from viewdatacomponent") + }; + } + } + + [Theory] + [MemberData(nameof(ViewViewComponents_AreRenderedCorrectlyData))] + public async Task ViewViewComponents_AreRenderedCorrectly(string actionName, string expected) + { + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var body = await client.GetStringAsync("http://localhost/Home/" + actionName); + + // Assert + Assert.Equal(expected, body.Trim()); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index f1e68382fc..6c547ea0c8 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -21,9 +21,11 @@ "RoutingWebSite": "1.0.0", "RazorWebSite": "1.0.0", "RazorInstrumentationWebsite": "1.0.0", - "ValueProvidersSite": "1.0.0", - "XmlSerializerWebSite": "1.0.0", + "TagHelpersWebSite": "1.0.0", "UrlHelperWebSite": "1.0.0", + "ValueProvidersSite": "1.0.0", + "ViewComponentWebSite": "1.0.0", + "XmlSerializerWebSite": "1.0.0", "WebApiCompatShimWebSite": "1.0.0", "Microsoft.AspNet.TestHost": "1.0.0-*", @@ -34,7 +36,6 @@ "Microsoft.Framework.DependencyInjection": "1.0.0-*", "Microsoft.Framework.Logging": "1.0.0-*", "Microsoft.Framework.Runtime.Interfaces": "1.0.0-*", - "TagHelpersWebSite": "1.0.0", "Xunit.KRunner": "1.0.0-*" }, "commands": { diff --git a/test/WebSites/ViewComponentWebSite/HomeController.cs b/test/WebSites/ViewComponentWebSite/HomeController.cs new file mode 100644 index 0000000000..4d38595739 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/HomeController.cs @@ -0,0 +1,20 @@ +// 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 HomeController + { + public ViewResult ViewWithAsyncComponents() + { + return new ViewResult(); + } + + public ViewResult ViewWithSyncComponents() + { + return new ViewResult(); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/SampleModel.cs b/test/WebSites/ViewComponentWebSite/SampleModel.cs new file mode 100644 index 0000000000..7a746e93c0 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/SampleModel.cs @@ -0,0 +1,19 @@ +// 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 System.Threading.Tasks; + +namespace ViewComponentWebSite +{ + public class SampleModel + { + public string Prop1 { get; set; } + + public string Prop2 { get; set; } + + public Task GetValueAsync() + { + return Task.FromResult(Prop1 + " " + Prop2); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Startup.cs b/test/WebSites/ViewComponentWebSite/Startup.cs new file mode 100644 index 0000000000..1629fb4954 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Startup.cs @@ -0,0 +1,21 @@ +// 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.Builder; +using Microsoft.Framework.DependencyInjection; + +namespace ViewComponentWebSite +{ + public class Startup + { + public void Configure(IApplicationBuilder app) + { + var configuration = app.GetTestConfiguration(); + app.UseServices(services => + { + services.AddMvc(configuration); + }); + app.UseMvc(); + } + } +} diff --git a/test/WebSites/ViewComponentWebSite/TestViewComponent.cs b/test/WebSites/ViewComponentWebSite/TestViewComponent.cs new file mode 100644 index 0000000000..8d797e8567 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/TestViewComponent.cs @@ -0,0 +1,20 @@ +// 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 TestViewComponent : ViewComponent + { + public IViewComponentResult Invoke(string valueFromView) + { + var model = new SampleModel + { + Prop1 = "value-from-component", + Prop2 = valueFromView + }; + return View(model); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/ViewComponentWebSite.kproj b/test/WebSites/ViewComponentWebSite/ViewComponentWebSite.kproj new file mode 100644 index 0000000000..b5c87ebc0a --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/ViewComponentWebSite.kproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 24b59501-5f37-4129-96e6-f02ec34c7e2c + + + + + + + + 2.0 + + + \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/ViewDataComponent.cs b/test/WebSites/ViewComponentWebSite/ViewDataComponent.cs new file mode 100644 index 0000000000..3bfd3accb9 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/ViewDataComponent.cs @@ -0,0 +1,25 @@ +// 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 System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace ViewComponentWebSite +{ + [ViewComponent(Name = "ViewData")] + public class ViewDataComponent : ViewComponent + { + public ViewViewComponentResult Invoke() + { + ViewData["value-from-component"] = nameof(Invoke) + ": hello from viewdatacomponent"; + return View("ComponentThatReadsViewData"); + } + + public Task InvokeAsync() + { + ViewData["value-from-component"] = nameof(InvokeAsync) + ": hello from viewdatacomponent"; + var result = View("ComponentThatReadsViewData"); + return Task.FromResult(result); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Views/Shared/Components/Test/Default.cshtml b/test/WebSites/ViewComponentWebSite/Views/Shared/Components/Test/Default.cshtml new file mode 100644 index 0000000000..67483c0672 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/Shared/Components/Test/Default.cshtml @@ -0,0 +1,2 @@ +@model SampleModel +@await Model.GetValueAsync() \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Views/Shared/Components/ViewData/ComponentThatReadsViewData.cshtml b/test/WebSites/ViewComponentWebSite/Views/Shared/Components/ViewData/ComponentThatReadsViewData.cshtml new file mode 100644 index 0000000000..f74815c4ea --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/Shared/Components/ViewData/ComponentThatReadsViewData.cshtml @@ -0,0 +1 @@ +@ViewData["value-from-view"] @ViewData["value-from-component"] \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithAsyncComponents.cshtml b/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithAsyncComponents.cshtml new file mode 100644 index 0000000000..b2afc51028 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithAsyncComponents.cshtml @@ -0,0 +1,5 @@ +@{ + ViewData["value-from-view"] = ViewContext.ActionDescriptor.Name; +} +@await Component.InvokeAsync("Test", "value-from-view") +@await Component.InvokeAsync("ViewData") \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithSyncComponents.cshtml b/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithSyncComponents.cshtml new file mode 100644 index 0000000000..ae3da14d03 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/Shared/ViewWithSyncComponents.cshtml @@ -0,0 +1,5 @@ +@{ + ViewData["value-from-view"] = ViewContext.ActionDescriptor.Name; +} +@Component.Invoke("Test", "value-from-view") +@Component.Invoke("ViewData") diff --git a/test/WebSites/ViewComponentWebSite/Views/_viewstart.cshtml b/test/WebSites/ViewComponentWebSite/Views/_viewstart.cshtml new file mode 100644 index 0000000000..8789eff591 --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/Views/_viewstart.cshtml @@ -0,0 +1 @@ +@using ViewComponentWebSite \ No newline at end of file diff --git a/test/WebSites/ViewComponentWebSite/project.json b/test/WebSites/ViewComponentWebSite/project.json new file mode 100644 index 0000000000..0c1b3e5ffb --- /dev/null +++ b/test/WebSites/ViewComponentWebSite/project.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0" + }, + "frameworks": { + "aspnet50": { }, + "aspnetcore50": { } + }, + "webroot": "wwwroot" +}