viewmodel构造函数

时间:2015-12-10 12:11:13

标签: c# asp.net asp.net-mvc exception-handling lifecycle

系统位于Asp.Net MVC 4,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
       }
    }
    

我试图在catch中使用try-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方法继续执行。

2 个答案:

答案 0 :(得分:4)

捕获此信息的最佳方法是创建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());
}

答案 1 :(得分:1)

虽然您可以使用ExceptionFilter,但这是不必要的。这里真正的问题是你错误地使用了视图模型。视图模型应仅包含您在视图中显示/编辑所需的属性,并且不应访问数据库。有两个原因是

  1. 您无法对模型或应用的任何组件进行单元测试, 包括使用该模型的控制器。即使它清楚 你还没有进入单元测试,你至少应该为它设计(我 保证一旦你这样做,你就会成为其中不可或缺的一部分 你的发展)。
  2. 因为您将回发您的视图模型,这意味着 DefaultModelBinder将初始化模型并调用它 构造函数,它反过来调用数据库来填充你的 SelectList。您应该需要SelectList的唯一原因 POST方法是因为ModelState无效而你需要 返回视图。如果启用了客户端验证,则可以 很少见,所以你不必通过建立数据库来降低性能 要求提供不会使用的数据。
  3. 建议您阅读What is ViewModel in MVC?

    中的答案

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

    1. DefaultModelBinder正在初始化您的模型及其 将模型属性的值添加到ModelState,如果您的 属性包含任何验证属性,然后ModelState将 无效。副作用将是任何验证错误 显示在初始视图中,以及任何设置值的尝试 HtmlHelper将忽略GET方法中的属性 方法,因为它们优先使用ModelState中的值 到模型属性。为了克服这个问题,你需要使用 ModelState.Clear()黑客,有效地撤消了什么 ModelBinder刚刚完成。它再次毫无意义 开销。
    2. 因为您不能为GET和POST提供相同的签名 方法,你需要重命名POST方法并使用重载 BeginForm(),指定操作方法名称。
    3. 相反,您应该在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方法中需要的公共值,如果需要返回视图,则需要{/ 1}。