C# viewmodel构造函数处的异常处理(重定向)

C# viewmodel构造函数处的异常处理(重定向),c#,asp.net,asp.net-mvc,exception-handling,lifecycle,C#,Asp.net,Asp.net Mvc,Exception Handling,Lifecycle,该系统采用Asp.NETMVC4,C# 在执行控制器方法之前引发的异常。我不知道如何处理它-我想将用户重定向到错误页面,但我不能 我有一个基本的ViewModel类,它包含一个要在下拉列表中使用的SelectList。在其构造函数中,ViewModel从数据库获取其SelectListItems。这是异常的来源 索引方法将viewmodel作为参数 下面是代码的草图: class MyViewModel{ public SelectList SelectListModel { get; s

该系统采用Asp.NETMVC4,C#

在执行控制器方法之前引发的异常。我不知道如何处理它-我想将用户重定向到错误页面,但我不能

  • 我有一个基本的ViewModel类,它包含一个要在下拉列表中使用的SelectList。在其构造函数中,ViewModel从数据库获取其SelectListItems。这是异常的来源

  • 索引方法将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
       }
    }
    

    我从其他SO答案中得到了这个答案,但它不起作用。执行此块时,用户不会重定向到错误页面。相反,MyControllers索引方法将继续执行。

    捕获此结果的最佳方法是创建ExceptionFilter

    public class CustomExceptionFilter : IExceptionFilter
    {    
            public void OnException(ExceptionContext filterContext)
            {
    
                if (filterContext.ExceptionHandled)
                    return;    
    
                //Do yout logic here
            }
    }
    
    并在FilterConfig.cs的RegisterGlobalFilters中进行全局注册

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

    捕获此信息的最佳方法是创建ExceptionFilter

    public class CustomExceptionFilter : IExceptionFilter
    {    
            public void OnException(ExceptionContext filterContext)
            {
    
                if (filterContext.ExceptionHandled)
                    return;    
    
                //Do yout logic here
            }
    }
    
    并在FilterConfig.cs的RegisterGlobalFilters中进行全局注册

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

    虽然可以使用
    例外过滤器
    ,但它是不必要的。这里真正的问题是不正确地使用视图模型。视图模型应仅包含需要在视图中显示/编辑的属性,并且不应访问数据库。这有两个原因

  • 您不能对应用程序的模型或任何组件进行单元测试, 包括使用模型的控制器。虽然很清楚 您还没有进入单元测试,您至少应该为它进行设计(I 保证一旦你这样做了,它将成为你生活中不可或缺的一部分 你的发展)
  • 因为您将发回视图模型,这意味着
    DefaultModelBinder
    将初始化模型并调用其 构造函数,然后调用数据库来填充
    选择列表
    。您需要选择列表的唯一原因是 POST方法是因为
    ModelState
    无效,您需要 返回视图。如果启用了客户端验证,这将是 非常罕见,因此您会通过使数据库 调用不使用的数据
  • 建议您阅读中的答案

    接下来,GET方法不应包含模型的参数。这有两个原因

  • DefaultModelBinder
    正在初始化您的模型,并且 将模型属性的值添加到
    ModelState
    ,如果 属性包含任何验证属性,则
    ModelState
    将 无效。副作用是任何验证错误都会 在初始视图中显示,以及任何设置该值的尝试 GET方法中的属性将被
    HtmlHelper
    方法,因为它们优先使用
    ModelState
    中的值 添加到模型属性。要克服这个问题,您需要使用
    ModelState.Clear()
    
    ModelBinder
    刚刚完成。再说一遍,这只是毫无意义的额外费用 头顶
  • 因为GET和POST的签名不能相同 方法,则需要重命名POST方法并使用 指定操作方法名称的
    BeginForm()
  • 相反,您应该在GET方法中初始化视图模型的实例

    最后,模型构造函数中用于生成
    SelectList
    的代码将生成一个
    IEnumerable
    ,然后从第一个创建第二个相同的
    IEnumerable
    (同样,这只是不必要的额外开销)

    根据您的评论,您已经指出这将是一个基本视图模型,因此我建议您使用以下方法创建一个
    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 });
    }
    
    然后在混凝土控制器中

    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
    }
    

    类似地,在每个具体控制器中,您可能有一个
    私有void ConfigureConcreteViewModel(您的ConcreteModel模型)
    方法,如果需要返回视图,该方法可以分配GET方法和POST方法中所需的公共值,例如
    selectList

    ,而您可以使用
    例外过滤器
    ,这是不必要的。这里真正的问题是不正确地使用视图模型。视图模型应仅包含需要在视图中显示/编辑的属性,并且不应访问数据库。这有两个原因

  • 您不能对应用程序的模型或任何组件进行单元测试, 包括使用模型的控制器。虽然很清楚 您还没有进入单元测试,您至少应该为它进行设计(I 保证一旦你这样做了,它将成为你生活中不可或缺的一部分 你的发展)
  • 因为您将发回视图模型,这意味着
    DefaultModelBinder
    将初始化模型并调用其 构造函数,然后调用数据库来填充
    选择列表
    。您需要选择列表的唯一原因是 POST方法是因为
    ModelState
    无效,您需要 返回视图。如果启用了客户端验证,这将是 非常罕见,因此您会通过使数据库 调用不使用的数据
  • 建议您阅读中的答案

    接下来,GET方法不应包含模型的参数。这有两个原因

  • DefaultModelBinder
    正在初始化您的模型,并且 将模型属性的值添加到
    ModelState
    ,如果 属性包含任何验证属性,则
    ModelState
    将 无效。副作用是任何验证错误都会 在初始视图中显示,以及任何设置该值的尝试 GET方法中的属性将被
    HtmlHelper
    方法,因为它们优先使用
    ModelState
    中的值 符合模型