使用实体数据模型的自定义验证 MVC 3

发布于 2024-12-09 14:03:00 字数 5449 浏览 0 评论 0原文

我正在开发一个使用 MVC3 的项目。我首先创建了一个数据库(具有 PK 和 FK 等必要的约束),并使用 ADO.NET 实体数据模型将此数据库添加到我的 Web 应用程序中。一切似乎都工作正常,但我无法使验证灵活。这里有一些代码来澄清我的问题。


编辑:添加了视图+正确的模型。这个版本没有导入数据库,但结果是一样的。


--Models----

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; }
    }
}

----Controller----

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(我猜是因为地址的必填字段),因此它将反弹回我的视图。

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

经过大量时间在网上搜索后,我一直无法找到任何解决方案。

我希望有人能帮助我。

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

I'm working on a project using MVC3. I've first created a database (with the necessary constraints like PK and FK's) and added this database to my webapplicatie using the ADO.NET Entity Data Model. Everything seems to work fine but I can't make validation flexible. Here some code to clarify my question.


Edit: Added the View + correct models. This version is without importing a database but results the same.


--Models----

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; }
    }
}

----Controller----

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");
    }
 }

Here is my View:

@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>
}

The problem (that I've noticed while debugging something like this) is that before the request gets inside of my action MVC first runs validators for your collection. So if I've only filled out Person in my form, while validating Model.Person it just return false (i guess because of the Required fields for Address) and so it will bounce back to my View.

I need to have some workaround that lets me decided which instances of my collection I want to validate in my action.

I've not been able to find any solution after a lot of time searching on the net.

I hope that someone can help me out.

PS. I'm quite a newbie with Visual Studio and MVC 3 so please don't be to hard on me :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

如若梦似彩虹 2024-12-16 14:03:01

为了验证返回到您的操作的模型的子模型,您需要将 TryValidateModel 应用到特定的子模型,并使用包含模型前缀的重载。如果您查看生成的 HTML,您将看到“人员”字段以 Person 作为前缀,第一个地址字段以 Addressess[0] 作为前缀,并且第二个地址字段以 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
    }
}

In order to validate sub-models of the model returned to your action, you need to apply TryValidateModel to the specific sub-model, and use the overload which includes the prefix for the model. If you look at your generated HTML, you will see that the Person fields have Person as a prefix, the first Address fields have Addressess[0] as the prefix, and the second Address fields have Addressess[1] as the prefix.

In addition, you must clear all model errors before invoking TryValidateModel (or the existing errors will cause TryValidateModel to return false).

Do the following:

[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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文