使用 ValidateEntity 实现唯一约束会"The given key was not present in the dictionary"错误



在尝试使用EF CodeFirst/Mvc3为我的db实现唯一键验证的过程中,我发现了这篇文章http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx,其中给出了如何通过使用IValidateObject为我的对象模型进行验证的示例:

public class Category : IValidatableObject
{
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    public string Description { get; set; }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var testContext = (TestContext)validationContext.Items["Context"];
        if (testContext.Categories.Any(
            c => c.CategoryName == CategoryName && c.CategoryID != CategoryID))
        {
            yield return new ValidationResult("A category with the same name already exists!", new[] { "CategoryName" });
        }
        yield break;
    }
}

和重写DbEntityValidationResult ValidateEntity:

public class TestContext : DbContext
{
    public DbSet<Test.Models.Category> Categories { get; set; }
    protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        var myItems = new Dictionary<object, object>();
        myItems.Add("Context", this);
        return base.ValidateEntity(entityEntry, myItems);
    }
}

和控制器上的动作

[HttpPost]
public ActionResult Create(Category category)
{
    if (ModelState.IsValid) {
        categoryRepository.InsertOrUpdate(category);
        categoryRepository.Save();
        return RedirectToAction("Index");
    } else {
        return View();
    }
}

但是我得到了错误:"The given key was not present in the dictionary."为行

var testContext = (TestContext)validationContext.Items["Context"];

似乎对象上的Validate正在被调用,它在override ValidateEntity代码中设置之前访问"Context"。

起初我认为它可能是ModelState。Isvalid触发validate太早,但没有。

谁知道我在这里错过了什么或我做错了什么?

Model.IsValid肯定触发得太早了,也许是别的什么。IValidatableObject是MVC和EF使用的全局接口,但是DbContext中的方法只有在上下文中调用SaveChanges时才会调用,因此在调用SaveChanges之前使用IValidatableObject将导致异常。如果希望以这种方式验证实体,则必须使用另一种方法。例如,在HttpContext.Items中存储上下文-您可以创建自定义动作过滤器,并在操作调用之前实例化和存储上下文,并在操作调用后处理上下文-希望它能解决所有问题。

我也面临着同样的问题…然后在谷歌上搜索了很多之后,我终于找到了这个:

练习3:使用IValidatableObject自定义验证

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    MusicStoreEntities storeDB = new MusicStoreEntities();
    if (storeDB.Albums.Any(
                       a => a.Title.Trim().ToUpper() == this.Title.Trim().ToUpper() &&
                       a.ArtistId == (int)this.ArtistId))
    {
        yield return new ValidationResult("Existing Album", new string[] { "Title" });
    }
}

正如你在他们的例子中看到的,他们实例化了一个新的Context,因此不需要validationContext.Items["Context"];

相关内容

最新更新