#c#
#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);
Но в моем коде is используется примерно так:
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;
}
Это полный бардак…
Итак, есть ли изящный трюк, который я мог бы использовать, или другой способ вернуть строку, только если у нее есть значение?
Я знаю, что это маловероятно, но я подумал, что хотел бы спросить, прежде чем внедрять уродливое решение: (
Комментарии:
1. «другой способ вернуть строку, только если у нее есть значение» — вы уже знаете способ сделать это — если это исключительное обстоятельство…
2. Возможно, было бы аккуратнее, если бы вы следовали шаблону ‘try’, подобному
int.TryParse
, т. е.if (ValidateIf.IsNullOrEmpty(x, out message)) { handle error }
. Кроме исключений, не уверен, что еще вы можете сделать.3. Просто чтобы вы знали, такой подход к выражению значительно замедлит ваш код. Компилировать что-либо в runtimeassembly только для проверки на наличие нулей немного излишне.
4. если вы используете ASP, почему бы не использовать проверку модели learn.microsoft.com/en-us/aspnet/web-api/overview /…
5. Я просто прикидывал, читая весь код. Чего я совершенно не понимаю, так это зачем вам вообще нужны выражения? Разве вы не можете просто ввести пустой аргумент null(параметры object[] o) и вернуть o.Any(x => x == null). Вызывающий может просто вызвать, например: ArgumentsisNull(модель, model?. Строки)