使用 ValidateEntity 实现唯一约束会给出“字典中不存在给定的键”错误

发布于 2024-11-28 07:32:40 字数 2079 浏览 0 评论 0原文

在尝试使用 EF CodeFirst/Mvc3 为我的数据库实现唯一的密钥验证时,我发现了这篇文章 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();
    }
}

但我收到错误:“给定的键字典中不存在。”

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

这行似乎正在调用对象上的 Validate,该对象在覆盖 ValidateEntity 代码中设置之前访问“Context”。

起初我以为可能是 ModelState.Isvalid 过早触发验证,但事实并非如此。

有人知道我在这里缺少什么或者我做错了什么吗?提前致谢。

While in search of trying to implement unique key validations for my db using EF CodeFirst/Mvc3 I came upon this post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx which gave an example on how to do it by using IValidateObject for my object model:

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

and overriding 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);
    }

}

And the action on the controller

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

But I get the error: "The given key was not present in the dictionary." for the line

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

It seems like Validate on the object is getting called which accesses "Context" before its set in the override ValidateEntity code.

At first I thought it could have been ModelState.Isvalid triggering validate too early but it wasn't.

Anyone know what I'm missing here or what I'm doing wrong? Thanks in advance.

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

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

发布评论

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

评论(2

饮湿 2024-12-05 07:32:40

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

Model.IsValid definitely triggers it too early and perhaps something else. IValidatableObject is global interface used by both MVC and EF but your method in DbContext is called only when you call SaveChanges on the context so any usage of IValidatableObject prior to calling SaveChanges will result in the exception. You must use another approach if you want to validate your entity this way. For example store context in HttpContext.Items - you can create custom action filter and instantiate and store the context before the operation call and dispose it after operation call - hopefully it will cover all problems.

荆棘i 2024-12-05 07:32:40

我面临着同样的问题......然后经过大量谷歌搜索我终于找到了这个:

练习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"];。这样做我们就不会再出现这个错误了。

I was facing the same problem... Then after a lot of Googling I finally found this:

Exercise 3: Using IValidatableObject Custom Validation

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

As you see in their example, they instantiate a new Context and as such there's no need for validationContext.Items["Context"];. Doing so we won't get this error anymore.

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