Server side validation with custom EFContextProvider

最后都变了- 提交于 2019-12-06 12:00:45

问题


After reading about Custom EFContextProvider and implementing it, I am still trying to figure out what is the best way to perform server side validation and how to apply business rule before saving...Namely my questions are revolving around 2 methods that are supposed to be overridden:

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

I am aware that docs specify that "BeforeSaveEntity method will be called for every entity before the BeforeSaveEntities method is called" once. Also, the questions I have revolve around validating/applying business rules on multiple entities that have domain specific relationships and not necessarily validating single entity's property (for that, I believe custom validation could work as explained here)

So my questions are:

  1. How do I return validation errors back from server? Once I apply business rules, and in case they fail how do I add validation error to one or more entities?
  2. How do I pass some sort of validation context so that server side code "knows" which business rules to apply? My application can add new Customer in several different places and based on app context a business rules should be applied or it should be optional. For example, if there is an explicit "Add New Customer" screen as well as "Print Check" screen which allows creating a new customer "on the fly" (in which case, more rules must be checked). This may not be desired design but it is requirement. There is few other places where customer can be created as well....On the server side though, I don't get this "context" in order to decide how to apply (and in which order) business rules...Furthermore, I cannot use logic of checking which entities I have in saveMap (in case of BeforeSaveEntities) in order to determine context since that is not deterministic (saveMap could have same entities for different app contexts)

Thanks Z...


回答1:


1) Typically the simplest way to return 'custom' validation errors from the server to the client is to simply throw an error on the server. What I have not tried but think might work is that if you want that error to be applied to a specific entity, create an exception that includes a property that contains the EntityKey's of the entity/entities that fail and throw this exception. On the client you should be able to retrieve this error, in the promise failure branch, and apply the validation error yourself to the specific entity. ( By the way, it does sound like a reasonable feature request for breeze to make this process easier. Please post it on the Breeze User Voice so we can gauge the community's interest.)

2) You have two methods to apply a 'context' to a save. The simplest is via the SaveOptions.tag property. This property can be set on the client and will be deserialized on the server for use within your ContextProvider via the SaveOptions property. (something like this):

 public class NorthwindContextProvider : EFContextProvider<NorthwindIBContext_EDMX_2012> {

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

       }
    }

The other approach is to create a completely separate endpoint for each 'version' of your save. You can do this via the 'resourceName' property of the SaveOptions instance.

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

will go to the 'MyCustomSave' controller method instead of the standard "SaveChanges" method. 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...
    }
 }



回答2:


Breeze will execute any registered validations using .NET Validation attributes during the save. Below are examples of applying validation attributes on both an entity level and a property level. Both of these validations will be executed during the save of any Customer object, and any validation errors will be returned in the SaveChanges 'fail'. promise. For now though, you will need to attach the resulting errors to the correct entity/property by inspecting the error result.

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


来源:https://stackoverflow.com/questions/16921819/server-side-validation-with-custom-efcontextprovider

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!