2013-03-22 87 views
5

如何避免ViewBag由於其動態錯誤的風險,而且避免每次都填充新的ViewModel並將其傳遞迴視圖。例如,我不想一定要改變後面的內容來暴露通常填充在ViewBag中的常見數據。有效地避免ASP.NET MVC中的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中,我會做一些類似於把這個基本頁面,然後有一個usercontrol,可以隱藏或顯示在頁面上根據需要。隨着控制器與視圖分離,我不知道如何複製類似的行爲。

這是可能的或者什麼是最好的設計方法?

感謝, 斯科特

+0

你真的有2個選擇:ViewBag和在你的模型中的專用屬性 – 2013-03-22 20:58:21

回答

8

剃刀意見是相當簡單的。您可以使用強類型的單一模型進行交互。然後,您需要在您的視圖中強烈鍵入任何您需要的模型。如果你有一些你不想要的模型或者是一次性的,那麼ViewBag作爲所有非模型數據的通用catch-all提供,這就是爲什麼它是動態的。強類型會限制它成爲一個全能型的能力。

簡而言之:如果你想強類型的添加消息到你的視圖模型。否則,堅持ViewBag。那些是你的選擇。

+0

偉大的解釋! – ledgeJumper 2013-03-22 21:25:59

+0

是的,我認爲這可能是最好的建議,我會看到其他人來到這個問題跟隨以及。我真的很想看看MVC架構中是否有某些東西允許將它擴展到將你自己的捕獲全部放在你知道那些將始終如一的地方。謝謝! – Scott 2013-03-23 23:22:07

+0

請記住,至少達到MVC5(可能是MVC6 /內核)獲取/設置ViewBag的任何屬性(例如ViewBag.Title)會導致框架內部的異常被適當抑制和處理。你可以通過禁用'just my code'選項來看到這一點。這是「動態」實施的固有設計,雖然「有效」,但卻是實現流量密集型網站高性能的嚴重障礙。更多的想法:http://mvolo.com/fix-the-3-high-cpu-performance-problems-for-iis-aspnet-apps/ – xDisruptor 2017-06-21 08:10:07

1

我同意克里斯的回答,我個人會將它扔進觀察袋。

但是爲了倡導鬼子,從技術上講,你可以通融一下......

編輯:想到它現在,你也許可以用以下替換ViewBag這樣HttpContext.Items你在技術上仍在使用ViewBag存儲,但只是添加一個包裝,給它溫暖的安全強烈的感覺。

E.g.你可以有這樣的事情:

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> 
+0

這實際上是我帶走的「解決方法」但我不想把它作爲問題的一部分,因爲害怕很快關閉新的創意。我與你和Chris都在同一頁面上,但是我希望MVC架構中可以有一些擴展點來處理這個我不知道的東西。 – Scott 2013-03-23 22:36:59

0

在ASP.NET MVC中,您可以隨時使用ViewBag,ViewDataTempData(有關更多信息,請參閱this blog post)。 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,因爲它使屬性獲得更短。如果您願意(ViewData["Messages"]),您可以使用ViewData做同樣的事情。

這與Model的實施方式並不完全相同,因爲如果您碰巧使用了您要保存的密鑰,某人可能意外覆蓋了您的財產,但它足夠接近以至於在功能上等同於您確保使用一個唯一的密鑰。

如果您深入挖掘,您可能可以從ViewDataDictionary派生出來,然後將您的屬性放在那裏,然後重寫一些控制器和視圖方法以代替它。那麼你的財產將與Model完全一樣。但我會把它留給你 - 我認爲這不值得。