【问题标题】:EF Model Validation against database针对数据库的 EF 模型验证
【发布时间】:2013-10-16 14:24:37
【问题描述】:

我想使用 EF 5 模型验证来避免数据库中的重复值,所以我使用这样的模型类:

[Table("MeasureUnits")]
public class MeasureUnit : IValidatableObject
{
    public int MeasureUnitId { get; set; }

    public string Symbol { get; set; }

    public string Name { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        using (MeasureUnitRepository rep = new MeasureUnitRepository())
        {
            MeasureUnit measureUnit = rep.FindDuplicateBySymbol(this);

            if (measureUnit != null)
                yield return new ValidationResult(
                    "There is another unit with this symbol, you can't duplicate it", 
                    new[] { "Symbol" });
        }
    }

存储库类创建 DbContext,实现 IDisposable,具有查找重复项的逻辑,这一切都按预期工作。

但是,使用调试器时,我意识到每次插入或更新都会执行两次验证,因此存储库(和 DbContext)也会被实例化和处理两次。

除此之外,控制器中还有另一个 DbContext,但只是找不到在模型类中使用它的方法,除了在模型的构造函数中包含 DbContext,但我觉得这不是正确的解决方案。

是否有更好的“正确”方法来实现此验证?

提前致谢。

【问题讨论】:

  • 根据您希望如何处理验证错误,在数据库中创建唯一索引可能会更容易并且性能会更好(但是如果出现重复,您将获得 UpdateException 而不是验证异常)
  • @Pawel 我会使用索引来加快 FindDuplicateBy... 但我只是更喜欢在更改发布之前抓住它。但是,既然您提到了它,我意识到我也必须处理它,因为在验证之后但实际提交之前可能会发生重复。 (为此+1)

标签: c# entity-framework validation asp.net-mvc-4


【解决方案1】:

当您必须访问数据库时,您需要使用DbContext,而DbContext 有一个名为ValidateEntity 的可覆盖方法。见这篇文章:Entity Framework Validation

我把我使用的代码放在另一个答案here

还有更多关于我如何在这里structured the validation in MVC

此外,在您的存储库中实例化一个上下文可能会让您感到痛苦。存储库将需要共享上下文。您可以将上下文视为您的工作单元并将其传递到构造函数中的存储库中,或者您可以将上下文包装在您自己的工作单元中并将其传入。

【讨论】:

  • 我不喜欢为 DbContext 中的所有模型编写复制逻辑(覆盖 ValidateEntity),但是看起来我可以使用 ValidateEntity 的“items”参数来获得类似(DbContext ,存储库或我的UnitOfWork)到模型中,(通过ValidationContext),所以我不会实例化DbContext或存储库,而是在每个模型中使用它,所以逻辑将是它所属的位置(恕我直言)。看起来对吗?
  • 我在对另一个问题的评论中发表的链接中的解决方案就是这样做的,但我认为让 POCO 依赖于 DbContexts 或 Repositories 或 Units Of Work 比在上下文中验证更糟糕。归根结底,验证是一个横切关注点。有时在客户端,有时在 Web 服务器上,有时在数据库中。通常它在所有三个中
  • @Bond 我很遗憾不得不只检查一个答案,因为它是两者的结合,但事实就是如此,谢谢你们。
【解决方案2】:

您可以使用任何可用的IOC 容器,例如UnityNinjectAutofacStructureMap,将您的存储库作为依赖项注入。

这样您就可以在控制器、您的 Validate 方法或任何您需要使用它的地方访问相同的对象。

其中一些 IOC(肯定是 Ninject - 寻找“请求范围”)容器能够与 ASP.NET MVC 集成,以便每个请求创建一次依赖项(在这种情况下是您的存储库)并在请求时释放结束。

使用 Ninject 的示例:

您创建一个全局可访问(设计取决于您)ninject 内核

public static class NinjectKernel
{
    public static IKernel Kernel = new StandardKernel();
    static NinjectKernel()
    {
        Kernel.Bind<IMyRepository>().To<MyRepositoryImpl>().InRequestScope();
    }
}

以及用于 MVC 控制器的控制器工厂

public class NinjectControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext,
    Type controllerType)
    {
        return controllerType == null ? null : (IController)NinjectKernel.Kernel.Get(controllerType);
    }
}

然后你可以像这样在 Global.asax 中设置你的控制器工厂

ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());

并在您的 Validate 方法中以与在控制器工厂中完成的类似方式获取存储库。

【讨论】:

  • 我并没有真正对 IOC 做任何重要的事情,但我知道我必须在模型类构造函数中包含存储库,然后 IOC 会照顾好,是这样吗?但是我只是不完全明白,你介意举个例子吗?
  • 是的——确切地说,IOC 会小心地将一个新对象注入到您的控制器的构造函数中,并在请求的生命周期内每次您需要它时返回相同的对象。示例将取决于您选择使用的 IOC 容器,我将在几分钟内使用 Ninject 示例更新我的答案
  • 如果我是对的,关键是我需要在 MODEL 类中注入存储库,而不是在 CONTROLLER 类中,是这样还是我完全偏离了目标?
  • 您也可以在 MODEL 类中访问它,但您通常不会使用 IOC 容器创建模型(尽管这也是可能的)。
  • @MiguelVeloso - 完全正确。这样做不是一个好的设计,考虑到所有需要在旅途中创建模型的框架(如 EF 和 MVC 的模型绑定器),您最终会得到一个混乱的代码库。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多