2015-12-10 58 views
1

該系統位於Asp.Net MVC 4,C#中。視圖模型構造函數的異常處理(重定向)

如果在執行控制器方法之前拋出異常。我不知道如何處理它 - 我想將用戶重定向到錯誤頁面,但我不能。

  • 我有一個基本的ViewModel類,它包含一個SelectList用於下拉菜單。在它的構造函數中,ViewModel從數據庫中獲取它的SelectListItems。這是異常的來源。

  • index方法將viewmodel作爲參數。

  • 這是代碼的草圖:

    class MyViewModel{ 
        public SelectList SelectListModel { get; set; } 
        public MyViewModel() 
        { 
         List<X> xs = GetItemsFromDB(); // <= Exception thrown here 
         List<SelectListItem> SelectListContent = new List<SelectListItem>(); 
         foreach(X x in xs) 
         { 
          SelectListContent.Add(new SelectListItem(Value = x.value,Text=x.text); 
         } 
         SelectListModel = new SelectList(SelectListContent , "Value", "Text"); 
        } 
    } 
    public class MyController : Controller 
    { 
    
        public ActionResult Index(MyViewModel model) //<< Exception thrown before entering method 
        { 
        //do something 
        } 
    } 
    

我試圖把一個try-catch的構造器內與在catch下面的代碼:

  var context = new HttpContextWrapper(HttpContext.Current); 
      var rc = new RequestContext(context, new RouteData()); 
      var urlHelper = new UrlHelper(rc); 
      context.Response.Redirect(urlHelper.Action("Index", "Error", new { messagem = x.Message }), false); 
      HttpContext.Current.ApplicationInstance.CompleteRequest(); 

我從其他SO答案中獲得了這個答案,但它不起作用。執行此塊時,用戶不會重定向到錯誤頁面。相反,MyControllers Index方法繼續執行。

+1

您不應該在GET方法中將模型作爲參數。爲什麼在這個世界上你創建了一個'SelectList',然後從它創建另一個重複的。視圖模型不應該有訪問數據庫的代碼。 –

+0

什麼是例外?斯蒂芬: – n1ff

+0

:但現在我有這個問題,你知道一個解決方法嗎?到Niff:例外是打開數據庫失敗(「無法打開數據庫etrc」)。 – galmeida

回答

4

趕上這一點的最好辦法是從FilterConfig.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
{ 
    filters.Add(new CustomExceptionFilter()); 
} 
+0

這工作,只需要添加filterContext.ExceptionHandled = true;在OnException()的末尾。 – galmeida

+0

@galmeida您能否請您將答案標記爲正確的答案? –

1

創建ExceptionFilter

public class CustomExceptionFilter : IExceptionFilter 
{  
     public void OnException(ExceptionContext filterContext) 
     { 

      if (filterContext.ExceptionHandled) 
       return;  

      //Do yout logic here 
     } 
} 

並註冊成爲全局,在RegisterGlobalFilters雖然你可以使用一個ExceptionFilter,它就是不必要。這裏真正的問題是你錯誤地使用視圖模型。視圖模型應該只包含需要在視圖中顯示/編輯的屬性,並且不應該訪問數據庫。兩個原因是

  1. 不能單元測試模型或您的應用程序的任何組件, 包括控制器,使用該模式。儘管它尚未進入單元測試階段,但您至少應該爲其設計(I 保證一旦您完成,您將成爲您的開發的一個組成部分)。
  2. 因爲你將張貼回到您的視圖模型,這意味着 DefaultModelBinder將初始化模型,並調用其 構造函數這反過來調用數據庫來填充你的 SelectList。您應該需要SelectList POST方法的唯一原因是因爲ModelState無效,您需要 返回視圖。如果啓用客戶端驗證,這將是罕見的,因此您通過使數據庫 調用不會使用的數據來不必要地降低性能。

建議你閱讀What is ViewModel in MVC?

接下來的答案,你的GET方法不應該包含您的模型的參數。兩個原因是

  1. DefaultModelBinder正在初始化您的模型,它 增加你的模特屬性的值ModelState,如果你 屬性包含任何驗證屬性,然後將ModelState 無效。副作用將是在初始視圖中顯示任何驗證錯誤 ,並且任何嘗試在GET方法中設置屬性值 將被HtmlHelper 方法忽略,因爲它們使用ModelState優先 到模型屬性。爲了克服這個問題,您需要使用破解工具,有效地撤銷剛剛完成的工作。再次,它只是沒有多餘的額外 開銷。
  2. 由於GET和POST方法不能具有相同的簽名,因此需要重命名POST方法並使用指定操作方法名稱的過載 BeginForm()

相反,您應該在GET方法內初始化視圖模型的實例。

最後,在你的模型構造函數中的代碼來生成SelectList正在產生一個IEnumerable<SelectListItem>,然後從第一個(也是它只是不必要的額外開銷)創造了第二個相同的IEnumerable<SelectListItem>

從你的意見,你已經表示,這將是一個基本視圖模型,所以我建議你有一個BaseController以下方法

protected void ConfigureBaseViewModel(BaseVM model) 
{ 
    List<X> xs = GetItemsFromDB(); 
    model.SelectListModel = new SelectList(xs, "value", "text"); 
    // or model.SelectListModel = xs.Select(x => new SelectListItem{ Value = x.value, Text=x.text }); 
} 

其中BaseVM

public abstract class BaseVM 
{ 
    [Required(ErrorMessage = "Please select an item")] // add other display and validation attributes as necessary 
    public int SelectedItem { get; set; } // or string? 
    public IEnumerable<SelectListItem> SelectListModel { get; set; } 
    .... // other common properties 
} 

然後在具體的控制器中

public ActionResult Index() 
{ 
    var model = new yourConcreteModel(); 
    ConfigureBaseViewModel(model); 
    return View(model); 
} 
[HttpPost] 
public ActionResult Index(yourConcreteModel model) 
{ 
    if (!ModelState.IsValid) 
    { 
    ConfigureBaseViewModel(model); 
    return View(model); 
    } 
    // save and redirect 
} 

類似地,您可能在每個具體控制器中都有一個private void ConfigureConcreteViewModel(yourConcreteModel model)方法,該方法分配GET方法和POST方法中需要的常用值(如SelectLists)(如果需要返回視圖)。