代码之家  ›  专栏  ›  技术社区  ›  Ronnie Overby

实体框架上下文验证

  •  2
  • Ronnie Overby  · 技术社区  · 14 年前

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

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

    3 回复  |  直到 10 年前
        1
  •  2
  •   Craig Stuntz    14 年前

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

    实体只有适用于的验证 全部的 实体。

        2
  •  2
  •   Steven    10 年前

    在我开始讨论如何使用vab之前,让我说您必须仔细考虑验证规则。虽然可以区分角色之间的验证,但这确实意味着一个角色中的用户保存的对象对另一个用户无效。这意味着某个角色的用户可能需要更改该对象才能保存它。当同一用户提升到另一个角色时,也可能发生这种情况。如果你确定要这样做,请继续阅读。


    这对企业图书馆来说似乎是个好工作 Validation Application Block (VAB),因为它允许验证这些复杂的场景。当您想这样做的时候,忘记基于属性的验证;它根本不起作用。您需要基于配置的验证才能工作。

    使用VAB可以做的是使用保存实际验证的配置文件。这在一定程度上取决于实际的验证规则应该是什么,但是您可以做的是为域中的每个对象创建一个基本配置。然后创建一个或多个包含 只有 扩展验证。比如说,你有一个 validation_base.config 文件,A validation_manager.config 和A validation_admin.config 文件。

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

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

    现在您必须将这些文件合并到(至少)两个配置中。一个包含base+管理器,另一个包含base+管理规则。虽然合并不是现成的支持, 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
  •  1
  •   Ronnie Overby    14 年前

    直到我听到一个更聪明的想法,我才这么做:

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