开发者

How do I Validate An Entity Framework 4 Model Using Metadata When The Property Validation Is Dependent on Another Property Value?

I have a Entity Framework model that I'm trying to add validation to but I've found my self in a bit of a catch 22 here.

I've got a partial class related to the entity generated model in a separate file that looks like this...

[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
                                && 开发者_StackOverflown.Name.Trim().ToLower() == value.Trim().ToLower()
                             select n.Name).Count() == 0;

        if (!valueIsUnique)
        {
            _errors.Add("Name", "The name must be unique");
        }
    }

    #endregion  

}

...and I've added validation in a separate class which works great.

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

I working with MVC 3 and my controller that uses this class looks like this...

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

My view that uses this class looks like this...

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

Okay, so here's the issue. I need to add some custom validation to this(cmsNode) class that checks to make sure the name is not already taken by any other node that has the same ParentId.

In order to do that I need to get a handle on an instance member named ParentId. Unfortunately, I can't figure out how to get a handle on the instance I'm validating from the cmsNodeMetadata class.

So I tried changing my approach by overriding the onChanging method for the Name Property in my cmsNode class and adding the validation there but that caused another issue. While I am able to check the name property using the override method of validation I get exceptions when the Name comes back unset from the view because my entity model does not except null values for the Name property and I'm not able to catch it because the contents of the view are converted to the model type in the input parameter of my controller.

Then I tried using both but the MetadatatType and the onChanging methods of validation together but the onChanging method is ignored.

I realize that I could solve this problem in the following way:

  • I could change the return type of the controller to FormCollection collection, a dto object, or define input properties directly and utilize and then create the object in the controller after I check for a null value which is fine.

I'm just wondering am I missing something? Is there a way to validate the property and check for duplicate names using ValidationAttributes? Can I prevent Nulls from coming back from my view? Why doesn't the onChanging method in entity framework have a Cancel option to prevent a member from being set if the validation doesn't work out? Thanks for your help!


Cross entity validation is not part of entity itself because it is not its responsibility. Because of that you should not expect that entity or any validator placed on that entity would do this validation. Another reason why your entity should not do this validation is that it needs context to make a query and entity (POCO) should not be dependent on the context.

Who is responsible for that validation? Depending on your application architecture either separate method which will be called inside your IsValid block once the entity itself is valid. This method will use EF and query if the same name exists. If you are close to domain driven design the validation should be in parent which will validate that no its child has a same name (it means it will load children).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜