【问题标题】:Custom Validation MVC 3 with Entity Data Model带有实体数据模型的自定义验证 MVC 3
【发布时间】:2011-10-12 13:43:37
【问题描述】:

我正在使用 MVC3 开发一个项目。我首先创建了一个数据库(具有必要的约束,如 PK 和 FK),并使用 ADO.NET 实体数据模型将此数据库添加到我的 webappplicatie。一切似乎都很好,但我不能让验证变得灵活。这里有一些代码可以澄清我的问题。


编辑:添加了视图 + 正确的模型。此版本不导入数据库,但结果相同。


--型号----

namespace CustomValidation.Models
{
    public class Person : IValidatableObject
    {   

    public int Id { get; set; }
    [Required]
    public String FirstName { get; set; }
    [Required]
    public String LastName { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (String.IsNullOrEmpty(FirstName))
        {
            var field = new[] { "FirstName" };
            yield return new ValidationResult("Firstname can't be null", field);
        }
    }
}
}



namespace CustomValidation.Models
{
    public class Address : IValidatableObject
    {

    public int Id { get; set; }
    [Required]
    public String Streetname { get; set; }
    [Required]
    public String Zipcode { get; set; }
    [Required]
    public String City { get; set; }
    [Required]
    public String Country { get; set; }
    public Person Person { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (String.IsNullOrEmpty(Streetname))
        {
            var field = new[] { "Streetname" };
            yield return new ValidationResult("Streetname can't be null", field);
        }
    }
}
}


public class MasterModel
{
    public List<Address> Addressess { get; set; }
    public Person Person { get; set; }
    }
}





namespace CustomValidation.Models
{
    public class DBEntities : DbContext
    {
    public DbSet<Person> People { get; set; }
    public DbSet<Address> Addressess { get; set; }
    }
}

----控制器----

namespace CustomValidation.Controllers
{
public class HomeController : Controller
{
    public ActionResult Index()
    {
        MasterModel Model = new MasterModel();
        Model.Person = new Person();
        Model.Addressess = new List<Address>();
        Model.Addressess.Add(new Address());
        Model.Addressess.Add(new Address());
        return View(Model);
    }


    [HttpPost]
    public ActionResult Index(MasterModel Model)
    {
        DBEntities _db = new DBEntities();
        if (TryValidateModel(Model.Person))
        {
            _db.People.Add(Model.Person);
            _db.SaveChanges();
        }
        else
        {
            return View(Model);
        }

        for (int i = 0; i < Model.Addressess.Count; i++)
        {
            if (!String.IsNullOrEmpty(Model.Addressess[i].Country))
            {
                if (TryValidateModel(Model.Addressess[i]))
                {
                    Model.Addressess[i].Person = Model.Person;
                    _db.Addressess.Add(Model.Addressess[i]);
                    _db.SaveChanges();
                }
                else
                {
                    return View(Model);
                }
            }
        }
        return RedirectToAction("Index");
    }
 }

这是我的观点:

@model CustomValidation.Models.MasterModel
@{
ViewBag.Title = "Index";
}

<h2>Create</h2>

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
<div class="editor-label">
@Html.LabelFor(m => Model.Person.FirstName)
@Html.EditorFor(m => Model.Person.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(m => Model.Person.LastName)
@Html.EditorFor(m => Model.Person.LastName)
</div>

</fieldset>
for (int i = 0; i < Model.Addressess.Count; i++)
{
<fieldset>
    <legend>Address</legend>

    <div class="editor-label">
        @Html.LabelFor(model => Model.Addressess[i].Streetname)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Addressess[i].Streetname)
        @Html.ValidationMessageFor(model => Model.Addressess[i].Streetname)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => Model.Addressess[i].Zipcode)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => Model.Addressess[i].Zipcode)
        @Html.ValidationMessageFor(model => Model.Addressess[i].Zipcode)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => Model.Addressess[i].City)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => Model.Addressess[i].City)
        @Html.ValidationMessageFor(model => Model.Addressess[i].City)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => Model.Addressess[i].Country)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => Model.Addressess[i].Country)
        @Html.ValidationMessageFor(model => Model.Addressess[i].Country)
    </div>

</fieldset>

}
    <p>
        <input type="submit" value="Create" />
    </p>
}

问题(我在调试类似的东西时注意到)是在请求进入我的操作之前,MVC 首先为您的集合运行验证器。因此,如果我只在表单中填写了 Person,而在验证 Model.Person 时它只会返回 false(我猜是因为 Address 的必填字段),所以它会反弹回我的视图。

我需要一些解决方法,让我决定要在我的操作中验证我的集合的哪些实例。

在网上找了很久也没找到解决办法。

希望有人能帮帮我。

PS。我是 Visual Studio 和 MVC 3 的新手,所以请不要对我太苛刻:)

【问题讨论】:

  • 我们不要认为这种观点是理所当然的。请添加您的视图,因为视图的结构会影响模型在 POST 回您的操作时的绑定方式。
  • 好的,我会在几个小时后回家,然后我会(重新)准确地发布所有内容。我不认为视图会影响模型的绑定方式。
  • 绑定按约定工作,扩展到视图中命名元素的约定。
  • 对不起,我不明白..?顺便说一句:我已经添加了所有代码(包括视图:))

标签: visual-studio asp.net-mvc-3 entity-framework validation


【解决方案1】:

为了验证返回给您的操作的模型的子模型,您需要将TryValidateModel 应用于特定的子模型,并使用包含模型前缀的重载。如果查看生成的 HTML,您将看到 Person 字段以 Person 作为前缀,第一个 Address 字段以 Addressess[0] 作为前缀,第二个 Address 字段以 Addressess[1] 作为前缀。

另外,在调用 TryValidateModel 之前必须清除所有模型错误(否则现有错误会导致 TryValidateModel 返回 false)。

执行以下操作:

[HttpPost]
public ActionResult Index(MasterModel model)
{
    var reloadView = true;

    // clear any existing errors
    foreach (var key in ModelState.Keys)
        ModelState[key].Errors.Clear();

    if (TryValidateModel(model.Person, "Person"))   
    {   
        _db.People.Add(model.Person);   
        _db.SaveChanges();   
        reloadView = false;

        for (var i = 0; i < model.Addressess.Count(); i++)
        {
            foreach (var key in ModelState.Keys)
                ModelState[key].Errors.Clear();

            if (TryValidateModel(model.Addressess[i], "Addressess[" + i.ToString() + "]"))
            {
                **// UPDATED CODE**
                // add Person to Address model
                model.Addressess[i].Person = model.Person;

                _db.Addressess.Add(model.Addressess[i]);   
                _db.SaveChanges();   
            }
        }
    }

    if (reloadView)
    {
        // clear and re-add all errors
        foreach (var key in ModelState.Keys)
            ModelState[key].Errors.Clear();

        TryValidateModel(model);
        return View(model);
    }
    else
    {
        // go to some other view
    }
}

【讨论】:

  • 太棒了!正是我需要的。非常感谢!!!!你甚至无法想象我现在有多开心:)。再次感谢!
  • 大声笑,我现在遇到了另一个问题....当我尝试使用 TryValidateModel 时,如果模型没有所需的字段,则 ModelState 不会返回 false。它只是试图在 db 上保存更改并在 SQL 约束上崩溃......有什么想法吗?
  • 糟糕,这让我想起了我对您的模型所做的更改。我从模型中删除了来自IValidatableObjectValidate 方法的继承。 IValidatableObject 在您的场景中是不必要的,并且可能是麻烦,因为我运行了几个不同的测试,并且 TryValidateModel 在我测试的每个场景下都返回了正确的有效性。
  • 好的,我尝试运行相同但没有 IValidatableObject (相反我使用了导入的数据实体模型)。如果我不清除模型状态,它只会像我发布的第一个场景一样反弹。如果我确实清除了模型状态; TryValidateModel 没有做他的工作,我的 web 应用程序一直在 SQL 约束上崩溃.....我不确定我错过了什么......?假设我一直在清除模型状态的错误......是否有某种方法可以让您手动添加需要模型状态检查约束的实体,然后再将其传递给 TryValidateModel 方法?谢谢!
  • 我的错。我错过了问题在于约束。如果你注意到了,我编写的代码只有在存在有效的 Person 实体时才会添加 Address 实体。请参阅我对上面代码所做的更改。如果这不起作用,请检查您的 .edmx 文件以查看 Person 对象和 Address 对象之间的 FK 约束。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多