gpt4 book ai didi

asp.net-mvc - 从 MVC 中的 Controller 确定局部 View 的模型

转载 作者:行者123 更新时间:2023-12-03 13:30:36 25 4
gpt4 key购买 nike

我目前的问题是我有一个部分 View ,我想确定它正在使用什么模型。

我不得不为我的项目处理一些奇怪的场景,所以我将尝试在这里概述它,也许有人可以提供更好的方法来做到这一点。

我正在设计类似 Google iGoogle 页面的东西。具有多个小部件的主页,这些小部件可以移动或根据需要进行配置。当前系统异步加载实际小部件的数据,查看我的应用程序中的 Controller 的 POST。该 Controller 要么将部分 View 呈现为可以返回的 HTML(然后加载到 JQUERY 页面 View 中),要么只呈现存储在数据库中的直接 HTML/JavaScript。

这对我来说很好用,我有一个小部件模型,其中包含通过数据库描述的选项字典,然后由局部 View 使用。当我想将数据传递给局部 View 时,问题就来了。我能想出的最佳解决方案是让 Controller 确定所讨论的局部 View 使用哪个模型,具有一些填充模型的函数,然后将它与局部 View 一起传递给将其呈现给的函数 Controller 中的 HTML。

我意识到这对于 MVC 来说是一个奇怪的场景(层正在混合......),任何关于基本设计或实现的建议将不胜感激。

我目前正在使用 MVC3/Razor。随意问任何其他问题。

最佳答案

我为此设计了一个可能的解决方案原型(prototype),因为它看起来是一个有趣的问题。我希望它对你有用。

楷模

首先,模型。我决定创建两个“小部件”,一个用于新闻,一个用于时钟。

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;
}
}

Controller

我的 Controller 对 View 一无所知。它所做的是返回单个模型,但该模型能够根据 View 的要求动态获取正确的模型。
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);
}

使用委托(delegate)以便仅在实际使用时创建/获取正确的模型。

模型选择器

Controller 使用的 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]();
}
}

View - 简单的解决方案

现在,实现 View 的最简单方法是:
@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>
}

为了完成这项工作,我必须创建一个自定义 View 引擎。

自定义 View 引擎

编译 Razor View 时,它会继承 ViewPage<T> , 其中 T@model .所以我们可以使用反射来确定 View 想要什么类型,然后选择它。
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 来注册 View 引擎:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ModelSelectorEnabledRazorViewEngine());

渲染

我的主页 View 包括以下几行来测试它:
@Html.Action("Show", "Widget", new { widgetName = "Clock" })
@Html.Action("Show", "Widget", new { widgetName = "News" })

关于asp.net-mvc - 从 MVC 中的 Controller 确定局部 View 的模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4922339/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com