Skip to content

Instantly share code, notes, and snippets.

@vkhorikov
Last active February 16, 2018 15:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkhorikov/402e3d75ff44a6de9e35c3a4896d6a7d to your computer and use it in GitHub Desktop.
Save vkhorikov/402e3d75ff44a6de9e35c3a4896d6a7d to your computer and use it in GitHub Desktop.
Code Review: Value Objects and Error Messages
public class AllowanceAmount : ValueObject
{
public const int MaxLength = 50;
public string Value { get; }
private AllowanceAmount(string value)
{
Value = value;
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Value;
}
public static Result<AllowanceAmount> Create(string allowance)
{
allowance = allowance?.Trim();
if (string.IsNullOrEmpty(allowance))
return Result.Ok(new AllowanceAmount(null));
if (allowance.Length > MaxLength)
return Result.Fail<AllowanceAmount>($"'Allowance' cannot be longer than {MaxLength} characters.");
return Result.Ok(new AllowanceAmount(allowance));
}
}
var bezeichnungResult = KatalogeintragBezeichnung.Create(updateItem.Bezeichnung);
var bezeichnungWeiblichResult = kademischerGradBezeichnungWeiblich.Create(updateItem.BezeichnungWeiblich);
var typResult = AkademischerGradTyp.Create(updateItem.Type);
var rangResult = AkademischerGradRang.Create(updateItem.Rank);
public class CustomerModel
{
[MaxLength(50, ErrorMessage = "Allowance 1 cannot be longer than 50 characters")]
public string Allowance1 { get; set; }
[MaxLength(50, ErrorMessage = "Allowance 2 cannot be longer than 50 characters")]
public string Allowance2 { get; set; }
}
[HttpPost]
public IActionResult Create([FromBody] CustomerModel item)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// ...
}
public class CustomerModel
{
public string Allowance1 { get; set; }
public string Allowance2 { get; set; }
}
[HttpPost]
public IActionResult Create([FromBody] CustomerModel item)
{
Result<AllowanceAmount> allowance1OrError = AllowanceAmount.Create(item.Allowance1);
Result<AllowanceAmount> allowance2OrError = AllowanceAmount.Create(item.Allowance2);
Result result = Result.Combine(allowance1OrError, allowance2OrError);
if (result.IsFailure)
return BadRequest(result.Error);
// ...
}
{
"Message": "The request is invalid.",
"ModelState": {
"model.Allowance1": [ "Allowance 1 cannot be longer than 50 characters." ],
"model.Allowance2": [ "Allowance 2 cannot be longer than 50 characters." ]
}
}
{
"Allowance1": [ "'Allowance' cannot be longer than 50 characters." ],
"Allowance2": [ "'Allowance' cannot be longer than 50 characters." ]
}
public class CustomerModel
{
public string Allowance1 { get; set; }
public string Allowance2 { get; set; }
}
[HttpPost]
public IActionResult Create([FromBody] CustomerModel item)
{
Result<AllowanceAmount> allowance1OrError = AllowanceAmount.Create(item.Allowance1);
Result<AllowanceAmount> allowance2OrError = AllowanceAmount.Create(item.Allowance2);
if (Result.Combine(allowance1OrError, allowance2OrError).IsFailure)
{
var errors = new[]
{
new ErrorEntry(() => item.Allowance1, allowance1OrError),
new ErrorEntry(() => item.Allowance2, allowance2OrError)
};
return BadRequest(errors);
}
// ...
}
public class ErrorEntry
{
public readonly Expression<Func<object>> Expression;
public readonly Result Result;
public ErrorEntry(Expression<Func<object>> expression, Result result)
{
Expression = expression;
Result = result;
}
}
private BadRequestObjectResult BadRequest(ErrorEntry[] entries)
{
foreach (ErrorEntry entry in entries)
{
if (entry.Result.IsSuccess)
continue;
ModelState.AddModelError(ConvertExpression(entry.Expression), entry.Result.Error);
}
return BadRequest(ModelState);
}
private string ConvertExpression(Expression<Func<object>> propertyExpression)
{
var expression = propertyExpression.Body as MemberExpression;
if (expression == null)
throw new ArgumentException(propertyExpression.Body.ToString());
return expression.Member.Name;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment