The problem
Have you ever needed a static way of persisting items throughout a "Post-Back" or a "Get" or "Post" without creating a static reference that you'll have to clean up later?
Have you ever needed a clean and defined way (non ViewBag version) of modifying a layout in MVC without having to create dozens and dozens of them?
This is something that I'm currently finding opportunities in right now. The issue arose when we realized that the application we are working on needs to have many customized features on the layout and many more that just aren't defined yet, knowing there'll be more requests once the product goes live. So I after researching and researching I developed a method I thought was pretty well defined and also extendable.
The solution
Here's one way to do that.
First I created a Model called the SharedLayoutModel in our sites Core library. This class will contain the following items that are initialized on every override ViewResult in our base controller:
- First, Second and Third Tier Navigation
- The current RouteData used when initalizing the Layout Model
- The current Navigation item we're on (database stored)
- and a Title for the window.
There's many other middle-tier code in this example, many extension methods and the use of a Repository Pattern on entity types (can be discussed at a later time if needed) but the main piece in this whole philosophy I came up with is that first static instance called Instance .
What that guy does is holds the defined generic type in the current HttpContext.Items container and allows the end of the http request to dispose of the object when we're finished from the HttpContext. This way we don't even have to worry that our (what could've been) singleton instance is disposed of properly. IIS will take care of it for us.
SharedLayoutModel.cs
using System;
using System.Linq;
using System.Web.Routing;
using Imfaqncodn.Core.Arch;
using Imfaqncodn.Core.Extensions;
using Imfaqncodn.Core.Utility;
namespace Imfaqncodn.Core.Web.UI.Models.Views.Shared
{
[Serializable]
public sealed class SharedLayoutModel
{
public static ContextInstance<SharedLayoutModel> Instance = new ContextInstance<SharedLayoutModel>("SharedLayoutModel_instance");
#regionRequired Assignments on Initialize
public NavItemModuleTypeNavItemModuleType { get; set; }
public RouteDataRouteData { get; set; }
#endregion
private HeadTitle_Title;
public HeadTitleTitle
{
get
{
if (_Title == null)
{
_Title = new HeadTitle();
}
return _Title;
}
set
{
_Title = value;
}
}
#regionNavigation
public IQueryable<NavItem> NavigationFirstTier()
{
return NavigationUtility.Instance.Get.GetFirstTierNavigationthis.NavItemModuleType, this.RouteData);
}
public IQueryable<NavItem> NavigationSecondTier()
{
return NavigationUtility.Instance.Get.GetSecondTierNavigation(this.NavItemModuleType, this.RouteData);
}
public IQueryable<NavItem> NavigationThirdTier()
{
return NavigationUtility.Instance.Get.GetThirdTierNavigation(this.NavItemModuleType, this.RouteData);
}
public NavItemCurrentNavItem()
{
string controller = RouteData.GetController();
string action = RouteData.GetAction();
Imfaqncodn.Core.DA.StoreRepository<NavItem> repository = newDA.StoreRepository<NavItem>();
NavItem navItem = repository.Find(n => n.Controller == controller && n.Action == action);
return navItem;
}
#endregion
public voidInitialize(RouteData routeData, NavItemModuleType navItemModuleType)
{
this.RouteData = routeData;
this.NavItemModuleType = navItemModuleType;
}
}
public sealed class HeadTitle
{
private stringRuntimeEnvironment
{
get
{
return EnvironmentUtility.RuntimeEnvironment != EnvironmentType.PRODUCTION
? EnvironmentUtility.RuntimeEnvironment.ToString()
: string.Empty;
}
}
public stringWindowTitle { get; set; }
public override string ToString()
{
string title = string.Empty;
if (!string.IsNullOrEmpty(RuntimeEnvironment))
{
title += "[" + RuntimeEnvironment + "] ";
}
string Controller = HttpContextUtility.GetRouteData().GetController();
string Action = HttpContextUtility.GetRouteData().GetAction();
title += Controller;
if (!Action.ToLower().In("index", "default"))
{
title += " " + Action;
}
return title;
}
}
}
ContextInstance.cs
using System;
usingImfaqncodn.Core.Utility;
namespace Imfaqncodn.Core.Arch
{
public sealed class ContextInstance<T> where T : class, new()
{
public readonly string ContextKey;
public ContextInstance(stringContextKey)
{
this.ContextKey = ContextKey;
}
public T Get
{
get
{
T _instance = HttpContextUtility.GetItem<T>(ContextKey);
if (_instance == null)
{
_instance = new T();
}
HttpContextUtility.AddItem(ContextKey, _instance);
return _instance;
}
set
{
HttpContextUtility.AddItem(ContextKey, value);
}
}
}
}
Here's an example of the HttpContextUtility code
HttpContextUtility.cs
public static HttpContext GetCurrentHttpContext()
{
return HttpContext.Current;
}
public static HttpRequest GetCurrentHttpRequest()
{
HttpRequest hr = null;
if (GetCurrentHttpContext() != null)
return HttpContext.Current.Request;
return hr;
}
public static void AddItem(objectkey, object value)
{
HttpContext current = GetCurrentHttpContext();
try
{
if (current != null&& current.Items != null)
{
if (current.Items.Contains(key))
{
current.Items.Remove(key);
}
current.Items.Add(key, value);
}
else
{
throw newException("Current Context Add Items is not available.");
}
}
catch(Exceptionex)
{
BusinessLogicExceptionHandler.HandleException(ref ex);
}
}
public static object GetItem(objectkey)
{
HttpContext current = GetCurrentHttpContext();
return (current != null && current.Items != null) ? current.Items[key] : null;
}
public static T GetItem<T>(object key) where T : class, new()
{
HttpContext current = GetCurrentHttpContext();
return (current != null && current.Items != null) ? current.Items[key] as T : null;
}
public static S GetItemAsValueType<S>(object key) where S : struct
{
HttpContext current = GetCurrentHttpContext();
return (current != null && current.Items != null) ? (S)current.Items[key] : default(S);
}
Example of cshtml files
<title> @SharedLayoutModel.Instance.Get.Title.ToString()</title>
_layout.cshtml
<head> @Html.Partial("_Head")
@RenderSection("HeadTagAdditions", required: false)
</head>
<body>
<!--
<div class="container root">
<div class="container top">
<div class="row">
<header class="three columns push-nine">
<h1 class="@SharedLayoutModel.Instance.Get.NavItemModuleType.GetFlyCssIconCode()">@LayoutHelpers.GetCurrentNavItemTerm()</h1>
<form action="@Url.Action("Search", "Orders")"id="frmsrch"method="post">
<p>
<input type="search" name="glbsterm" id="glbsterm" placeholder="Global Search">
<button type="submit">Search</button>
</p>
</form>
</header>
</div>
</div>
</div>
</body>
Example Controller Code
void ControllerMethod(stringtitleAddition){
SharedLayoutModel.Instance.Get.Title.WindowTitle = titleAddition;
}
And that's it! Hope this makes your layouts a little easier to manage with some definition. ;)