diff --git a/Mvc.sln b/Mvc.sln index 0664e78c78..a37943cf5e 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22602.0 +VisualStudioVersion = 14.0.22609.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}" EndProject @@ -148,6 +148,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BestEffortLinkGenerationWeb EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LowercaseUrlsWebSite", "test\WebSites\LowercaseUrlsWebSite\LowercaseUrlsWebSite.kproj", "{BCDB13A6-7D6E-485E-8424-A156432B71AC}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TempDataWebSite", "test\WebSites\TempDataWebSite\TempDataWebSite.kproj", "{8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -866,6 +868,18 @@ Global {BCDB13A6-7D6E-485E-8424-A156432B71AC}.Release|Mixed Platforms.Build.0 = Release|Any CPU {BCDB13A6-7D6E-485E-8424-A156432B71AC}.Release|x86.ActiveCfg = Release|Any CPU {BCDB13A6-7D6E-485E-8424-A156432B71AC}.Release|x86.Build.0 = Release|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Debug|x86.ActiveCfg = Debug|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Debug|x86.Build.0 = Debug|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Release|Any CPU.Build.0 = Release|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Release|x86.ActiveCfg = Release|Any CPU + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -938,5 +952,6 @@ Global {A19022EF-9BA3-4349-94E4-F48E13E1C8AE} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {B11C99C9-E577-4CA2-AC53-4F20EA71AD34} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {BCDB13A6-7D6E-485E-8424-A156432B71AC} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} + {8AEB631E-AB74-4D2E-83FB-8931EE10D9D3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} EndGlobalSection EndGlobal diff --git a/samples/MvcSample.Web/HomeController.cs b/samples/MvcSample.Web/HomeController.cs index c2f0104729..4ece3bbd80 100644 --- a/samples/MvcSample.Web/HomeController.cs +++ b/samples/MvcSample.Web/HomeController.cs @@ -113,6 +113,21 @@ namespace MvcSample.Web return View(); } + public ActionResult AddTempData() + { + TempData["controllerData"] = "Temporary data from controller through ViewBag."; + TempData["tempData"] = "Temporary data directly from TempData."; + return RedirectToAction("UseTempData"); + } + + public ActionResult UseTempData() + { + var data = TempData["controllerData"]; + ViewBag.TempData = data; + + return View("MyView", CreateUser()); + } + /// /// Action that exercises input formatter /// diff --git a/samples/MvcSample.Web/Startup.cs b/samples/MvcSample.Web/Startup.cs index 0fe1ae37e6..ab347cd4b8 100644 --- a/samples/MvcSample.Web/Startup.cs +++ b/samples/MvcSample.Web/Startup.cs @@ -2,9 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Security.Claims; -using Microsoft.AspNet.Authentication; -using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Razor; @@ -41,6 +38,9 @@ namespace MvcSample.Web app.UseServices(services => { + services.AddCachingServices(); + services.AddSessionServices(); + services.AddMvc(); services.AddSingleton(); services.AddSingleton(); @@ -83,6 +83,9 @@ namespace MvcSample.Web { app.UseServices(services => { + services.AddCachingServices(); + services.AddSessionServices(); + services.AddMvc(); services.AddSingleton(); services.AddSingleton(); @@ -102,6 +105,7 @@ namespace MvcSample.Web }); } + app.UseInMemorySession(); app.UseMvc(routes => { routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}"); diff --git a/samples/MvcSample.Web/Views/Shared/MyView.cshtml b/samples/MvcSample.Web/Views/Shared/MyView.cshtml index 0e2bc5ea94..d1f6231da3 100644 --- a/samples/MvcSample.Web/Views/Shared/MyView.cshtml +++ b/samples/MvcSample.Web/Views/Shared/MyView.cshtml @@ -51,6 +51,7 @@

ASP.NET

ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.

Learn more »

+

@ViewBag.TempData
@TempData["tempData"]

File Upload Demo:
diff --git a/samples/MvcSample.Web/project.json b/samples/MvcSample.Web/project.json index 8c4dfb6618..b5f9ee8245 100644 --- a/samples/MvcSample.Web/project.json +++ b/samples/MvcSample.Web/project.json @@ -14,6 +14,7 @@ "Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*", "Microsoft.AspNet.Server.IIS": "1.0.0-*", "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.AspNet.Session": "1.0.0-*", "Microsoft.AspNet.StaticFiles": "1.0.0-*", "Microsoft.Framework.PropertyHelper.Internal": { "version": "1.0.0-*", "type": "build" }, "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" } diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs index 3d5c12b3a8..3c60ff4a7c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/PartialViewResult.cs @@ -32,6 +32,11 @@ namespace Microsoft.AspNet.Mvc /// public ViewDataDictionary ViewData { get; set; } + /// + /// Gets or sets the used for rendering the view for this result. + /// + public ITempDataDictionary TempData { get; set; } + /// /// Gets or sets the used to locate views. /// @@ -57,7 +62,7 @@ namespace Microsoft.AspNet.Mvc using (view as IDisposable) { - await ViewExecutor.ExecuteAsync(view, context, ViewData, contentType: null); + await ViewExecutor.ExecuteAsync(view, context, ViewData, TempData, contentType: null); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs index bcf4b224db..36d9b9b109 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectResult.cs @@ -60,6 +60,8 @@ namespace Microsoft.AspNet.Mvc destinationUrl = urlHelper.Content(Url); } + var tempData = context.HttpContext.RequestServices.GetRequiredService(); + tempData.Keep(); context.HttpContext.Response.Redirect(destinationUrl, Permanent); } diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToActionResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToActionResult.cs index aced988ba9..2ab54025a9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToActionResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToActionResult.cs @@ -51,6 +51,8 @@ namespace Microsoft.AspNet.Mvc throw new InvalidOperationException(Resources.NoRoutesMatched); } + var tempData = context.HttpContext.RequestServices.GetRequiredService(); + tempData.Keep(); context.HttpContext.Response.Redirect(destinationUrl, Permanent); } diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToRouteResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToRouteResult.cs index 38b8238f14..a658c50aec 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToRouteResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/RedirectToRouteResult.cs @@ -51,6 +51,8 @@ namespace Microsoft.AspNet.Mvc throw new InvalidOperationException(Resources.NoRoutesMatched); } + var tempData = context.HttpContext.RequestServices.GetRequiredService(); + tempData.Keep(); context.HttpContext.Response.Redirect(destinationUrl, Permanent); } diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs index 0035b1a999..7737c519b2 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewExecutor.cs @@ -22,10 +22,12 @@ namespace Microsoft.AspNet.Mvc /// The to render. /// The for the current executing action. /// The for the view being rendered. + /// The for the view being rendered. /// A that represents the asychronous rendering. public static async Task ExecuteAsync([NotNull] IView view, [NotNull] ActionContext actionContext, [NotNull] ViewDataDictionary viewData, + [NotNull] ITempDataDictionary tempData, string contentType) { if (string.IsNullOrEmpty(contentType)) @@ -40,7 +42,7 @@ namespace Microsoft.AspNet.Mvc { try { - var viewContext = new ViewContext(actionContext, view, viewData, writer); + var viewContext = new ViewContext(actionContext, view, viewData, tempData, writer); await view.RenderAsync(viewContext); } catch diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs index 09d1f70427..e51064ab7a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs @@ -32,6 +32,11 @@ namespace Microsoft.AspNet.Mvc /// public ViewDataDictionary ViewData { get; set; } + /// + /// Gets or sets the for this result. + /// + public ITempDataDictionary TempData { get; set; } + /// /// Gets or sets the used to locate views. /// @@ -57,7 +62,7 @@ namespace Microsoft.AspNet.Mvc using (view as IDisposable) { - await ViewExecutor.ExecuteAsync(view, context, ViewData, contentType: null); + await ViewExecutor.ExecuteAsync(view, context, ViewData, TempData, contentType: null); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs index fd12c596e4..fbca4e2fea 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs @@ -185,6 +185,12 @@ namespace Microsoft.AspNet.Mvc } } + /// + /// Gets or sets used by . + /// + [FromServices] + public ITempDataDictionary TempData { get; set; } + /// /// Gets the dynamic view bag. /// @@ -254,6 +260,7 @@ namespace Microsoft.AspNet.Mvc { ViewName = viewName, ViewData = ViewData, + TempData = TempData }; } @@ -310,6 +317,7 @@ namespace Microsoft.AspNet.Mvc { ViewName = viewName, ViewData = ViewData, + TempData = TempData }; } diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs index af37f59642..705c062781 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs @@ -16,6 +16,7 @@ namespace Microsoft.AspNet.Mvc.Core private readonly ControllerActionDescriptor _descriptor; private readonly IControllerFactory _controllerFactory; private readonly IControllerActionArgumentBinder _argumentBinder; + private readonly ITempDataDictionary _tempData; public ControllerActionInvoker( [NotNull] ActionContext actionContext, @@ -27,7 +28,8 @@ namespace Microsoft.AspNet.Mvc.Core [NotNull] IModelBinderProvider modelBinderProvider, [NotNull] IModelValidatorProviderProvider modelValidatorProviderProvider, [NotNull] IValueProviderFactoryProvider valueProviderFactoryProvider, - [NotNull] IScopedInstance actionBindingContextAccessor) + [NotNull] IScopedInstance actionBindingContextAccessor, + [NotNull] ITempDataDictionary tempData) : base( actionContext, filterProviders, @@ -40,6 +42,7 @@ namespace Microsoft.AspNet.Mvc.Core _descriptor = descriptor; _controllerFactory = controllerFactory; _argumentBinder = controllerActionArgumentBinder; + _tempData = tempData; if (descriptor.MethodInfo == null) { @@ -59,6 +62,7 @@ namespace Microsoft.AspNet.Mvc.Core protected override void ReleaseInstance(object instance) { + _tempData.Save(); _controllerFactory.ReleaseController(instance); } diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs index f3fcec1287..f5dc3a3c7a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs @@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.Core private readonly IModelValidatorProviderProvider _modelValidationProviderProvider; private readonly IValueProviderFactoryProvider _valueProviderFactoryProvider; private readonly IScopedInstance _actionBindingContextAccessor; + private readonly ITempDataDictionary _tempData; public ControllerActionInvokerProvider( IControllerFactory controllerFactory, @@ -29,7 +30,8 @@ namespace Microsoft.AspNet.Mvc.Core IModelBinderProvider modelBinderProvider, IModelValidatorProviderProvider modelValidationProviderProvider, IValueProviderFactoryProvider valueProviderFactoryProvider, - IScopedInstance actionBindingContextAccessor) + IScopedInstance actionBindingContextAccessor, + ITempDataDictionary tempData) { _controllerFactory = controllerFactory; _inputFormattersProvider = inputFormattersProvider; @@ -39,6 +41,7 @@ namespace Microsoft.AspNet.Mvc.Core _modelValidationProviderProvider = modelValidationProviderProvider; _valueProviderFactoryProvider = valueProviderFactoryProvider; _actionBindingContextAccessor = actionBindingContextAccessor; + _tempData = tempData; } public int Order @@ -63,7 +66,8 @@ namespace Microsoft.AspNet.Mvc.Core _modelBinderProvider, _modelValidationProviderProvider, _valueProviderFactoryProvider, - _actionBindingContextAccessor); + _actionBindingContextAccessor, + _tempData); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ITempDataDictionary.cs b/src/Microsoft.AspNet.Mvc.Core/ITempDataDictionary.cs new file mode 100644 index 0000000000..29865c5861 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ITempDataDictionary.cs @@ -0,0 +1,42 @@ +// 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.Collections.Generic; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Represents a set of data that persists only from one request to the next. + /// + public interface ITempDataDictionary : IDictionary + { + /// + /// Loads the dictionary by using the registered . + /// + void Load(); + + /// + /// Saves the dictionary by using the registered . + /// + void Save(); + + /// + /// Marks all keys in the dictionary for retention. + /// + void Keep(); + + /// + /// Marks the specified key in the dictionary for retention. + /// + /// The key to retain in the dictionary. + void Keep(string key); + + /// + /// Returns an object that contains the element that is associated with the specified key, + /// without marking the key for deletion. + /// + /// The key of the element to return. + /// An object that contains the element that is associated with the specified key. + object Peek(string key); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ITempDataProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ITempDataProvider.cs new file mode 100644 index 0000000000..46d679891c --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ITempDataProvider.cs @@ -0,0 +1,29 @@ +// 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.Collections.Generic; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Defines the contract for temporary-data providers that store data that is viewed on the next request. + /// + public interface ITempDataProvider + { + /// + /// Loads the temporary data. + /// + /// The . + /// The temporary data. + IDictionary LoadTempData([NotNull] HttpContext context); + + /// + /// Saves the temporary data. + /// + /// The . + /// The values to save. + void SaveTempData([NotNull] HttpContext context, IDictionary values); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index c52e34159e..23b15cc092 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -113,6 +113,15 @@ namespace Microsoft.AspNet.Mvc.Rendering } } + /// + public ITempDataDictionary TempData + { + get + { + return ViewContext.TempData; + } + } + /// public IHtmlEncoder HtmlEncoder { get; } @@ -450,7 +459,7 @@ namespace Microsoft.AspNet.Mvc.Rendering var view = viewEngineResult.View; using (view as IDisposable) { - var viewContext = new ViewContext(ViewContext, view, newViewData, writer); + var viewContext = new ViewContext(ViewContext, view, newViewData, TempData, writer); await viewEngineResult.View.RenderAsync(viewContext); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs index 4a27b73ee0..48cc8df723 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs @@ -46,6 +46,11 @@ namespace Microsoft.AspNet.Mvc.Rendering /// ViewDataDictionary ViewData { get; } + /// + /// Gets the current instance. + /// + ITempDataDictionary TempData { get; } + /// /// Gets the to be used for encoding HTML. /// diff --git a/src/Microsoft.AspNet.Mvc.Core/SessionStateTempDataProvider.cs b/src/Microsoft.AspNet.Mvc.Core/SessionStateTempDataProvider.cs new file mode 100644 index 0000000000..126b00744a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/SessionStateTempDataProvider.cs @@ -0,0 +1,78 @@ +// 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.IO; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; +using Newtonsoft.Json; +using Newtonsoft.Json.Bson; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Provides session-state data to the current object. + /// + public class SessionStateTempDataProvider : ITempDataProvider + { + private static JsonSerializer jsonSerializer = new JsonSerializer(); + private static string TempDataSessionStateKey = "__ControllerTempData"; + + /// + public virtual IDictionary LoadTempData([NotNull] HttpContext context) + { + if (!IsSessionEnabled(context)) + { + // Session middleware is not enabled. No-op + return null; + } + + var tempDataDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + var session = context.Session; + byte[] value; + + if (session != null && session.TryGetValue(TempDataSessionStateKey, out value)) + { + using (var memoryStream = new MemoryStream(value)) + using (var writer = new BsonReader(memoryStream)) + { + tempDataDictionary = jsonSerializer.Deserialize>(writer); + } + + // If we got it from Session, remove it so that no other request gets it + session.Remove(TempDataSessionStateKey); + } + + return tempDataDictionary; + } + + /// + public virtual void SaveTempData([NotNull] HttpContext context, IDictionary values) + { + var hasValues = (values != null && values.Count > 0); + if (hasValues) + { + // Accessing Session property will throw if the session middleware is not enabled. + var session = context.Session; + + using (var memoryStream = new MemoryStream()) + using (var writer = new BsonWriter(memoryStream)) + { + jsonSerializer.Serialize(writer, values); + session[TempDataSessionStateKey] = memoryStream.ToArray(); + } + } + else if (IsSessionEnabled(context)) + { + var session = context.Session; + session.Remove(TempDataSessionStateKey); + } + } + + private static bool IsSessionEnabled(HttpContext context) + { + return context.GetFeature() != null; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/TempDataDictionary.cs b/src/Microsoft.AspNet.Mvc.Core/TempDataDictionary.cs new file mode 100644 index 0000000000..c8a559741b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/TempDataDictionary.cs @@ -0,0 +1,285 @@ +// 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; +using System.Collections.Generic; +using Microsoft.AspNet.Hosting; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.Mvc +{ + /// + public class TempDataDictionary : ITempDataDictionary + { + private Dictionary _data; + private bool _loaded; + private readonly ITempDataProvider _provider; + private readonly IHttpContextAccessor _contextAccessor; + private HashSet _initialKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + private HashSet _retainedKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + + /// + /// Initializes a new instance of the class. + /// + /// The that provides the HttpContext. + /// The used to Load and Save data. + public TempDataDictionary([NotNull] IHttpContextAccessor context, [NotNull] ITempDataProvider provider) + { + _provider = provider; + _loaded = false; + _contextAccessor = context; + } + + public int Count + { + get + { + Load(); + return _data.Count; + } + } + + public ICollection Keys + { + get + { + Load(); + return _data.Keys; + } + } + + public ICollection Values + { + get + { + Load(); + return _data.Values; + } + } + + bool ICollection>.IsReadOnly + { + get + { + Load(); + return ((ICollection>)_data).IsReadOnly; + } + } + + public object this[string key] + { + get + { + Load(); + object value; + if (TryGetValue(key, out value)) + { + // Mark the key for deletion since it is read. + _initialKeys.Remove(key); + return value; + } + return null; + } + set + { + Load(); + _data[key] = value; + _initialKeys.Add(key); + } + } + + /// + public void Keep() + { + Load(); + _retainedKeys.Clear(); + _retainedKeys.UnionWith(_data.Keys); + } + + /// + public void Keep(string key) + { + Load(); + _retainedKeys.Add(key); + } + + /// + public void Load() + { + if (_loaded) + { + return; + } + + var providerDictionary = _provider.LoadTempData(_contextAccessor.HttpContext); + _data = (providerDictionary != null) + ? new Dictionary(providerDictionary, StringComparer.OrdinalIgnoreCase) + : new Dictionary(StringComparer.OrdinalIgnoreCase); + _initialKeys = new HashSet(_data.Keys, StringComparer.OrdinalIgnoreCase); + _retainedKeys.Clear(); + _loaded = true; + } + + /// + public void Save() + { + if (!_loaded) + { + return; + } + + // Because it is not possible to delete while enumerating, a copy of the keys must be taken. + // Use the size of the dictionary as an upper bound to avoid creating more than one copy of the keys. + var removeCount = 0; + var keys = new string[_data.Count]; + foreach (var entry in _data) + { + if (!_initialKeys.Contains(entry.Key) && !_retainedKeys.Contains(entry.Key)) + { + keys[removeCount] = entry.Key; + removeCount++; + } + } + for (var i = 0; i < removeCount; i++) + { + _data.Remove(keys[i]); + } + + _provider.SaveTempData(_contextAccessor.HttpContext, _data); + } + + /// + public object Peek(string key) + { + Load(); + object value; + _data.TryGetValue(key, out value); + return value; + } + + public void Add(string key, object value) + { + Load(); + _data.Add(key, value); + _initialKeys.Add(key); + } + + public void Clear() + { + Load(); + _data.Clear(); + _retainedKeys.Clear(); + _initialKeys.Clear(); + } + + public bool ContainsKey(string key) + { + Load(); + return _data.ContainsKey(key); + } + + public bool ContainsValue(object value) + { + Load(); + return _data.ContainsValue(value); + } + + public IEnumerator> GetEnumerator() + { + Load(); + return new TempDataDictionaryEnumerator(this); + } + + public bool Remove(string key) + { + Load(); + _retainedKeys.Remove(key); + _initialKeys.Remove(key); + return _data.Remove(key); + } + + public bool TryGetValue(string key, out object value) + { + Load(); + // Mark the key for deletion since it is read. + _initialKeys.Remove(key); + return _data.TryGetValue(key, out value); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + Load(); + ((ICollection>)_data).CopyTo(array, index); + } + + void ICollection>.Add(KeyValuePair keyValuePair) + { + Load(); + _initialKeys.Add(keyValuePair.Key); + ((ICollection>)_data).Add(keyValuePair); + } + + bool ICollection>.Contains(KeyValuePair keyValuePair) + { + Load(); + return ((ICollection>)_data).Contains(keyValuePair); + } + + bool ICollection>.Remove(KeyValuePair keyValuePair) + { + Load(); + _initialKeys.Remove(keyValuePair.Key); + return ((ICollection>)_data).Remove(keyValuePair); + } + + IEnumerator IEnumerable.GetEnumerator() + { + Load(); + return new TempDataDictionaryEnumerator(this); + } + + private sealed class TempDataDictionaryEnumerator : IEnumerator> + { + private IEnumerator> _enumerator; + private TempDataDictionary _tempData; + + public TempDataDictionaryEnumerator(TempDataDictionary tempData) + { + _tempData = tempData; + _enumerator = _tempData._data.GetEnumerator(); + } + + public KeyValuePair Current + { + get + { + var kvp = _enumerator.Current; + // Mark the key for deletion since it is read. + _tempData._initialKeys.Remove(kvp.Key); + return kvp; + } + } + + object IEnumerator.Current + { + get { return Current; } + } + + public bool MoveNext() + { + return _enumerator.MoveNext(); + } + + public void Reset() + { + _enumerator.Reset(); + } + + void IDisposable.Dispose() + { + _enumerator.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs index 7e350aae85..78f1f15c7a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewComponents/ViewViewComponentResult.cs @@ -28,6 +28,11 @@ namespace Microsoft.AspNet.Mvc /// public ViewDataDictionary ViewData { get; set; } + /// + /// Gets or sets the instance. + /// + public ITempDataDictionary TempData { get; set; } + /// /// Gets or sets the . /// @@ -90,6 +95,7 @@ namespace Microsoft.AspNet.Mvc context.ViewContext, view, ViewData ?? context.ViewContext.ViewData, + TempData ?? context.ViewContext.TempData, context.Writer); using (view as IDisposable) diff --git a/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs b/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs index d653e745d9..e358acddc8 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ViewContext.cs @@ -24,16 +24,19 @@ namespace Microsoft.AspNet.Mvc /// The . /// The being rendered. /// The . + /// The . /// The to render output to. public ViewContext( [NotNull] ActionContext actionContext, [NotNull] IView view, [NotNull] ViewDataDictionary viewData, + [NotNull] ITempDataDictionary tempData, [NotNull] TextWriter writer) : base(actionContext) { View = view; ViewData = viewData; + TempData = tempData; Writer = writer; _formContext = _defaultFormContext; @@ -64,6 +67,7 @@ namespace Microsoft.AspNet.Mvc View = view; ViewData = viewData; + TempData = viewContext.TempData; Writer = writer; } @@ -135,6 +139,11 @@ namespace Microsoft.AspNet.Mvc /// public ViewDataDictionary ViewData { get; set; } + /// + /// Gets or sets the instance. + /// + public ITempDataDictionary TempData { get; set; } + /// /// Gets or sets the used to write the output. /// diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs index b5666cd01c..bacbc0799a 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs @@ -105,7 +105,19 @@ namespace Microsoft.AspNet.Mvc.Razor { get { - return (ViewContext == null) ? null : ViewContext.ViewBag; + return ViewContext?.ViewBag; + } + } + + /// + /// Gets the from the . + /// + /// Returns null if is null. + public ITempDataDictionary TempData + { + get + { + return ViewContext?.TempData; } } diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 40b3e2dc24..5828498774 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -177,6 +177,10 @@ namespace Microsoft.AspNet.Mvc yield return describe.Singleton(); yield return describe.Transient(); + + // Temp Data + yield return describe.Singleton(); + yield return describe.Scoped(); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs index ce3b5f6b6d..3861d7fb14 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectResultTest.cs @@ -91,6 +91,30 @@ namespace Microsoft.AspNet.Mvc.Core.Test httpResponse.Verify(); } + [Fact] + public void Execute_Calls_TempDataKeep() + { + // Arrange + var tempData = new Mock(); + tempData.Setup(t => t.Keep()).Verifiable(); + + var httpContext = new Mock(); + httpContext.Setup(o => o.Response).Returns(new Mock().Object); + httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))).Returns(tempData.Object); + var actionContext = GetActionContext(httpContext.Object); + + var result = new RedirectResult("url") + { + UrlHelper = Mock.Of() + }; + + // Act + result.ExecuteResult(actionContext); + + // Assert + tempData.Verify(t => t.Keep(), Times.Once()); + } + private static ActionContext GetActionContext(HttpContext httpContext) { var routeData = new RouteData(); @@ -105,6 +129,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test { var serviceCollection = new ServiceCollection(); serviceCollection.AddInstance(urlHelper); + serviceCollection.AddInstance(Mock.Of()); return serviceCollection.BuildServiceProvider(); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToActionResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToActionResultTest.cs index 6b1d6b7c66..de95f012f3 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToActionResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToActionResultTest.cs @@ -21,6 +21,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults var httpContext = new Mock(); var httpResponse = new Mock(); httpContext.Setup(o => o.Response).Returns(httpResponse.Object); + httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))) + .Returns(Mock.Of()); var actionContext = new ActionContext(httpContext.Object, new RouteData(), @@ -66,6 +68,32 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults "No route matches the supplied values."); } + [Fact] + public void RedirectToAction_Execute_Calls_TempDataKeep() + { + // Arrange + var tempData = new Mock(); + tempData.Setup(t => t.Keep()).Verifiable(); + + var httpContext = new Mock(); + httpContext.Setup(o => o.Response).Returns(new Mock().Object); + httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))).Returns(tempData.Object); + var actionContext = new ActionContext(httpContext.Object, + new RouteData(), + new ActionDescriptor()); + + var result = new RedirectToActionResult("SampleAction", null, null) + { + UrlHelper = GetMockUrlHelper("SampleAction") + }; + + // Act + result.ExecuteResult(actionContext); + + // Assert + tempData.Verify(t => t.Keep(), Times.Once()); + } + private static IUrlHelper GetMockUrlHelper(string returnValue) { var urlHelper = new Mock(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToRouteResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToRouteResultTest.cs index bf21fa6155..ab74cab1e6 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToRouteResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/RedirectToRouteResultTest.cs @@ -23,6 +23,8 @@ namespace Microsoft.AspNet.Mvc.Core var httpContext = new Mock(); var httpResponse = new Mock(); httpContext.Setup(o => o.Response).Returns(httpResponse.Object); + httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))) + .Returns(Mock.Of()); var actionContext = new ActionContext(httpContext.Object, new RouteData(), @@ -69,6 +71,32 @@ namespace Microsoft.AspNet.Mvc.Core "No route matches the supplied values."); } + [Fact] + public void RedirectToRoute_Execute_Calls_TempDataKeep() + { + // Arrange + var tempData = new Mock(); + tempData.Setup(t => t.Keep()).Verifiable(); + + var httpContext = new Mock(); + httpContext.Setup(o => o.Response).Returns(new Mock().Object); + httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))).Returns(tempData.Object); + var actionContext = new ActionContext(httpContext.Object, + new RouteData(), + new ActionDescriptor()); + + var result = new RedirectToRouteResult("SampleRoute", null) + { + UrlHelper = GetMockUrlHelper("SampleRoute") + }; + + // Act + result.ExecuteResult(actionContext); + + // Assert + tempData.Verify(t => t.Keep(), Times.Once()); + } + public static IEnumerable RedirectToRouteData { get diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs index d82d0f4220..c3eb5ef7fe 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ViewExecutorTest.cs @@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Mvc var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); // Act - await ViewExecutor.ExecuteAsync(view.Object, actionContext, viewData, contentType: null); + await ViewExecutor.ExecuteAsync(view.Object, actionContext, viewData, null, contentType: null); // Assert Assert.Equal(expected, memoryStream.ToArray()); @@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Mvc var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); // Act - await ViewExecutor.ExecuteAsync(view, actionContext, viewData, contentType); + await ViewExecutor.ExecuteAsync(view, actionContext, viewData, null, contentType); // Assert Assert.Equal(contentType, context.Response.ContentType); @@ -120,7 +120,7 @@ namespace Microsoft.AspNet.Mvc // Act await Record.ExceptionAsync( - () => ViewExecutor.ExecuteAsync(view.Object, actionContext, viewData, contentType: null)); + () => ViewExecutor.ExecuteAsync(view.Object, actionContext, viewData, null, contentType: null)); // Assert Assert.Equal(expectedLength, memoryStream.Length); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs index 08b2fb63fd..b8e794f540 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs @@ -51,6 +51,36 @@ namespace Microsoft.AspNet.Mvc filter.Verify(f => f.OnException(It.IsAny()), Times.Never()); } + [Fact] + public async Task InvokeAction_SavesTempData_WhenActionDoesNotThrow() + { + // Arrange + var tempData = new Mock(); + tempData.Setup(t => t.Save()).Verifiable(); + + var invoker = CreateInvoker(Mock.Of(), actionThrows: false, tempData: tempData.Object); + + // Act + await invoker.InvokeAsync(); + + // Assert + tempData.Verify(t => t.Save(), Times.Once()); + } + + [Fact] + public async Task InvokeAction_SavesTempData_WhenActionThrows() + { + // Arrange + var tempData = new Mock(); + tempData.Setup(t => t.Save()).Verifiable(); + + var invoker = CreateInvoker(Mock.Of(), actionThrows: true, tempData: tempData.Object); + + // Act & Assert + await Assert.ThrowsAsync(_actionException.GetType(), async () => await invoker.InvokeAsync()); + tempData.Verify(t => t.Save(), Times.Once()); + } + [Fact] public async Task InvokeAction_DoesNotAsyncInvokeExceptionFilter_WhenActionDoesNotThrow() { @@ -1929,12 +1959,18 @@ namespace Microsoft.AspNet.Mvc Assert.Same(input, contentResult.Value); } - private TestControllerActionInvoker CreateInvoker(IFilter filter, bool actionThrows = false) + private TestControllerActionInvoker CreateInvoker( + IFilter filter, + bool actionThrows = false, + ITempDataDictionary tempData = null) { - return CreateInvoker(new[] { filter }, actionThrows); + return CreateInvoker(new[] { filter }, actionThrows, tempData); } - private TestControllerActionInvoker CreateInvoker(IFilter[] filters, bool actionThrows = false) + private TestControllerActionInvoker CreateInvoker( + IFilter[] filters, + bool actionThrows = false, + ITempDataDictionary tempData = null) { var actionDescriptor = new ControllerActionDescriptor() { @@ -1951,6 +1987,7 @@ namespace Microsoft.AspNet.Mvc actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ActionMethod"); } + tempData = tempData ?? new Mock().Object; var httpContext = new Mock(MockBehavior.Loose); var httpRequest = new DefaultHttpContext().Request; var httpResponse = new DefaultHttpContext().Response; @@ -1965,6 +2002,8 @@ namespace Microsoft.AspNet.Mvc httpContext.SetupGet(c => c.Response).Returns(httpResponse); httpContext.Setup(o => o.RequestServices.GetService(typeof(IOutputFormattersProvider))) .Returns(mockFormattersProvider.Object); + httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))) + .Returns(tempData); httpResponse.Body = new MemoryStream(); var options = new Mock>(); @@ -2012,7 +2051,8 @@ namespace Microsoft.AspNet.Mvc new MockModelBinderProvider(), new MockModelValidatorProviderProvider(), new MockValueProviderFactoryProvider(), - new MockScopedInstance()); + new MockScopedInstance(), + tempData); return invoker; } @@ -2044,6 +2084,8 @@ namespace Microsoft.AspNet.Mvc var context = new Mock(); context.SetupGet(c => c.Items) .Returns(new Dictionary()); + context.Setup(c => c.RequestServices.GetService(typeof(ITempDataDictionary))) + .Returns(new Mock().Object); var actionContext = new ActionContext(context.Object, new RouteData(), actionDescriptor); @@ -2067,7 +2109,8 @@ namespace Microsoft.AspNet.Mvc new MockModelBinderProvider() { ModelBinders = new List() { binder.Object } }, new MockModelValidatorProviderProvider(), new MockValueProviderFactoryProvider(), - new MockScopedInstance()); + new MockScopedInstance(), + Mock.Of()); // Act await invoker.InvokeAsync(); @@ -2164,7 +2207,8 @@ namespace Microsoft.AspNet.Mvc IModelBinderProvider modelBinderProvider, IModelValidatorProviderProvider modelValidatorProviderProvider, IValueProviderFactoryProvider valueProviderFactoryProvider, - IScopedInstance actionBindingContext) + IScopedInstance actionBindingContext, + ITempDataDictionary tempData) : base( actionContext, filterProvider, @@ -2175,7 +2219,8 @@ namespace Microsoft.AspNet.Mvc modelBinderProvider, modelValidatorProviderProvider, valueProviderFactoryProvider, - actionBindingContext) + actionBindingContext, + tempData) { ControllerFactory = controllerFactory; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs index 5bcaa9d82c..5304c27099 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs @@ -5,15 +5,16 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; -using System.Reflection; +using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Routing; using Microsoft.AspNet.Testing; using Microsoft.AspNet.WebUtilities; -using Microsoft.AspNet.Http.Core; #if DNX451 using Moq; #endif @@ -739,12 +740,13 @@ namespace Microsoft.AspNet.Mvc.Test } [Fact] - public void Controller_View_WithoutParameter_SetsResultNullViewNameAndNullViewDataModel() + public void Controller_View_WithoutParameter_SetsResultNullViewNameAndNullViewDataModelAndSameTempData() { // Arrange var controller = new TestableController() { ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()), + TempData = new TempDataDictionary(Mock.Of(), Mock.Of()), }; // Act @@ -754,16 +756,18 @@ namespace Microsoft.AspNet.Mvc.Test Assert.IsType(actualViewResult); Assert.Null(actualViewResult.ViewName); Assert.Same(controller.ViewData, actualViewResult.ViewData); + Assert.Same(controller.TempData, actualViewResult.TempData); Assert.Null(actualViewResult.ViewData.Model); } [Fact] - public void Controller_View_WithParameterViewName_SetsResultViewNameAndNullViewDataModel() + public void Controller_View_WithParameterViewName_SetsResultViewNameAndNullViewDataModelAndSameTempData() { // Arrange var controller = new TestableController() { ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()), + TempData = new TempDataDictionary(Mock.Of(), Mock.Of()), }; // Act @@ -773,16 +777,18 @@ namespace Microsoft.AspNet.Mvc.Test Assert.IsType(actualViewResult); Assert.Equal("CustomViewName", actualViewResult.ViewName); Assert.Same(controller.ViewData, actualViewResult.ViewData); + Assert.Same(controller.TempData, actualViewResult.TempData); Assert.Null(actualViewResult.ViewData.Model); } [Fact] - public void Controller_View_WithParameterViewModel_SetsResultNullViewNameAndViewDataModel() + public void Controller_View_WithParameterViewModel_SetsResultNullViewNameAndViewDataModelAndSameTempData() { // Arrange var controller = new TestableController() { ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()), + TempData = new TempDataDictionary(Mock.Of(), Mock.Of()), }; var model = new object(); @@ -793,16 +799,18 @@ namespace Microsoft.AspNet.Mvc.Test Assert.IsType(actualViewResult); Assert.Null(actualViewResult.ViewName); Assert.Same(controller.ViewData, actualViewResult.ViewData); + Assert.Same(controller.TempData, actualViewResult.TempData); Assert.Same(model, actualViewResult.ViewData.Model); } [Fact] - public void Controller_View_WithParameterViewNameAndViewModel_SetsResultViewNameAndViewDataModel() + public void Controller_View_WithParameterViewNameAndViewModel_SetsResultViewNameAndViewDataModelAndSameTempData() { // Arrange var controller = new TestableController() { ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()), + TempData = new TempDataDictionary(Mock.Of(), Mock.Of()), }; var model = new object(); @@ -813,6 +821,7 @@ namespace Microsoft.AspNet.Mvc.Test Assert.IsType(actualViewResult); Assert.Equal("CustomViewName", actualViewResult.ViewName); Assert.Same(controller.ViewData, actualViewResult.ViewData); + Assert.Same(controller.TempData, actualViewResult.TempData); Assert.Same(model, actualViewResult.ViewData.Model); } @@ -1466,6 +1475,21 @@ namespace Microsoft.AspNet.Mvc.Test Assert.Equal("The 'BindingContext' property of 'Microsoft.AspNet.Mvc.Controller' must not be null.", exception.Message); } + [Fact] + public void TempData_CanSetAndGetValues() + { + // Arrange + var controller = GetController(null, null); + var input = "Foo"; + + // Act + controller.TempData["key"] = input; + var result = controller.TempData["key"]; + + // Assert + Assert.Equal(input, result); + } + private static Controller GetController(IModelBinder binder, IValueProvider provider) { var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); @@ -1473,6 +1497,7 @@ namespace Microsoft.AspNet.Mvc.Test var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); var viewData = new ViewDataDictionary(metadataProvider, new ModelStateDictionary()); + var tempData = new TempDataDictionary(Mock.Of(), Mock.Of()); var bindingContext = new ActionBindingContext() { @@ -1487,6 +1512,7 @@ namespace Microsoft.AspNet.Mvc.Test BindingContext = bindingContext, MetadataProvider = metadataProvider, ViewData = viewData, + TempData = tempData, ObjectValidator = new DefaultObjectValidator(Mock.Of(), metadataProvider) }; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs index e676754cdc..9b77a73ebf 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerUnitTestabilityTests.cs @@ -35,6 +35,7 @@ namespace Microsoft.AspNet.Mvc Assert.NotNull(viewResult.ViewData); Assert.Same(model, viewResult.ViewData.Model); Assert.Same(controller.ViewData, viewResult.ViewData); + Assert.Same(controller.TempData, viewResult.TempData); if (model != null) { @@ -61,6 +62,7 @@ namespace Microsoft.AspNet.Mvc Assert.NotNull(viewResult.ViewData); Assert.Same(model, viewResult.ViewData.Model); Assert.Same(controller.ViewData, viewResult.ViewData); + Assert.Same(controller.TempData, viewResult.TempData); if (model != null) { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs index 8560032f4f..6f222a2037 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs @@ -281,6 +281,8 @@ namespace Microsoft.AspNet.Mvc.Core services .Setup(s => s.GetService(typeof(IScopedInstance))) .Returns(new MockScopedInstance()); + services.Setup(s => s.GetService(typeof(ITempDataDictionary))) + .Returns(new Mock().Object); return services.Object; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs index 3ec8c06df9..e315b59fd0 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultViewComponentActivatorTests.cs @@ -109,6 +109,7 @@ namespace Microsoft.AspNet.Mvc return new ViewContext(actionContext, Mock.Of(), new ViewDataDictionary(new EmptyModelMetadataProvider()), + null, TextWriter.Null); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs index 5599d053ec..3f70d7e464 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs @@ -881,6 +881,11 @@ Environment.NewLine; get { return _innerHelper.ViewData; } } + public ITempDataDictionary TempData + { + get { return _innerHelper.TempData; } + } + public IHtmlEncoder HtmlEncoder { get { return _innerHelper.HtmlEncoder; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs index 433bf546ed..ac9991a245 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultTemplatesUtilities.cs @@ -219,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.Rendering new HtmlEncoder(), new UrlEncoder(), new JavaScriptStringEncoder()); - var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, new StringWriter()); + var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, null, new StringWriter()); htmlHelper.Contextualize(viewContext); return htmlHelper; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/ViewContextTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/ViewContextTests.cs index 72cacee7af..f66b5d372f 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/ViewContextTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/ViewContextTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Mvc.Rendering public void SettingViewData_AlsoUpdatesViewBag() { // Arrange (eventually passing null to these consturctors will throw) - var context = new ViewContext(new ActionContext(null, null, null), view: null, viewData: null, writer: null); + var context = new ViewContext(new ActionContext(null, null, null), view: null, viewData: null, tempData: null, writer: null); var originalViewData = context.ViewData = new ViewDataDictionary(metadataProvider: new EmptyModelMetadataProvider()); var replacementViewData = new ViewDataDictionary(metadataProvider: new EmptyModelMetadataProvider()); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/SessionStateTempDataProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/SessionStateTempDataProviderTest.cs new file mode 100644 index 0000000000..f2b431cc8e --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/SessionStateTempDataProviderTest.cs @@ -0,0 +1,97 @@ +// 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 Microsoft.AspNet.Http; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class SessionStateTempDataProviderTest + { + [Fact] + public void Load_NullSession_ReturnsEmptyDictionary() + { + // Arrange + var testProvider = new SessionStateTempDataProvider(); + + // Act + var tempDataDictionary = testProvider.LoadTempData( + GetHttpContext(session: null, sessionEnabled: true)); + + // Assert + Assert.Empty(tempDataDictionary); + } + + [Fact] + public void Load_NonNullSession_NoSessionData_ReturnsEmptyDictionary() + { + // Arrange + var testProvider = new SessionStateTempDataProvider(); + + // Act + var tempDataDictionary = testProvider.LoadTempData( + GetHttpContext(Mock.Of())); + + // Assert + Assert.Empty(tempDataDictionary); + } + + [Fact] + public void Save_NullSession_NullDictionary_DoesNotThrow() + { + // Arrange + var testProvider = new SessionStateTempDataProvider(); + + // Act & Assert (does not throw) + testProvider.SaveTempData(GetHttpContext(session: null, sessionEnabled: false), null); + } + + [Fact] + public void Save_NullSession_EmptyDictionary_DoesNotThrow() + { + // Arrange + var testProvider = new SessionStateTempDataProvider(); + + // Act & Assert (does not throw) + testProvider.SaveTempData( + GetHttpContext(session: null, sessionEnabled: false), new Dictionary()); + } + + [Fact] + public void Save_NullSession_NonEmptyDictionary_Throws() + { + // Arrange + var testProvider = new SessionStateTempDataProvider(); + + // Act & Assert + Assert.Throws(() => + { + testProvider.SaveTempData( + GetHttpContext(session: null, sessionEnabled: false), + new Dictionary { { "foo", "bar" } } + ); + }); + } + + private HttpContext GetHttpContext(ISessionCollection session, bool sessionEnabled=true) + { + var httpContext = new Mock(); + if (session != null) + { + httpContext.Setup(h => h.Session).Returns(session); + } + else if (!sessionEnabled) + { + httpContext.Setup(h => h.Session).Throws(); + } + if (sessionEnabled) + { + httpContext.Setup(h => h.GetFeature()).Returns(Mock.Of()); + } + return httpContext.Object; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/TempDataDictionaryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/TempDataDictionaryTest.cs new file mode 100644 index 0000000000..3497d34138 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/TempDataDictionaryTest.cs @@ -0,0 +1,225 @@ +// 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.Collections.Generic; +using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class TempDataDictionaryTest + { + [Fact] + public void TempData_Load_CreatesEmptyDictionaryIfProviderReturnsNull() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + + // Act + tempData.Load(); + + // Assert + Assert.Empty(tempData); + } + + [Fact] + public void TempData_Save_RemovesKeysThatWereRead() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + tempData["Foo"] = "Foo"; + tempData["Bar"] = "Bar"; + + // Act + var value = tempData["Foo"]; + tempData.Save(); + + // Assert + Assert.False(tempData.ContainsKey("Foo")); + Assert.True(tempData.ContainsKey("Bar")); + } + + [Fact] + public void TempData_EnumeratingDictionary_MarksKeysForDeletion() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + tempData["Foo"] = "Foo"; + tempData["Bar"] = "Bar"; + + // Act + var enumerator = tempData.GetEnumerator(); + while (enumerator.MoveNext()) + { + var value = enumerator.Current; + } + tempData.Save(); + + // Assert + Assert.False(tempData.ContainsKey("Foo")); + Assert.False(tempData.ContainsKey("Bar")); + } + + [Fact] + public void TempData_TryGetValue_MarksKeyForDeletion() + { + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + object value; + tempData["Foo"] = "Foo"; + + // Act + tempData.TryGetValue("Foo", out value); + tempData.Save(); + + // Assert + Assert.False(tempData.ContainsKey("Foo")); + } + + [Fact] + public void TempData_Keep_RetainsAllKeysWhenSavingDictionary() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + tempData["Foo"] = "Foo"; + tempData["Bar"] = "Bar"; + + // Act + tempData.Keep(); + tempData.Save(); + + // Assert + Assert.True(tempData.ContainsKey("Foo")); + Assert.True(tempData.ContainsKey("Bar")); + } + + [Fact] + public void TempData_Keep_RetainsSpecificKeysWhenSavingDictionary() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + tempData["Foo"] = "Foo"; + tempData["Bar"] = "Bar"; + + // Act + var foo = tempData["Foo"]; + var bar = tempData["Bar"]; + tempData.Keep("Foo"); + tempData.Save(); + + // Assert + Assert.True(tempData.ContainsKey("Foo")); + Assert.False(tempData.ContainsKey("Bar")); + } + + [Fact] + public void TempData_Peek_DoesNotMarkKeyForDeletion() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + tempData["Bar"] = "barValue"; + + // Act + var value = tempData.Peek("bar"); + tempData.Save(); + + // Assert + Assert.Equal("barValue", value); + Assert.True(tempData.ContainsKey("Bar")); + } + + [Fact] + public void TempData_CompareIsOrdinalIgnoreCase() + { + // Arrange + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + var item = new object(); + + // Act + tempData["Foo"] = item; + var value = tempData["FOO"]; + + // Assert + Assert.Same(item, value); + } + + [Fact] + public void TempData_LoadAndSaveAreCaseInsensitive() + { + // Arrange + var data = new Dictionary(); + data["Foo"] = "Foo"; + data["Bar"] = "Bar"; + var provider = new TestTempDataProvider(data); + var tempData = new TempDataDictionary(GetHttpContextAccessor(), provider); + + // Act + tempData.Load(); + var value = tempData["FOO"]; + tempData.Save(); + + // Assert + Assert.False(tempData.ContainsKey("foo")); + Assert.True(tempData.ContainsKey("bar")); + } + + [Fact] + public void TempData_RemovalOfKeysAreCaseInsensitive() + { + var tempData = new TempDataDictionary(GetHttpContextAccessor(), new NullTempDataProvider()); + object fooValue; + tempData["Foo"] = "Foo"; + tempData["Bar"] = "Bar"; + + // Act + tempData.TryGetValue("foo", out fooValue); + var barValue = tempData["bar"]; + tempData.Save(); + + // Assert + Assert.False(tempData.ContainsKey("Foo")); + Assert.False(tempData.ContainsKey("Boo")); + } + + private class NullTempDataProvider : ITempDataProvider + { + public IDictionary LoadTempData([NotNull]HttpContext context) + { + return null; + } + + public void SaveTempData([NotNull]HttpContext context, IDictionary values) + { + } + } + + private class TestTempDataProvider : ITempDataProvider + { + private IDictionary _data; + + public TestTempDataProvider(IDictionary data) + { + _data = data; + } + + public IDictionary LoadTempData([NotNull]HttpContext context) + { + return _data; + } + + public void SaveTempData([NotNull]HttpContext context, IDictionary values) + { + } + } + + private static IHttpContextAccessor GetHttpContextAccessor() + { + var httpContext = new Mock(); + var httpContextAccessor = new Mock(); + httpContextAccessor.Setup(h => h.HttpContext).Returns(httpContext.Object); + return httpContextAccessor.Object; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs index e8def7c0f2..4c744801b8 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ContentViewComponentResultTest.cs @@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Mvc { var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); - var viewContext = new ViewContext(actionContext, view, viewData, TextWriter.Null); + var viewContext = new ViewContext(actionContext, view, viewData, null, TextWriter.Null); var writer = new StreamWriter(stream) { AutoFlush = true }; var viewComponentContext = new ViewComponentContext(typeof(object).GetTypeInfo(), viewContext, writer); return viewComponentContext; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs index 7f840dbd82..bc507d8f76 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/JsonViewComponentResultTest.cs @@ -92,7 +92,7 @@ namespace Microsoft.AspNet.Mvc { var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); - var viewContext = new ViewContext(actionContext, view, viewData, TextWriter.Null); + var viewContext = new ViewContext(actionContext, view, viewData, null, TextWriter.Null); var writer = new StreamWriter(stream) { AutoFlush = true }; var viewComponentContext = new ViewComponentContext(typeof(object).GetTypeInfo(), viewContext, writer); return viewComponentContext; diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs index 7064c95c51..a0b15a2c0e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/ViewViewComponentResultTest.cs @@ -300,7 +300,7 @@ namespace Microsoft.AspNet.Mvc private static ViewComponentContext GetViewComponentContext(IView view, ViewDataDictionary viewData) { var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); - var viewContext = new ViewContext(actionContext, view, viewData, TextWriter.Null); + var viewContext = new ViewContext(actionContext, view, viewData, null, TextWriter.Null); var viewComponentContext = new ViewComponentContext(typeof(object).GetTypeInfo(), viewContext, TextWriter.Null); return viewComponentContext; } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/TempDataTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/TempDataTest.cs new file mode 100644 index 0000000000..3e491685d1 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/TempDataTest.cs @@ -0,0 +1,41 @@ +// 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.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class TempDataTest + { + private readonly IServiceProvider _services = TestHelper.CreateServices("TempDataWebSite"); + private readonly Action _app = new TempDataWebSite.Startup().Configure; + + [Fact] + public async Task ViewRendersTempData() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var nameValueCollection = new List> + { + new KeyValuePair("value", "Foo"), + }; + var content = new FormUrlEncodedContent(nameValueCollection); + + // Act + var response = await client.PostAsync("http://localhost/Home/DisplayTempData", content); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("Foo", body); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index a5ec57df6e..7b0f1cc286 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -48,6 +48,7 @@ "RoutingWebSite": "1.0.0", "TagHelperSample.Web": "1.0.0", "TagHelpersWebSite": "1.0.0", + "TempDataWebSite": "1.0.0", "UrlHelperWebSite": "1.0.0", "ValidationWebSite": "1.0.0", "ValueProvidersWebSite": "1.0.0", diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageActivatorTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageActivatorTest.cs index e91a86b3b0..3c56982005 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageActivatorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageActivatorTest.cs @@ -43,6 +43,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = new ViewContext(actionContext, Mock.Of(), new ViewDataDictionary(new EmptyModelMetadataProvider()), + Mock.Of(), TextWriter.Null); // Act @@ -73,6 +74,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = new ViewContext(actionContext, Mock.Of(), new ViewDataDictionary(new EmptyModelMetadataProvider()), + Mock.Of(), TextWriter.Null); // Act and Assert @@ -114,6 +116,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, + Mock.Of(), TextWriter.Null); // Act @@ -151,6 +154,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, + Mock.Of(), TextWriter.Null); // Act @@ -185,6 +189,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, + Mock.Of(), TextWriter.Null); // Act diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateModelExpressionTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateModelExpressionTest.cs index ec75e99425..aca6a0db50 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateModelExpressionTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateModelExpressionTest.cs @@ -125,6 +125,7 @@ namespace Microsoft.AspNet.Mvc.Razor actionContext, view: Mock.Of(), viewData: viewData, + tempData: Mock.Of(), writer: new StringWriter()); } diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs index 7544500cdf..0b5ec18c18 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageCreateTagHelperTest.cs @@ -115,6 +115,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, + Mock.Of(), TextWriter.Null); return new TestRazorPage diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs index 7bb80fab87..bdecdf37db 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs @@ -816,6 +816,7 @@ namespace Microsoft.AspNet.Mvc.Razor actionContext, Mock.Of(), null, + Mock.Of(), writer); } diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs index f5d633fd67..3f99dc90fc 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs @@ -1023,6 +1023,7 @@ namespace Microsoft.AspNet.Mvc.Razor actionContext, view, new ViewDataDictionary(new EmptyModelMetadataProvider()), + Mock.Of(), new StringWriter()); } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/CacheTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/CacheTagHelperTest.cs index cbbb25963b..79f628ed39 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/CacheTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/CacheTagHelperTest.cs @@ -756,6 +756,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers return new ViewContext(actionContext, Mock.Of(), new ViewDataDictionary(new EmptyModelMetadataProvider()), + Mock.Of(), TextWriter.Null); } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/FormTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/FormTagHelperTest.cs index 15b031250e..006ed4a135 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/FormTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/FormTagHelperTest.cs @@ -441,6 +441,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers actionContext, Mock.Of(), new ViewDataDictionary(new TestModelMetadataProvider()), + Mock.Of(), TextWriter.Null); } } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs index 3e8e6a61e4..ef53483608 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/LinkTagHelperTest.cs @@ -371,7 +371,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); var metadataProvider = new EmptyModelMetadataProvider(); var viewData = new ViewDataDictionary(metadataProvider); - var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, TextWriter.Null); + var viewContext = new ViewContext( + actionContext, + Mock.Of(), + viewData, + Mock.Of(), + TextWriter.Null); return viewContext; } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs index 09fb496a73..ec5c2bcaa2 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ScriptTagHelperTest.cs @@ -462,7 +462,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); var metadataProvider = new EmptyModelMetadataProvider(); var viewData = new ViewDataDictionary(metadataProvider); - var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, TextWriter.Null); + var viewContext = new ViewContext( + actionContext, + Mock.Of(), + viewData, + Mock.Of(), + TextWriter.Null); return viewContext; } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/TestableHtmlGenerator.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/TestableHtmlGenerator.cs index b2ef5823c3..d763c0480f 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/TestableHtmlGenerator.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/TestableHtmlGenerator.cs @@ -59,7 +59,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers { Model = model, }; - var viewContext = new ViewContext(actionContext, Mock.Of(), viewData, TextWriter.Null); + var viewContext = new ViewContext( + actionContext, + Mock.Of(), + viewData, + Mock.Of(), + TextWriter.Null); return viewContext; } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs index 3344f0007e..e8a4e795e6 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationMessageTagHelperTest.cs @@ -305,6 +305,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers Mock.Of(), new ViewDataDictionary( new EmptyModelMetadataProvider()), + Mock.Of(), TextWriter.Null); } } diff --git a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs index 5ad599609b..5357e8cdd9 100644 --- a/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs +++ b/test/Microsoft.AspNet.Mvc.TagHelpers.Test/ValidationSummaryTagHelperTest.cs @@ -310,6 +310,7 @@ Parameter name: value", Mock.Of(), new ViewDataDictionary( new EmptyModelMetadataProvider()), + Mock.Of(), TextWriter.Null); } diff --git a/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs b/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs index 223207e7a7..90cfbebba2 100644 --- a/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs +++ b/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs @@ -24,7 +24,7 @@ namespace ModelBindingWebSite.Controllers { _activated = true; var viewData = new ViewDataDictionary(ViewData); - var context = new ViewContext(ActionContext, new TestView(), viewData, TextWriter.Null); + var context = new ViewContext(ActionContext, new TestView(), viewData, null, TextWriter.Null); ((ICanHasViewContext)PersonHelper).Contextualize(context); } diff --git a/test/WebSites/TempDataWebSite/Controllers/HomeController.cs b/test/WebSites/TempDataWebSite/Controllers/HomeController.cs new file mode 100644 index 0000000000..2149b7c257 --- /dev/null +++ b/test/WebSites/TempDataWebSite/Controllers/HomeController.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.Mvc; + +namespace TempDataWebSite.Controllers +{ + public class HomeController : Controller + { + public IActionResult Index() + { + return View(); + } + + public IActionResult DisplayTempData(string value) + { + TempData["key"] = value; + return View(); + } + } +} diff --git a/test/WebSites/TempDataWebSite/Startup.cs b/test/WebSites/TempDataWebSite/Startup.cs new file mode 100644 index 0000000000..0b7d87de25 --- /dev/null +++ b/test/WebSites/TempDataWebSite/Startup.cs @@ -0,0 +1,31 @@ +// 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 TempDataWebSite +{ + public class Startup + { + public void Configure(IApplicationBuilder app) + { + var configuration = app.GetTestConfiguration(); + + app.UseServices(services => + { + services.AddCachingServices(); + services.AddSessionServices(); + services.AddMvc(configuration); + }); + + app.UseInMemorySession(); + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} diff --git a/test/WebSites/TempDataWebSite/TempDataWebSite.kproj b/test/WebSites/TempDataWebSite/TempDataWebSite.kproj new file mode 100644 index 0000000000..072af3b4ee --- /dev/null +++ b/test/WebSites/TempDataWebSite/TempDataWebSite.kproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 8aeb631e-ab74-4d2e-83fb-8931ee10d9d3 + TempDataWebSite + ..\..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 28690 + + + \ No newline at end of file diff --git a/test/WebSites/TempDataWebSite/Views/Home/DisplayTempData.cshtml b/test/WebSites/TempDataWebSite/Views/Home/DisplayTempData.cshtml new file mode 100644 index 0000000000..6b0b25a039 --- /dev/null +++ b/test/WebSites/TempDataWebSite/Views/Home/DisplayTempData.cshtml @@ -0,0 +1 @@ +@TempData["key"] \ No newline at end of file diff --git a/test/WebSites/TempDataWebSite/Views/Home/Index.cshtml b/test/WebSites/TempDataWebSite/Views/Home/Index.cshtml new file mode 100644 index 0000000000..173fcf9417 --- /dev/null +++ b/test/WebSites/TempDataWebSite/Views/Home/Index.cshtml @@ -0,0 +1,11 @@ + + + + + + Hello + + + Hello + + \ No newline at end of file diff --git a/test/WebSites/TempDataWebSite/project.json b/test/WebSites/TempDataWebSite/project.json new file mode 100644 index 0000000000..73a045d0df --- /dev/null +++ b/test/WebSites/TempDataWebSite/project.json @@ -0,0 +1,19 @@ +{ + "commands": { + "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001", + "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000" + }, + "dependencies": { + "Kestrel": "1.0.0-*", + "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0", + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Session": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + }, + "webroot": "wwwroot" +} diff --git a/test/WebSites/TempDataWebSite/readme.md b/test/WebSites/TempDataWebSite/readme.md new file mode 100644 index 0000000000..b1748ea84b --- /dev/null +++ b/test/WebSites/TempDataWebSite/readme.md @@ -0,0 +1,4 @@ +TempDataWebSite +=== + +This web site illustrates use cases of TempData. diff --git a/test/WebSites/TempDataWebSite/wwwroot/HelloWorld.htm b/test/WebSites/TempDataWebSite/wwwroot/HelloWorld.htm new file mode 100644 index 0000000000..3da1ec26e9 --- /dev/null +++ b/test/WebSites/TempDataWebSite/wwwroot/HelloWorld.htm @@ -0,0 +1 @@ +HelloWorld