Breeze 使用自定义EFContextProvider进行服务器端验证

Breeze 使用自定义EFContextProvider进行服务器端验证,breeze,Breeze,在阅读并实现了它之后,我仍在试图找出执行服务器端验证的最佳方式,以及在保存之前如何应用业务规则……也就是说,我的问题围绕着两个应该被覆盖的方法: protectedoverride bool BeforeSaveEntity(EntityInfo EntityInfo){/} 受保护的覆盖字典BeforeSaveEntities(字典保存映射){/} 我知道文档指定“在调用BeforeSaveEntities方法之前,将为每个实体调用BeforeSaveEntities方法”一次。此外,我的

在阅读并实现了它之后,我仍在试图找出执行服务器端验证的最佳方式,以及在保存之前如何应用业务规则……也就是说,我的问题围绕着两个应该被覆盖的方法:

  • protectedoverride bool BeforeSaveEntity(EntityInfo EntityInfo){/}
  • 受保护的覆盖字典BeforeSaveEntities(字典保存映射){/}
我知道文档指定“在调用BeforeSaveEntities方法之前,将为每个实体调用BeforeSaveEntities方法”一次。此外,我的问题围绕着验证/应用多个实体上的业务规则,这些实体具有特定于域的关系,而不一定验证单个实体的属性(为此,我认为自定义验证可以作为一种方法)

因此,我的问题是:

  • 如何从服务器返回验证错误? 一旦我应用了业务规则,如果它们失败了,我如何向一个或多个实体添加验证错误
  • 如何传递某种验证上下文,以便服务器端代码“知道”要应用哪些业务规则? 我的应用程序可以在几个不同的地方添加新客户,并且基于应用程序上下文,应该应用业务规则,或者应该是可选的。例如,如果有明确的“添加新客户”屏幕和“打印检查”屏幕,允许“动态”创建新客户(在这种情况下,必须检查更多规则)。这可能不是理想的设计,但却是必需的。几乎没有其他地方可以创建客户……不过,在服务器端,我没有获得这个“上下文”来决定如何应用(以及以何种顺序)业务规则……此外,我无法使用逻辑来检查我在saveMap中拥有哪些实体(如果是BeforeSaveEntities)为了确定上下文,因为这是不确定的(对于不同的应用程序上下文,saveMap可以有相同的实体)
  • 谢谢 Z.

    1)通常,从服务器向客户端返回“自定义”验证错误的最简单方法是在服务器上抛出一个错误。我没有尝试但认为可能有效的是,如果希望将错误应用于特定实体,请创建一个异常,该异常包含一个属性,该属性包含失败实体的EntityKey,并引发此异常。在客户机上,您应该能够在promise failure分支中检索此错误,并自己将验证错误应用于特定实体。(顺便说一句,这听起来是breeze的一个合理的功能要求,可以简化此过程。请将其发布在上,以便我们可以了解社区的兴趣。)

    2) 有两种方法可将“上下文”应用于保存。最简单的方法是通过SaveOptions.tag属性。此属性可以在客户端上设置,并将在服务器上反序列化,以便通过SaveOptions属性在ContextProvider中使用。(大概是这样的):

    将转到“MyCustomSave”控制器方法,而不是标准的“SaveChanges”方法。i、 e

    public class NorthwindIBModelController : ApiController {
    
        [HttpPost]
        public SaveResult MyCustomSave(JObject saveBundle) {
            ContextProvider.BeforeSaveEntitiesDelegate = MyCustomBeforeSaveEntities;
            return ContextProvider.SaveChanges(saveBundle);
        }
    
        private Dictionary<Type, List<EntityInfo>> MyCustomBeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap) {     
            // your code...
        }
     }
    
    公共类NorthwindIBModelController:ApicController{
    [HttpPost]
    public SaveResult MyCustomSave(JObject saveBundle){
    ContextProvider.BeforeSaveEntitiesDelegate=MyCustomBeforeSaveEntities;
    返回ContextProvider.SaveChanges(saveBundle);
    }
    专用词典MyCustomBeforeSaveEntities(词典保存映射){
    //你的代码。。。
    }
    }
    
    在保存过程中,Breeze将使用.NET验证属性执行任何已注册的验证。下面是在实体级别和属性级别应用验证属性的示例。这两个验证都将在保存任何客户对象期间执行,任何验证错误都将在SaveChanges“fail”中返回。答应我。但是现在,您需要通过检查错误结果将结果错误附加到正确的实体/属性

    [AttributeUsage(AttributeTargets.Class)] // NEW
    public class CustomerValidator : ValidationAttribute {
      public override Boolean IsValid(Object value) {
        var cust = value as Customer;
        if (cust != null && cust.CompanyName.ToLower() == "xxx") {
          ErrorMessage = "This customer is not valid!";
          return false;
        }
        return true;
      }
    }
    
    [AttributeUsage(AttributeTargets.Property)]
    public class ContactNameValidator : ValidationAttribute {
      public override Boolean IsValid(Object value) {
        try {
          var val = (string)value;
          if (!string.IsNullOrEmpty(val) && val.StartsWith("xxx")) {
            ErrorMessage = "{0} should not start with 'xxx'"";
            return false;
          }
          return true;
        } catch (Exception e) {
          var x = e;
          return false;
        }
      }
    }
    
    [MetadataType(typeof(CustomerMetaData))]
    [CustomerValidator]
    public partial class Customer {
    
      [ContactNameValidator]
      public string ContactName {
        get;
        set;
      }
    }
    

    再次抛出一个错误似乎是被动的,没有办法在服务器上主动验证并返回验证错误吗?在模型实体上实现IValidatableObject怎么样?Breeze会“接受”验证吗?关于保存选项。标记似乎是传递上下文的一种很好的方式,我会尝试一下。我刚刚添加了一种额外的方法作为单独的答案。谢谢-关于
    IValidatableObject
    ?我们是否可以使用
    公共类Customer:IValidatableObject
    ——下面是一个示例
    public class NorthwindIBModelController : ApiController {
    
        [HttpPost]
        public SaveResult MyCustomSave(JObject saveBundle) {
            ContextProvider.BeforeSaveEntitiesDelegate = MyCustomBeforeSaveEntities;
            return ContextProvider.SaveChanges(saveBundle);
        }
    
        private Dictionary<Type, List<EntityInfo>> MyCustomBeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap) {     
            // your code...
        }
     }
    
    [AttributeUsage(AttributeTargets.Class)] // NEW
    public class CustomerValidator : ValidationAttribute {
      public override Boolean IsValid(Object value) {
        var cust = value as Customer;
        if (cust != null && cust.CompanyName.ToLower() == "xxx") {
          ErrorMessage = "This customer is not valid!";
          return false;
        }
        return true;
      }
    }
    
    [AttributeUsage(AttributeTargets.Property)]
    public class ContactNameValidator : ValidationAttribute {
      public override Boolean IsValid(Object value) {
        try {
          var val = (string)value;
          if (!string.IsNullOrEmpty(val) && val.StartsWith("xxx")) {
            ErrorMessage = "{0} should not start with 'xxx'"";
            return false;
          }
          return true;
        } catch (Exception e) {
          var x = e;
          return false;
        }
      }
    }
    
    [MetadataType(typeof(CustomerMetaData))]
    [CustomerValidator]
    public partial class Customer {
    
      [ContactNameValidator]
      public string ContactName {
        get;
        set;
      }
    }