Base solution for your next web application
Open Closed

ICustomValidate with DB access #6217


User avatar
0
rbarbosa created

Started reading https://support.aspnetzero.com/QA/Questions/2583 and could not find a way to open it again.

When sending a list or array of complex objects ABP will let you know the index of the object and the property that is having issues, I am trying to use ICustomValidate to hook up to this behavior and check for duplicates on the data, among other things that need a roundtrip to the DB

eg:

    public class AddSkuInput : ICustomValidate
    {
        [MaxLength(35)]
        [Required]
        public string Sku { get; set; }
        
        
        
        public void AddValidationErrors(CustomValidationContext context)
        {
        
            var skuManager = context.IocResolver.Resolve<ISkuManager>();
            var productTypeManager = context.IocResolver.Resolve<IProductTypeManager>();

            try
            {

                AsyncHelper.RunSync(() => skuManager.ValidateDuplicateSkuAsync(Sku)); 
            }
            catch (UserFriendlyException e)
            {
                context.Results.Add(new ValidationResult(e.Message));
            }
      }
    }
    
  
      
      //for brevity this is the ValidateDuplicateSkuAsync Method
      
      public async Task ValidateDuplicateSkuAsync(string input)
        {
            if (await _skuRepository.GetAll().AnyAsync(p => p.Sku == input))
            {
                throw new UserFriendlyException($"SKU {input} already exists.");
            }
        }

With or without AsyncHelper it produces DbContext.Disposed Errors

Users normally send 100s of these items and I would like to deal with the majority of errors in batches too so I'd like to maintain the validation error structure like this response example:

{
    "result": null,
    "targetUrl": null,
    "success": false,
    "error": {
        "code": 0,
        "message": "Your request is not valid!",
        "details": "The following errors were detected during validation.\r\n - The Sku field is required.\r\n - The Upc field is required.\r\n - The Upc field is required.\r\n",
        "validationErrors": [
            {
                "message": "The Sku field is required.",
                "members": [
                    "[0].Sku"
                ]
            },
            {
                "message": "The Upc field is required.",
                "members": [
                    "[0].Upc"
                ]
            },
             {
                "message": "The Upc field is required.",
                "members": [
                    "[1].Upc"
                ]
            }
        ]
    },
    "unAuthorizedRequest": false,
    "__abp": true
}

If keeping Database access out of the DTO is a must, how can i access the ValidationContext after the method has reached the AppService (or the DomainService) as I'd like to maintain the DataAnnotations style error result through every validation process.

Markdown is supported
Copy & paste or drag & drop images (max 30 MB per image)

3 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @rbarbosa

    You can manually begin a unitOfWork in ValidateDuplicateSkuAsync as exmplained here https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work#iunitofworkmanager.

    You can also consider using DbLevel unique index for preventing duplicate records.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    rbarbosa created

    Thank you @ismcagdas that solves the UOW issue. Happy new year!

    However I dont seem to be able to find a way to get the index of the property's parent when an array of them are being sent, as shown here:

    https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/src/Abp.Web.Api/WebApi/Validation/WebApiActionInvocationValidator.cs#L33

    public void AddValidationErrors(CustomValidationContext context)
            {
    
                var skuManager = context.IocResolver.Resolve<ISkuManager>();
                var productTypeManager = context.IocResolver.Resolve<IProductTypeManager>();
               // var actionContext = context.IocResolver.Resolve<HttpActionContext>();
                try
                {
    
                    AsyncHelper.RunSync(() => skuManager.ValidateDuplicateSkuAsync(Sku));
                }
                catch (UserFriendlyException e)
                {
                    context.Results.Add(new ValidationResult(e.Message, new[] {"Sku" }));
                }
            }
    
    {
        "result": null,
        "targetUrl": null,
        "success": false,
        "error": {
            "code": 0,
            "message": "Your request is not valid!",
            "details": "The following errors were detected during validation.\r\n - SKU duplicateSku already exists.\r\n - UPC: duplicateUpc already exists for a different SKU\r\n - Division: BadDivision not found.\r\n - SKU duplicateSku already exists.\r\n - UPC: duplicateUPC already exists for a different SKU\r\n",
            "validationErrors": [
                {
                    "message": "SKU duplicateSku already exists.",
                    "members": [
                        "sku"
                    ]
                },
                {
                    "message": "UPC: duplicateUpc already exists for a different SKU",
                    "members": null
                },
                {
                    "message": "Division: badDivision not found.",
                    "members": null
                },
                {
                    "message": "SKU duplicateSku already exists.",
                    "members": [
                        "sku"
                    ]
                },
                {
                    "message": "UPC: duplicateUpc already exists for a different SKU",
                    "members": null
                }
            ]
        },
        "unAuthorizedRequest": false,
        "__abp": true
    }
    

    is it better to abandon this validation style?

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    ismcagdas created
    Support Team

    I think it is because you are not providing the second parameter when adding ValidationResult; context.Results.Add(new ValidationResult(e.Message));

    If there will be 100s of items in request or requests, I would try to find another approach for validation. In that case, this approach will slow down your requests.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)