我不得不处理我的项目的一些奇怪的情况,所以我会尝试在这里概述,也许有人可以提供一个更好的方法来做到这一点。
我正在设计像Google iGoogle页面。具有多个小部件的主页面,可以移动或根据需要进行配置。当前系统将实际的小部件的数据异步地查看POST到我的应用程序中的控制器。该控制器将呈现可以返回(然后加载到页面视图JQUERY)的HTML的部分视图,或仅存储在数据库中的直接HTML / JavaScript。
这对我来说工作正常,我有一个模型,其中包含通过数据库描述的选项的字典,然后由部分视图使用。当我想将数据传递给部分视图时,问题出现了。我可以想出的最好的解决办法是让控制器确定所使用的部分视图中的哪个模型,具有填充模型的一些功能,然后将其与局部视图一起传递到将使其变为控制器内的HTML。
我意识到这是一个奇怪的场景,MVC(层是混合…)和任何关于基础设计的建议,或执行这将非常感谢。
我目前使用的是MVC3 / Razor。随时问任何其他问题。
解决方法
楷模
一,模型。我决定创建两个“小工具”,一个是新闻,一个是时钟。
public class NewsModel { public string[] Headlines { get; set; } public NewsModel(params string[] headlines) { Headlines = headlines; } } public class ClockModel { public DateTime Now { get; set; } public ClockModel(DateTime now) { Now = now; } }
调节器
我的控制器不了解任何意见。它所做的是返回单个模型,但是该模型可以根据视图的要求动态获取正确的模型。
public ActionResult Show(string widgetName) { var selector = new ModelSelector(); selector.WhenRendering<ClockModel>(() => new ClockModel(DateTime.Now)); selector.WhenRendering<NewsModel>(() => new NewsModel("Headline 1","Headline 2","Headline 3")); return PartialView(widgetName,selector); }
使用代表,以便仅在实际使用正确的模型时创建/提取正确的模型。
ModelSelector
控制器使用的ModelSelector非常简单 – 只需保留一个代理包来创建每个模型类型:
public class ModelSelector { private readonly Dictionary<Type,Func<object>> modelLookup = new Dictionary<Type,Func<object>>(); public void WhenRendering<T>(Func<object> getter) { modelLookup.Add(typeof(T),getter); } public object GetModel(Type modelType) { if (!modelLookup.ContainsKey(modelType)) { throw new KeyNotFoundException(string.Format("A provider for the model type '{0}' was not provided",modelType.FullName)); } return modelLookup[modelType](); } }
视图 – 简单的解决方案
现在,实现视图的最简单方法是:
@model MvcApplication2.ModelSelector @using MvcApplication2.Models @{ var clock = (ClockModel) Model.GetModel(typeof (ClockModel)); } <h2>The time is: @clock.Now</h2>
你可以在这里结束并使用这种方法。
意见 – 更好的解决方案
这很丑陋我希望我的意见如下:
@model MvcApplication2.Models.ClockModel <h2>Clock</h2> @Model.Now
和
@model MvcApplication2.Models.NewsModel <h2>News Widget</h2> @foreach (var headline in Model.Headlines) { <h3>@headline</h3> }
为了使这项工作,我不得不创建一个自定义视图引擎。
自定义视图引擎
当Razor视图被编译时,它会继承ViewPage< T>,其中T是@model。所以我们可以使用反射来确定视图想要什么类型,然后选择它。
public class ModelSelectorEnabledRazorViewEngine : RazorViewEngine { protected override IView CreateView(ControllerContext controllerContext,string viewPath,string masterPath) { var result = base.CreateView(controllerContext,viewPath,masterPath); if (result == null) return null; return new CustomRazorView((RazorView) result); } protected override IView CreatePartialView(ControllerContext controllerContext,string partialPath) { var result = base.CreatePartialView(controllerContext,partialPath); if (result == null) return null; return new CustomRazorView((RazorView)result); } public class CustomRazorView : IView { private readonly RazorView view; public CustomRazorView(RazorView view) { this.view = view; } public void Render(ViewContext viewContext,TextWriter writer) { var modelSelector = viewContext.ViewData.Model as ModelSelector; if (modelSelector == null) { // This is not a widget,so fall back to stock-standard MVC/Razor rendering view.Render(viewContext,writer); return; } // We need to work out what @model is on the view,so that we can pass the correct model to it. // We can do this by using reflection over the compiled views,since Razor views implement a // ViewPage<T>,where T is the @model value. var compiledViewType = BuildManager.GetCompiledType(view.ViewPath); var baseType = compiledViewType.BaseType; if (baseType == null || !baseType.IsGenericType) { throw new Exception(string.Format("When the view '{0}' was compiled,the resulting type was '{1}',with base type '{2}'. I expected a base type with a single generic argument; I don't know how to handle this type.",view.ViewPath,compiledViewType,baseType)); } // This will be the value of @model var modelType = baseType.GetGenericArguments()[0]; if (modelType == typeof(object)) { // When no @model is set,the result is a ViewPage<object> throw new Exception(string.Format("The view '{0}' needs to include the @model directive to specify the model type. Did you forget to include an @model line?",view.ViewPath)); } var model = modelSelector.GetModel(modelType); // Switch the current model from the ModelSelector to the value of @model viewContext.ViewData.Model = model; view.Render(viewContext,writer); } } }
视图引擎注册在Global.asax.cs中:
ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new ModelSelectorEnabledRazorViewEngine());
渲染
我的家庭视图包括以下几行来测试它:
@Html.Action("Show","Widget",new { widgetName = "Clock" }) @Html.Action("Show",new { widgetName = "News" })