2015-12-10 60 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)(如果需要返回视图)。