C# viewmodel构造函数处的异常处理(重定向)
该系统采用Asp.NETMVC4,C# 在执行控制器方法之前引发的异常。我不知道如何处理它-我想将用户重定向到错误页面,但我不能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
- 我有一个基本的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
并在FilterConfig.cs的RegisterGlobalFilters中进行全局注册public class CustomExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext filterContext) { if (filterContext.ExceptionHandled) return; //Do yout logic here } }
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new CustomExceptionFilter()); }
捕获此信息的最佳方法是创建ExceptionFilter
并在FilterConfig.cs的RegisterGlobalFilters中进行全局注册public class CustomExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext filterContext) { if (filterContext.ExceptionHandled) return; //Do yout logic here } }
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new CustomExceptionFilter()); }
虽然可以使用
,但它是不必要的。这里真正的问题是不正确地使用视图模型。视图模型应仅包含需要在视图中显示/编辑的属性,并且不应访问数据库。这有两个原因例外过滤器
- 您不能对应用程序的模型或任何组件进行单元测试, 包括使用模型的控制器。虽然很清楚 您还没有进入单元测试,您至少应该为它进行设计(I 保证一旦你这样做了,它将成为你生活中不可或缺的一部分 你的发展)
- 因为您将发回视图模型,这意味着
将初始化模型并调用其 构造函数,然后调用数据库来填充DefaultModelBinder
。您需要选择列表的唯一原因是 POST方法是因为选择列表
无效,您需要 返回视图。如果启用了客户端验证,这将是 非常罕见,因此您会通过使数据库 调用不使用的数据ModelState
建议您阅读中的答案
接下来,GET方法不应包含模型的参数。这有两个原因
正在初始化您的模型,并且 将模型属性的值添加到DefaultModelBinder
,如果 属性包含任何验证属性,则ModelState
将 无效。副作用是任何验证错误都会 在初始视图中显示,以及任何设置该值的尝试 GET方法中的属性将被ModelState
方法,因为它们优先使用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
。,而您可以使用例外过滤器
,这是不必要的。这里真正的问题是不正确地使用视图模型。视图模型应仅包含需要在视图中显示/编辑的属性,并且不应访问数据库。这有两个原因
DefaultModelBinder
将初始化模型并调用其
构造函数,然后调用数据库来填充
选择列表
。您需要选择列表的唯一原因是
POST方法是因为ModelState
无效,您需要
返回视图。如果启用了客户端验证,这将是
非常罕见,因此您会通过使数据库
调用不使用的数据DefaultModelBinder
正在初始化您的模型,并且
将模型属性的值添加到ModelState
,如果
属性包含任何验证属性,则ModelState
将
无效。副作用是任何验证错误都会
在初始视图中显示,以及任何设置该值的尝试
GET方法中的属性将被HtmlHelper
方法,因为它们优先使用ModelState
中的值
符合模型