通用验证方法C#

时间:2019-03-22 09:59:50

标签: c#

我有这个如下所示的当前方法:

public static class ThrowIf
{
    public static void ArgumentIsNull(params Expression<Func<object>>[] exprs)
    {
        foreach (var expr in exprs)
        {
            var member = expr.Body as MemberExpression;
            var name = member?.Member.Name ?? "Unknown";
            var value = expr.Compile()();

            if (value == null) throw new ArgumentNullException(name);
        }
    }
}

哪个很棒。我可以这样调用它:

ThrowIf.ArgumentIsNull(() => model, () => model.Lines);

代替这样做:

If (model == null) throw new ArgumentNullException(nameof(model));
If (model.Lines == null) throw new ArgumentNullException(nameof(model.Lines));

受此扩展方法的启发,我决定创建一种验证方法。我是这样的:

public static class ValidateIf
{
    public static string IsNullOrEmpty(params Expression<Func<object>>[] exprs)
    {
        foreach (var expr in exprs)
        {
            var member = expr.Body as MemberExpression;
            var name = member?.Member.Name ?? "Unknown";
            var value = expr.Compile()();

            if (value == null) return $"{name} cannot be null.";
        }

        return null;
    }
}

我没想到我就这样使用它:

ValidateIf.IsNullOrEmpty(
    () => line.ProductCode,
    () => line.Currency,
    () => line.Dates);

此问题是未使用返回类型。 我将必须执行以下操作:

return ValidateIf.IsNullOrEmpty(
    () => line.ProductCode,
    () => line.Currency,
    () => line.Dates);

但是在我的代码中,是这样使用的:

public string ValidateSynchronize(OrderViewModel model)
{
    ValidateIf.IsNullOrEmpty(
        () => model.Account,
        () => model.Account.OpeningTimes,
        () => model.Account.VehicleRouting,
        () => model.Lines
    );
    foreach (var line in model.Lines)
        ValidateIf.IsNullOrEmpty(
            () => line.Delivery,
            () => line.Delivery.OpeningTimes,
            () => line.Delivery.VehicleRouting,
            () => line.Product
        );

    return null;
}

必须成为这个:

public string ValidateSynchronize(OrderViewModel model)
{
    var message = ValidateIf.IsNullOrEmpty(
        () => model.Account,
        () => model.Account.OpeningTimes,
        () => model.Account.VehicleRouting,
        () => model.Lines
    );
    if (!string.IsNullOrEmpty(message)) return message;

    foreach (var line in model.Lines)
    {
        message = ValidateIf.IsNullOrEmpty(
            () => line.Delivery,
            () => line.Delivery.OpeningTimes,
            () => line.Delivery.VehicleRouting,
            () => line.Product
        );
        if (!string.IsNullOrEmpty(message)) return message;
    }

    return null;
}

哪个有点丑陋,当我有这样的方法时:

public async Task<string> ValidateSaveAsync(OrderViewModel model, IStockProvider stockProvider)
{
    ValidateIf.IsNullOrEmpty(
        () => model.Account,
        () => model.Account.Currency,
        () => model.AccountNumber,
        () => model.ReferenceNumber,
        () => model.Source,
        () => model.OrderDate,
        () => model.Type,
        () => model.Lines);

    var validationMessage = Validate(model);
    if (!string.IsNullOrEmpty(validationMessage))
        return validationMessage;

    foreach (var line in model.Lines)
    {
        ValidateIf.IsNullOrEmpty(
            () => line.Product,
            () => line.Product.ProductCode,
            () => line.UnitOfMeasure,
            () => line.Currency,
            () => line.Currency.UnitPrice,
            () => line.Currency.LineValue,
            () => line.Currency.IssueReference,
            () => line.Currency.IssueNumber,
            () => line.Dates);

        if (!ValidationExtensions.IsIssueNumberValid(line.Currency.IssueNumber)) return "Price Issue Number is invalid, must be greater than 0";
        if (!ValidationExtensions.IsRequiredDateValid(line.Dates.Required)) line.Dates.Required = DateTime.Now.AddDays(1).Date;
        if (!ValidationExtensions.IsDespatchDateValid(line.Dates.Dispatch)) line.Dates.Dispatch = DateTime.Now.Date;
        if (!ValidationExtensions.IsDeliveryDateValid(line.Dates.Delivery)) line.Dates.Delivery = DateTime.Now.Date;
        if (!ValidationExtensions.IsUnitPriceValid(line.Currency.UnitPrice)) return "Unit price is invalid, must be between 0 and 30";
        if (!ValidationExtensions.ValidateQuantity(line.Type, line.Quantity)) return $"Minimum of 1.5M and maximum of 22M order quantity on cut - lengths - Line:{line.Id}";
        if (!ValidationExtensions.IsRollAvailable(line.Type, line.UnitOfMeasure, line.Product.Roll30Available)) return $"30M rolls not allowed - Line:{line.Id}";

        if (string.IsNullOrEmpty(model.Type) || model.Type.Equals("S", StringComparison.CurrentCultureIgnoreCase)) continue;
        if (ValidationExtensions.IsProductDiscontinued(line.Product.Status))
            return $"Product discontinued - Line:{line.Id}";

        // TODO: Matt asked me to add this, see the comment below
        // I believe it is when they choose a particular lot number when ordering a roll as the qty for rolls is always 1 yet the stock can be
        // any size... for now if the ordered item type is a roll ignore this validation check.
        if (line.Type == OrderLineType.Roll || string.IsNullOrEmpty(line.LotNumber)) continue;

        var stock = await stockProvider.GetAsync(line.LotNumber);

        if (line.Quantity >= stock.Quantity) continue;

        return $"Selected lot {line.LotNumber} has changed - quantities no longer match (requested {line.Quantity} got {stock.Quantity}) Line {line.Id}";
    }

    return null;
}

一团糟... 那么,是否有一个我可以使用的巧妙技巧或仅当它具有值时才返回字符串的另一种方法? 我知道这不太可能,但是我想在实施丑陋的解决方案之前先问一下:(

0 个答案:

没有答案