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

时间:2013-06-04 15:32:56

标签: breeze

在阅读了Custom EFContextProvider并实现它之后,我仍在尝试找出执行服务器端验证的最佳方法,以及如何在保存之前应用业务规则...即我的问题围绕着2应该被覆盖的方法:

  • protected override bool BeforeSaveEntity(EntityInfo entityInfo) { //}
  • protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap) { // }

我知道文档指定“在调用BeforeSaveEntities方法之前”将为每个实体调用“ BeforeSaveEntity方法”。此外,我的问题围绕在具有域特定关系的多个实体上验证/应用业务规则,而不一定验证单个实体的属性(为此,我相信自定义验证可以作为explained here

所以我的问题是:

  1. 如何从服务器返回验证错误? 一旦我应用业务规则,如果它们失败,我如何向一个或多个实体添加验证错误?
  2. 如何传递某种验证上下文,以便服务器端代码“知道”应用哪些业务规则? 我的应用程序可以在几个不同的地方添加新客户,并根据应用程序上下文应该应用业务规则,或者它应该是可选的。例如,如果有一个明确的“添加新客户”屏幕以及“打印检查”屏幕,该屏幕允许“即时”创建新客户(在这种情况下,必须检查更多规则)。这可能不是理想的设计,但它是必需的。在其他几个地方也可以创建客户....但是在服务器端,我没有得到这个“上下文”来决定如何应用(以及以何种顺序)业务规则...... ,我不能使用逻辑来检查我在saveMap中的哪些实体(在BeforeSaveEntities的情况下)以确定上下文,因为这不是确定性的(saveMap可以为不同的应用程序上下文具有相同的实体)
  3. 由于 Z ...

2 个答案:

答案 0 :(得分:2)

Breeze将在保存期间使用.NET验证属性执行任何已注册的验证。以下是在实体级别和属性级别应用验证属性的示例。这两个验证都将在保存任何Customer对象期间执行,并且任何验证错误都将在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;
  }
}

答案 1 :(得分:1)

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

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

 public class NorthwindContextProvider : EFContextProvider<NorthwindIBContext_EDMX_2012> {

    protected override bool BeforeSaveEntity(EntityInfo entityInfo) {
       if ((string)SaveOptions.Tag == "myCustomSaveSetting") {

       }
    }

另一种方法是为保存的每个“版本”创建一个完全独立的端点。您可以通过 SaveOptions 实例的“resourceName”属性执行此操作。

 var so = new SaveOptions({ resourceName: "MyCustomSave" });
 return myEntityManager.saveChanges(null, so);

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

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