【问题标题】:Entity Framework Contextual Validation实体框架上下文验证
【发布时间】:2011-04-14 18:55:19
【问题描述】:

当在某些情况下应应用不同的验证逻辑时,您将如何为实体框架实体实施验证?

例如,如果用户是管理员,则以一种方式验证实体,否则以不同的方式验证。

【问题讨论】:

  • 不管叫什么,你会怎么做?我想在实体处于无效状态时有条件地阻止持久化实体。
  • 当您想查看您的模型是否处于业务认为有效的状态,并且在检查此模型的过程中您不会更改该模型并报告任何错误到用户,在那种情况下,我会认为您正在做的是验证。如果您对不同类型的用户有不同的规则,这并不重要。
  • 别忘了标记你最喜欢的答案 ;-)

标签: c# .net entity-framework validation


【解决方案1】:

我将验证属性放在特定于上下文的专用编辑模型上。

实体只有适用于所有实体的验证。

【讨论】:

  • 我喜欢。我不喜欢 cmets 上的字符最小值。
【解决方案2】:

在我开始讨论如何使用 VAB 执行此操作之前,让我说您必须认真考虑您的验证规则。虽然区分角色之间的验证是可能的,但这确实意味着一个角色中的用户保存的对象对另一个用户无效。这意味着具有特定角色的用户可能需要更改该对象才能保存它。当同一用户被提升为另一个角色时,这也可能发生在同一用户身上。如果您确定要这样做,请继续阅读。


这对于 Enterprise Library 的 Validation Application Block (VAB) 来说似乎是一项不错的工作,因为它允许验证这些复杂的场景。当你想这样做时,忘记基于属性的验证;它根本行不通。为此,您需要基于配置的验证。

您可以使用 VAB 执行的操作是使用包含实际验证的配置文件。这有点取决于实际的验证规则应该是什么,但您可以做的是创建一个始终适用于域中每个对象的基本配置。然后创建一个或多个扩展验证的配置。例如,假设您有一个validation_base.config 文件、一个validation_manager.config 和一个validation_admin.config 文件。

您可以根据用户的角色将这些验证合并在一起。看一下这个例子,它基于配置文件创建了三个配置源:

var base = new FileConfigurationSource("validation_base.config");
var mngr = new FileConfigurationSource("validation_manager.config");
var admn = new FileConfigurationSource("validation_admin.config");

现在您必须将这些文件合并到(至少)两个配置中。一个包含基本 + 管理器,另一个包含基本 + 管理规则。虽然开箱即用不支持合并,但this article 将向您展示如何进行。使用该文章中的代码时,您将能够做到这一点:

var managerValidations = 
    new ValidationConfigurationSourceCombiner(base, mngr);

var adminValidations =
    new ValidationConfigurationSourceCombiner(base, admn);

您需要做的最后一件事是将这些验证包装在一个类中,该类会根据用户的角色返回正确的集合。你可以这样:

public class RoleConfigurationSource : IConfigurationSource
{
    private IConfigurationSource base;
    private IConfigurationSource managerValidations;
    private IConfigurationSource adminValidations;

    public RoleConfigurationSource()
    {
        this.base = new FileConfigurationSource("validation_base.config");
        var mngr = new FileConfigurationSource("validation_manager.config");
        var admn = new FileConfigurationSource("validation_admin.config");

        managerValidations = 
            new ValidationConfigurationSourceCombiner(base, mngr);

        adminValidations =
            new ValidationConfigurationSourceCombiner(base, admn);
    }

    public ConfigurationSection GetSection(string sectionName)
    {
        if (sectionName == ValidationSettings.SectionName)
        {
            if (Roles.UserIsInRole("admin"))
            {
                return this.adminValidations;
            }
            else
            {
                return this.managerValidations;
            }
        }

        return null;
    }

    #region IConfigurationSource Members

    // Rest of the IConfigurationSource members left out.
    // Just implement them by throwing an exception from
    // their bodies; they are not used.

    #endregion
}

现在这个RoleConfigurationSource 可以创建一次,您可以在验证对象时提供它,如下所示:

static readonly IConfigurationSource validationConfiguration =
    new RoleConfigurationSource();

Validator customerValidator =
    ValidationFactory.CreateValidator<Customer>(validationConfiguration);

ValidationResults results = customerValidator.Validate(customer);

if (!results.IsValid)
{
    throw new InvalidOperationException(results[0].Message);
}

请注意,验证应用程序块不是一个简单的框架。需要一些时间来学习它。但是,当您的应用程序足够大时,您的特定要求将证明使用它是合理的。如果您选择 VAB,请先阅读“Hands-On Labs”文档。如果您有问题,请回到这里 ;-)

祝你好运。

【讨论】:

【解决方案3】:

在我听到更聪明的想法之前,我正在这样做:

public partial class MyObjectContext
{
    ValidationContext ValidationContext { get; set; }

    partial void OnContextCreated()
    {
        SavingChanges += new EventHandler(EntitySavingChanges);
    }

    private void EntitySavingChanges(object sender, EventArgs e)
    {
        ObjectStateManager
            .GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted)
            .Where(entry => entry.Entity is IValidatable).ToList().ForEach(entry =>
            {
                var entity = entry.Entity as IValidatable;
                entity.Validate(entry, ValidationContext);
            });
    }
}

interface IValidatable
{
    void Validate(ObjectStateEntry entry, ValidationContext context);
}

public enum ValidationContext
{
    Admin,
    SomeOtherContext
}

public partial class MyEntity : IValidatable
{
    public ValidationContext ValidationContext { get; set; }

    public void Validate(ObjectStateEntry entry, ValidationContext context)
    {
        // this validation doesn't apply to admins
        if (context != ValidationContext.Admin)
        {
            // validation logic here
        }
    }  
}

【讨论】:

    猜你喜欢
    • 2018-08-11
    • 2010-09-16
    • 1970-01-01
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多