有效地避免了ViewBag在ASP.NET MVC(Effectively avoiding Vie

2019-08-17 22:12发布

你如何处理以避免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的,我会做类似的东西就把这个基页,然后有一个可以隐藏,或根据需要在网页上显示一个用户控件。 从视图分离的控制器,我不知道如何复制类似的行为。

这是可能的,或者什么是最好的设计方法?

谢谢,斯科特

Answer 1:

剃刀意见是相当简单的。 你有一个单一的模式,这是强类型的交互。 要在您的视图强类型的话,那么,需要在您的模型。 如果您有什么你不希望你的模型,或者是一次性的,那么ViewBag是作为一个普通的包罗万象的所有非模型数据,这就是为什么它是一个动态的。 是强类型将限制它的是一个包罗万象的能力。

短而简单:如果你想强类型添加邮件到您的视图模型。 否则,坚持ViewBag 。 那是你的选择。



Answer 2:

我同意克里斯的答案,我个人把它扔到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>


Answer 3:

在ASP.NET MVC,您在您的处置ViewBagViewData ,和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; } }
}

我们可以实现通过使用您的最终ViewBagViewData握住你的特殊属性。 第一步是创建一个自定义的推导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 。 但我把它留给你 - 我不认为这是值得的。



文章来源: Effectively avoiding ViewBag in ASP.NET MVC