【问题标题】:Validation in Entity Framework without exceptions实体框架中的验证无例外
【发布时间】:2016-09-12 18:28:40
【问题描述】:

我正在使用实体框架,我想验证我的模型。

示例服务:

var user = _userRepository.GetUser(...);
var order = user.MakeOrder();             //<- this is some business logic in Rich Domain Model
_userRepository.Update(user);
_orderRepository.Add(order);

数据库操作可能会抛出DbEntityValidationException。我可以抓住它并做一些工作来向用户展示错误:

try
{
    _userRepository.Update(user);
    _orderRepository.Add(order);
}
catch(DbEntityValidationException ex)
{
   var error = ex.EntityValidationErrors();
   //Pass errors to Controller
}

但我知道,例外很慢。有没有什么方法可以毫无例外地做同样的事情(例如某种返回值)以获得更好的性能?

【问题讨论】:

  • 你的html文件是cshtml吗?
  • @I'mBlueDaBaDee,是的
  • 好吧,你想避免异常,所以如果你遇到错误,比如缺少必填字段,那么请确保这些被客户端捕获。
  • 您是否有任何理由不使用内置模型验证?
  • 记得接受回答

标签: c# asp.net entity-framework validation domain-driven-design


【解决方案1】:

1) 你必须实现你的模型 IValidatableObject 接口然后在 Validate 方法中定义验证规则

2-) 使用 ModelState.IsValid 属性。无需 try catch 块

3-) 为页面元素添加验证消息块

更多详情

http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3

补充

您可以使用流畅的验证 https://fluentvalidation.codeplex.com/wikipage?title=mvc

基本示例http://www.jerriepelser.com/blog/using-fluent-validation-with-asp-net-mvc-part-1-the-basics

【讨论】:

    【解决方案2】:

    您可以使用DbContext.SaveChanges 内部使用的相同方法。它是公开的 - DbContext.GetValidationErrors:

    验证跟踪的实体并返回包含验证结果的 DbEntityValidationResult 集合。

    当然,您应该以某种方式通过您的存储库公开它。

    P.S. 但请注意,实际上这可能会导致性能下降,因为SaveChanges 会再次这样做(请注意,此方法包括注释中提到的DetectChanges 调用),所以因为验证错误应该是一个例外情况,可能最好将它们作为例外情况处理,即在您的原始代码中。

    【讨论】:

      【解决方案3】:

      如果您想避免页面往返,请尝试向模型添加验证并尝试添加客户端验证。使用 JQuery 验证进行客户端验证以获得更好的性能。

      有关客户端验证的更多详细信息。尝试通过以下链接:

      1. http://www.codeproject.com/Articles/718004/ASP-NET-MVC-Client-Side-Validation
      2. http://www.asp.net/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-validation-to-the-model

      【讨论】:

        【解决方案4】:

        我建议在尝试保存到数据库之前使用内置模型验证。

        这个例子来自于 asp.net mvc 模板中构建的登录功能。

        public class LoginViewModel
        {
            [Required]
            [Display(Name = "Email")]
            [EmailAddress]
            public string Email { get; set; }
        
            [Required]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }
        
            [Display(Name = "Remember me?")]
            public bool RememberMe { get; set; }
        }
        

        然后在控制器中:

            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
            {
                if (!ModelState.IsValid)
                {
                    return View(model);
                }
        
                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, change to shouldLockout: true
                var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
                switch (result)
                {
                    case SignInStatus.Success:
                        return RedirectToLocal(returnUrl);
                    case SignInStatus.LockedOut:
                        return View("Lockout");
                    case SignInStatus.RequiresVerification:
                        return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                    case SignInStatus.Failure:
                    default:
                        ModelState.AddModelError("", "Invalid login attempt.");
                        return View(model);
                }
            }
        

        在视图中:

        @using WebApplication1.Models
        @model LoginViewModel
        @{
            ViewBag.Title = "Log in";
        }
        
        <h2>@ViewBag.Title.</h2>
        <div class="row">
            <div class="col-md-8">
                <section id="loginForm">
                    @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                    {
                        @Html.AntiForgeryToken()
                        <h4>Use a local account to log in.</h4>
                        <hr />
                        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                        <div class="form-group">
                            @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
                            <div class="col-md-10">
                                @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
                                @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
                            </div>
                        </div>
                        <div class="form-group">
                            @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
                            <div class="col-md-10">
                                @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                                @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <div class="checkbox">
                                    @Html.CheckBoxFor(m => m.RememberMe)
                                    @Html.LabelFor(m => m.RememberMe)
                                </div>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-offset-2 col-md-10">
                                <input type="submit" value="Log in" class="btn btn-default" />
                            </div>
                        </div>
                        <p>
                            @Html.ActionLink("Register as a new user", "Register")
                        </p>
                        @* Enable this once you have account confirmation enabled for password reset functionality
                            <p>
                                @Html.ActionLink("Forgot your password?", "ForgotPassword")
                            </p>*@
                    }
                </section>
            </div>
            <div class="col-md-4">
                <section id="socialLoginForm">
                    @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl })
                </section>
            </div>
        </div>
        

        Html 辅助方法 ValidationMessageFor 将使用显示注释中的文本来告知问题所在。

        【讨论】:

        • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
        • 我知道。我在睡觉的路上正在玩手机。我将在今天晚些时候的工作休息时间编辑答案。
        猜你喜欢
        • 2022-01-18
        • 2010-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-23
        相关资源
        最近更新 更多