当属性验证依赖于另一个属性值时,如何使用元数据验证实体框架4模型?

本文关键字:属性 验证 实体 元数据 框架 模型 何使用 另一个 依赖于 | 更新日期: 2023-09-27 18:02:23

我有一个实体框架模型,我试图添加验证,但我发现我自己在这里有点catch 22。

我在一个单独的文件中有一个与实体生成模型相关的部分类,看起来像这样…

[MetadataType(typeof(cmsNodeMetadata))]
public partial class cmsNode : IDataErrorInfo
{  
    ... Code ....

    #region IDataErrorInfo
    private readonly Dictionary<string, string> _errors = new Dictionary<string, string>();
    private readonly Entities _db = new Entities();
    public string this[string columnName]
    {
        get
        {
            if (_errors.ContainsKey(columnName))
                return _errors[columnName];
            return string.Empty;
        }
    }
    public string Error { get; private set; }
    partial void OnNameChanging(string value)
    {
        var valueIsUnique = (from n in _db.cmsNodes
                             where n.ParentId == ParentId
                                && n.Name.Trim().ToLower() == value.Trim().ToLower()
                             select n.Name).Count() == 0;
        if (!valueIsUnique)
        {
            _errors.Add("Name", "The name must be unique");
        }
    }
    #endregion  
}

…我在一个单独的类中添加了验证,效果很好。

public class cmsNodeMetadata
{
    [StringLength(150), Required]
    [Display(Name = "Name")]
    public string Name;
}

我使用MVC 3和我的控制器,使用这个类看起来像这样…

[HttpPost]
public ActionResult Edit(cmsNode cmsnode)
{
    if (ModelState.IsValid)
    {
        var obj = _db.cmsNodes.Single(c => c.Id == cmsnode.Id);
        obj.Name = cmsnode.Name;
        obj.Alias = cmsnode.Name.Slugify(150);
        _db.SaveChanges();
        return Content(Boolean.TrueString);
    }
    return Content("Please review your form");
}

使用这个类的视图是这样的…

@model Project.com.Admin.Models.cmsNode
@using (Ajax.BeginForm("Create", "Node", null, new AjaxOptions
{
    UpdateTargetId = "create-message",
    InsertionMode = InsertionMode.Replace,
    HttpMethod = "POST",
    OnSuccess = "createSuccess"
}, new { @id = "createNodeForm" }))
{
    @Html.ValidationSummary(true)
    <div id="create-message" class="error invisible"></div>
    <fieldset>
        <legend>Create New Navigation Tree Node</legend>
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
        @Html.HiddenFor(model => model.ParentId)
        @Html.HiddenFor(model => model.Alias)
    </fieldset>
}

好的,这就是问题所在。我需要向这个(cmsNode)类添加一些自定义验证,以检查该名称是否已被具有相同ParentId的任何其他节点所采用。

为了做到这一点,我需要得到一个实例成员名为ParentId的句柄。不幸的是,我不知道如何从cmsNodeMetadata类获得我正在验证的实例的句柄。

所以我试图改变我的方法通过覆盖onChanging方法的名称属性在我的cmsNode类和添加验证,但这引起了另一个问题。虽然我能够使用验证的重写方法检查name属性,但当name从视图返回未设置时,我会得到异常,因为我的实体模型不为name属性排除空值,而且我无法捕获它,因为视图的内容在我的控制器的输入参数中被转换为模型类型。

然后我尝试使用两者,但MetadatatType和onChanging验证方法在一起,但onChanging方法被忽略。

我意识到我可以用下列方法解决这个问题:

  • 我可以将控制器的返回类型更改为FormCollection collection,一个dto对象,或者直接定义输入属性,然后在我检查空值后在控制器中使用并创建对象。

我只是想知道我错过了什么?是否有一种方法来验证属性并使用ValidationAttributes检查重复的名称?我能阻止null从我的视图返回吗?为什么在实体框架的onChanging方法有一个取消选项,以防止成员被设置,如果验证不工作?谢谢你的帮助!

当属性验证依赖于另一个属性值时,如何使用元数据验证实体框架4模型?

跨实体验证不是实体本身的一部分,因为它不是实体的责任。因此,您不应该期望该实体或放置在该实体上的任何验证器会执行此验证。实体不应该做这种验证的另一个原因是,它需要上下文来进行查询,而实体(POCO)不应该依赖于上下文。

谁负责验证?根据应用程序的体系结构,一旦实体本身有效,将在IsValid块中调用单独的方法。此方法将使用EF并查询是否存在相同的名称。如果你接近域驱动设计,验证应该在父级,这将验证它的子级没有相同的名字(这意味着它将加载子级)。