Reputation: 3832
I'm creating a .NET Core Web API and want to call an endpoint submitting a customer order. The customer id comes as a route parameter. In the request body it's possible to send an array of objects. Each object contains the product id and its amount. But this field is optional, empty orders are possible too (products can be added later on).
So I started with this DTO
public class CreateCustomerOrderByIdDto
{
[FromRoute]
public uint Id { get; set; }
[FromBody]
public OrderPosition[] OrderPositions { get; set; }
}
public class OrderPosition
{
[Range(1, uint.MaxValue)]
public uint ProductId { get; set; }
[Range(1, uint.MaxValue)]
public uint Amount { get; set; }
}
This request DTO should make the OrderPositions
field optional but when adding an item both properties are required for that item. I want to set a default value for OrderPositions
if missing so I thought this data annotation would do it
[DefaultValue(new OrderPosition[0])]
Unfortunately I get this error message
An attribute argument must be a constant expression, 'typeof()' expression or array creation expression of an attribute parameter type
So how do you mark that field as optional and set a default value?
When passing no order positions the array will be transformed to an empty one so I can avoid null checks and work with loops that just never run
Upvotes: 4
Views: 2288
Reputation: 276
The problem with arrays is that it has a fixed length. You can resize the array using Array.Resize()
but that too needs a fixed length.
A better way is to use List instead of an array:
public class CreateCustomerOrderByIdDto
{
[FromRoute]
public uint Id { get; set; }
[FromBody]
public List<OrderPosition> OrderPositions { get; set; }
}
By default, the list object OrderPositions
will have null
value; which is a good way to represent no items present in the list.
If you still need the list to be empty by default, instead of null, you can set the default value as below:
public List<OrderPosition> OrderPositions { get; set; } = new List<OrderPosition>();
Upvotes: 1
Reputation: 387
Similar to hphp's answer, you can set a default value like you would with any c# class:
public class CreateCustomerOrderByIdDto
{
[FromRoute]
public uint Id { get; set; }
[FromBody]
public OrderPosition[] OrderPositions { get; set; } = new OrderPosition[0]; // auto-initialize it here.
// alternatively you can use a constructor, but I prefer setting the property like above
public CreateCustomerOrderByIdDto
{
OrderPositions = new OrderPosition[0];
}
}
public class OrderPosition
{
[Range(1, uint.MaxValue)]
public uint ProductId { get; set; }
[Range(1, uint.MaxValue)]
public uint Amount { get; set; }
}
If you don't like those ideas and you are using Newtonsoft.Json, you can also take advantage serialization events, and perform the default value setters on those as well: https://www.newtonsoft.com/json/help/html/SerializationCallbacks.htm
Upvotes: 1
Reputation: 2272
Maybe you can use List<OrderPosition>
instead of array. Then initialize it to empty list at constructor
public class CreateCustomerOrderByIdDto
{
public CreateCustomerOrderByIdDto()
{
this.OrderPositions = new List<OrderPosition>();
}
[FromRoute]
public uint Id { get; set; }
[FromBody]
public List<OrderPosition> OrderPositions { get; set; }
}
public class OrderPosition
{
[Range(1, uint.MaxValue)]
public uint ProductId { get; set; }
[Range(1, uint.MaxValue)]
public uint Amount { get; set; }
}
Upvotes: 1