你如何处理以避免ViewBag由于其错误的风险与富有活力,也避免填充一个新视图模型,每一次传回的看法。 举例来说,我不想一定改变后续暴露通常ViewBag酿通用数据。
[HttpGet]
void Index()
{
return View();
}
至
[HttpGet]
void Index()
{
var messages = new MessageCollection();
messages.AddError("Uh oh!");
return View(messages);
}
凡在管道我想补充像ViewBag属性是自定义的和强类型的,但有它在控制器优雅暴露,也是观。 我宁愿做时,我并不需要一个特定的视图模型所有的时间...
[HttpGet]
void Index()
{
Messages.AddError("Uh oh!");
return View();
}
而在观察侧,而不是@((IMessageCollection)ViewBag.Messages).Errors ID,而有一些像@ Messages.Errors是强类型和随处可见。 另外,我不想只投它一个代码块在我的Razor视图的顶部。
在WebForms的,我会做类似的东西就把这个基页,然后有一个可以隐藏,或根据需要在网页上显示一个用户控件。 从视图分离的控制器,我不知道如何复制类似的行为。
这是可能的,或者什么是最好的设计方法?
谢谢,斯科特
剃刀意见是相当简单的。 你有一个单一的模式,这是强类型的交互。 要在您的视图强类型的话,那么,需要在您的模型。 如果您有什么你不希望你的模型,或者是一次性的,那么ViewBag
是作为一个普通的包罗万象的所有非模型数据,这就是为什么它是一个动态的。 是强类型将限制它的是一个包罗万象的能力。
短而简单:如果你想强类型添加邮件到您的视图模型。 否则,坚持ViewBag
。 那是你的选择。
我同意克里斯的答案,我个人把它扔到viewbag。
但是为了倡导鬼子,从技术上讲,你可以通融一下...
编辑:只是想着现在,你很可能取代HttpContext.Items
下面ViewBag
,让你在技术上仍在使用ViewBag存储,但只是增加一个包装给它温暖安全的强类型的感觉。
例如,你可以有这样的事情:
namespace Your.Namespace
{
public class MessageCollection : IMessageCollection
{
public IList<string> Errors { get; protected set; }
protected MessageCollection()
{
//Initialization stuff here
Errors = new List<string>();
}
private const string HttpContextKey = "__MessageCollection";
public static MessageCollection Current
{
get
{
var httpContext = HttpContext.Current;
if (httpContext == null) throw new InvalidOperationException("MessageCollection must be used in the context of a web application.");
if (httpContext.Items[HttpContextKey] == null)
{
httpContext.Items[HttpContextKey] = new MessageCollection();
}
return httpContext.Items[HttpContextKey] as MessageCollection;
}
}
}
}
然后,只需把它在你的控制器是这样的:
[HttpGet]
public ActionResult Index()
{
MessageCollection.Current.AddError("Uh oh!");
return View();
}
或者你可以有一个快捷方式的BaseController吸气......如
protected MessageCollection Messages { get { return MessageCollection.Current; } }
然后在你的控制器不是从它继承
[HttpGet]
public ActionResult Index()
{
Messages.AddError("Uh oh!");
return View();
}
为了得到它在你看来,简单地改变你的web.config(你可能需要做这在一些地方(即你的主web.config文件,查看目录的web.config和区域的观点目录的web.config)
<system.web.webPages.razor>
<!-- blah -->
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<!-- blah -->
<add namespace="Your.Namespace" />
</namespaces>
</pages>
</system.web.webPages.razor>
然后在你的意见,你应该能够做到:
<div class="messages">
@foreach (var error in MessageCollection.Current.Errors)
{
<span>@error</span>
}
</div>
在ASP.NET MVC,您在您的处置ViewBag
, ViewData
,和TempData
(有关更多信息,请参见这篇博客文章 )。 该ViewBag
是围绕一个动态包装ViewData
字典。 如果你ViewBag.Prop = "value"
就等同于ViewData["Prop"] = "value"
。 当您使用Model
视图中的属性,你检索ViewData.Model
。 看一下吧:
public abstract class WebViewPage<TModel> : WebViewPage
{
private ViewDataDictionary<TModel> _viewData;
public new AjaxHelper<TModel> Ajax { get; set; }
public new HtmlHelper<TModel> Html { get; set; }
public new TModel Model { get { return ViewData.Model; } }
}
我们可以实现通过使用您的最终ViewBag
或ViewData
握住你的特殊属性。 第一步是创建一个自定义的推导WebViewPage<TModel>
你想要的属性:
public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel>
{
public IList<string> Messages
{
get { return ViewBag.Messages ?? (ViewBag.Messages = new List<string>()); }
}
}
现在去你的看法,并更换线路@model YourModelClass
(第一行),有以下:
@inherits CustomWebViewPage<YourModelClass>
您现在可以使用的Messages
在您的视图属性。
@String.Join(", ", Messages)
要在控制器上使用它,你可能会想从派生Controller
和添加属性那里。
public abstract class CustomControllerBase : Controller
{
public IList<string> Messages
{
get
{
return ViewBag.Messages ?? (ViewBag.Messages = new List<string>());
}
}
}
现在,如果从控制器获得,你可以用你的新属性。 你把东西名单也将提供给您的视图。
public class ExampleController : CustomControllerBase
{
public ActionResult Index()
{
Messages.Add("This is a message");
return View();
}
}
我用ViewBag,因为它使属性getter短。 你可以做同样的事情ViewData
,如果你喜欢( ViewData["Messages"]
这是不太一样的是如何Model
的实现,因为意外,如果他们碰巧使用你一键节能别人可以覆盖你的财产,但它是足够接近的功能等效,如果你只需要确保使用唯一的密钥。
如果你深入挖掘,您可以从导出ViewDataDictionary
,并把你的财产在那里,然后覆盖某些控制器和视图的方法,而不是使用它。 然后,你的财产将是完全一样的Model
。 但我把它留给你 - 我不认为这是值得的。