MVC 操作共享布局





4.00/5 (1投票)
问题:你是否曾经需要一种静态的方式来在“回发”、“Get”或“Post”请求中持久化数据,而无需创建稍后需要清理的静态引用?你是否曾经需要一种清晰且定义明确的方式(非 ViewBag 版本)来修改 MVC 中的布局?
问题所在
你是否曾经需要一种静态的方式来在“回发”、“Get”或“Post”请求中持久化数据,而无需创建稍后需要清理的静态引用?
你是否曾经需要一种清晰且定义明确的方式(非 ViewBag 版本)来修改 MVC 中的布局,而无需创建成百上千个不同的布局文件?
我目前正在项目中遇到这种情况。问题出现时,我们意识到我们正在开发的应用程序需要在布局上拥有许多自定义功能,并且还有许多尚未定义的功能,并且预计产品上线后会有更多请求。因此,在研究了很长时间后,我开发了一种我认为定义良好且可扩展的方法。
解决方案
这里有一种实现方法。
首先,我在我们的站点核心库中创建了一个名为 SharedLayoutModel 的模型。这个类将包含以下项目,这些项目将在我们基础控制器中的每个 override ViewResult 中初始化。
- 第一、第二和第三层导航
- 初始化布局模型时使用的当前 RouteData
- 我们当前所在的导航项(数据库存储)
- 以及窗口的标题。
这个例子中还有许多其他中间层代码,许多扩展方法以及实体类型上存储库模式的使用(如果需要,稍后可以讨论),但我在整个理念中最重要的部分是第一个静态实例,名为 Instance。
这个家伙的作用是持有当前 HttpContext.Items 容器中的定义泛型类型,并允许在完成请求后从 HttpContext 中释放该对象。这样我们就无需担心我们的(原本可能是)单例实例是否被正确释放。IIS 会为我们处理它。
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
{
/// <summary>
/// The shared offers a singleton object representation of shared properties across all layouts.
/// </summary>
/// <remarks></remarks>
[Serializable]
public sealed class SharedLayoutModel
{
/// <summary>
///
/// </summary>
public static ContextInstance<SharedLayoutModel> Instance = new ContextInstance<SharedLayoutModel>("SharedLayoutModel_instance");
#regionRequired Assignments on Initialize
/// <summary>
/// Set's the Nav Item Module Type for this Layout.
/// </summary>
/// <value>The type of the nav item module.</value>
/// <remarks>Set in the Initialize method</remarks>
public NavItemModuleTypeNavItemModuleType { get; set; }
/// <summary>
/// Set's the current Route Data for this Layout.
/// </summary>
/// <value>The route data.</value>
/// <remarks>Set in the Initialize method</remarks>
public RouteDataRouteData { get; set; }
#endregion
/// <summary>
///
/// </summary>
private HeadTitle_Title;
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
/// <remarks></remarks>
public HeadTitleTitle
{
get
{
if (_Title == null)
{
_Title = new HeadTitle();
}
return _Title;
}
set
{
_Title = value;
}
}
#regionNavigation
/// <summary>
/// First Tier Navigation Menu
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
public IQueryable<NavItem> NavigationFirstTier()
{
return NavigationUtility.Instance.Get.GetFirstTierNavigationthis.NavItemModuleType, this.RouteData);
}
/// <summary>
/// Second Tier Navigation Menu
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
public IQueryable<NavItem> NavigationSecondTier()
{
return NavigationUtility.Instance.Get.GetSecondTierNavigation(this.NavItemModuleType, this.RouteData);
}
/// <summary>
/// Third Tier Navigation Menu
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
public IQueryable<NavItem> NavigationThirdTier()
{
return NavigationUtility.Instance.Get.GetThirdTierNavigation(this.NavItemModuleType, this.RouteData);
}
/// <summary>
/// Returns the best suited Nav Item for the current Route Data provided.
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
public NavItemCurrentNavItem()
{
string controller = RouteData.GetController();
string action = RouteData.GetAction();
//GetControllerAction(out controller, out action);
Imfaqncodn.Core.DA.StoreRepository<NavItem> repository = newDA.StoreRepository<NavItem>();
NavItem navItem = repository.Find(n => n.Controller == controller && n.Action == action);
return navItem;
}
#endregion
/// <summary>
/// Initialized on each start of an Actions ViewResult.
/// </summary>
/// <param name="routeData">The route data.</param>
/// <param name="navItemModuleType">Defaults to UNKNOWN</param>
/// <remarks></remarks>
public voidInitialize(RouteData routeData, NavItemModuleType navItemModuleType)
{
//clear out other data here...
this.RouteData = routeData;
this.NavItemModuleType = navItemModuleType;
}
}
/// <summary>
///
/// </summary>
/// <remarks></remarks>
public sealed class HeadTitle
{
/// <summary>
/// Gets the runtime environment.
/// </summary>
/// <remarks></remarks>
private stringRuntimeEnvironment
{
get
{
return EnvironmentUtility.RuntimeEnvironment != EnvironmentType.PRODUCTION
? EnvironmentUtility.RuntimeEnvironment.ToString()
: string.Empty;
}
}
/// <summary>
/// Gets or sets the window title.
/// </summary>
/// <value>The window title.</value>
/// <remarks></remarks>
public stringWindowTitle { get; set; }
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
/// <remarks></remarks>
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
{
/// <summary>
/// Creates a member instance of this Generic Type for the duration of an HttpContext.
/// </summary>
/// <example>
/// <![CDATA[
/// [Serializable]
/// public class SharedLayoutModel
/// {
/// public static ContextInstance<SharedLayoutModel> Instance = new ContextInstance<SharedLayoutModel>("SharedLayoutModel_instance");
/// }]]>
/// </example>
/// <typeparam name="T"></typeparam>
/// <remarks></remarks>
public sealed class ContextInstance<T> where T : class, new()
{
/// <summary>
///
/// </summary>
public readonly string ContextKey;
/// <summary>
/// Initializes a new instance of the <see cref="ContextInstance<T>"/> class.
/// </summary>
/// <param name="ContextKey">The context key.</param>
/// <remarks></remarks>
public ContextInstance(stringContextKey)
{
this.ContextKey = ContextKey;
}
/// <summary>
/// Gets the instance.
/// </summary>
/// <value>The get.</value>
/// <remarks></remarks>
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);
}
}
}
}
这里是 HttpContextUtility 代码的一个例子
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)
{
//Handle Exception using BusinessLogicExceptionHandler, rethrow by policy, otherwise handle within catch as desired
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);
}
cshtml 文件示例
<title> @SharedLayoutModel.Instance.Get.Title.ToString()</title>
_layout.cshtml
<head> @Html.Partial("_Head")
@RenderSection("HeadTagAdditions", required: false)
</head>
<body>
<!-- container -->
<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>
控制器代码示例
void ControllerMethod(stringtitleAddition){
SharedLayoutModel.Instance.Get.Title.WindowTitle = titleAddition;
}
就这样了!希望这能让你的布局管理起来更容易一些,并且更加清晰。;)