有效地避免了ASP中的ViewBag.净MVC



您如何处理避免ViewBag由于其错误的风险是动态的,但也避免不得不填充一个新的ViewModel,并把它传回给视图每次。例如,我不想为了暴露通常塞在ViewBag中的公共数据而改变跟随。

[HttpGet]
void Index() 
{ 
    return View(); 
}

[HttpGet]
void Index() 
{
    var messages = new MessageCollection();
    messages.AddError("Uh oh!");
    return View(messages);
}

在管道中,我将添加一个像ViewBag这样的属性,它是自定义的和强类型的,但它在控制器和视图中优雅地暴露。我宁愿这样做,当我不需要一个特定的ViewModel所有的时间…

[HttpGet]
void Index()
{
    Messages.AddError("Uh oh!");
    return View();
}

在视图侧,而不是@((IMessageCollection)ViewBag.Messages)。我宁愿使用@Messages之类的东西。错误是强类型的,在任何地方都可以使用。而且,我不想在Razor视图顶部的代码块中强制转换它。

在WebForms中,我会做一些事情,比如把它放在一个基本页面,然后有一个用户控件,可以根据需要在页面上隐藏或显示。随着控制器与视图解耦,我不确定如何复制类似的行为。

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

谢谢,斯科特

Razor视图相当简单。您与单个模型交互,该模型是强类型的。因此,视图中需要强类型的任何内容都需要在模型中。如果您的模型中有您不想要的东西,或者这是一次性的,那么ViewBag是作为所有非模型数据的通用捕获器提供的,这就是为什么它是动态的。强类型会限制它成为包罗万象的能力。

简短:如果你想要强类型的添加消息到你的视图模型。否则,坚持使用ViewBag

我同意Chris的回答,我个人会把它扔进viewbag。

但是,从技术上讲,你可以改变规则…

编辑:现在考虑一下,你可以用ViewBag替换HttpContext.Items,这样你在技术上仍然使用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();
}

或者你可以有一个带有快捷getter的BaseController…例如

protected MessageCollection Messages { get { return MessageCollection.Current; } }

然后在你的控制器中继承它

[HttpGet]
public ActionResult Index()
{
    Messages.AddError("Uh oh!");
    return View();
}

要在您的视图中获得它,只需更改您的web。你可能需要在几个地方这样做(即你的主web。配置,查看目录web。配置和区域视图目录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>

. NET MVC,您可以使用ViewBag, ViewDataTempData(有关更多信息,请参阅此博客文章)。ViewBagViewData字典的动态包装器。如果您执行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完全相同。但是我把它留给你——我认为不值得。

相关内容

  • 没有找到相关文章

最新更新