Async Defaulters
The FluentDefaults library supports default values for collections using the ForEach method to apply the same rule to multiple items in a collection.
ForEach().SetDefaulter()
It is also possible to set a defaulter for each item in a collection using the SetDefaulter (optional with a reference to the instance) method.
Example
using FluentDefaults;
public class CollectionAddressDefaulter : AbstractDefaulter<CollectionAddress>
{
public CollectionAddressDefaulter()
{
DefaultFor(x => x.Street).Is("- unknown street -");
}
}
public class CollectionCustomerDefaulter : AbstractDefaulter<CollectionCustomer>
{
public CollectionCustomerDefaulter()
{
ForEach(x => x.Addresses).SetDefaulter(new CollectionAddressDefaulter());
}
}
You can then apply the default values to an instance of the CollectionCustomer class:
var customer = new CollectionCustomer();
var defaulter = new CollectionCustomerDefaulter();
defaulter.Apply(customer);
Console.WriteLine(customer.Addresses1[0].Street); // Output: '- unknown street -'
ForEach().DefaultFor().Is()
The ForEach method is used to iterate over each item in a specified collection property of an object. It allows you to apply rules or actions to each item within the collection. The DefaultFor method is used to specify a property of the items within the collection that you want to set a default value for. The Is method is used to assign the default value to the specified property. It is called after DefaultFor to set the value that should be used if the property is not already set. You can pass a fixed value or a function (eg () => Guid.NewGuid())
Example with fixed value
internal sealed class CollectionCustomerWithDefaultForDefaulter : AbstractDefaulter<CollectionCustomer>
{
int _number = 0;
internal CollectionCustomerWithDefaultForDefaulter()
{
ForEach(x => x.Addresses).DefaultFor(x => x.Street).Is("- unknown street -");
ForEach(x => x.Addresses2).DefaultFor(x => x.Id).Is(GetNumber);
ForEach(x => x.Addresses1).DefaultFor(x => x.Street).Is((Customer x) => GetSome(x));
ForEach(x => x.Addresses1).DefaultFor(x => x.City).Is((Address x) => GetSome(x));
}
private string GetSome(Customer x) => $"{x.Number5} street";
private string GetSome(Address x) => $"City in {x.Region}";
private int GetNumber() => ++_number;
}
public class CollectionCustomer
{
public CollectionAddress[] Addresses { get; set; } = [new CollectionAddress()];
}
public class CollectionAddress
{
public string? Street { get; set; }
public string? City { get; set; }
}
ForEach().DefaultFor().SetDefaulter()
This method allows you to apply a defaulter to a specific property of each item in a collection. This is particularly useful when working with dictionaries or other collection types where you want to set default values for nested objects.
Example with Dictionary
public class AddressDefaulter : AbstractDefaulter<Address>
{
public AddressDefaulter()
{
DefaultFor(x => x.Street).Is("- unknown street -");
DefaultFor(x => x.City).Is("- unknown city -");
}
}
public class CustomerDefaulter : AbstractDefaulter<Customer>
{
public CustomerDefaulter()
{
ForEach(x => x.Addresses).DefaultFor(x => x.Value).SetDefaulter(new AddressDefaulter());
}
}
public class Customer
{
public Dictionary<string, Address> Addresses { get; set; } = new Dictionary<string, Address>();
}
public class Address
{
public string? Street { get; set; }
public string? City { get; set; }
}
Warning: If deferred execution is detected, a
DeferredExecutionExceptionwill be thrown:This is the check:
if (!(currentValue is ICollection || currentValue is Array)) { throw new DeferredExecutionException("Deferred execution detected. Please ensure the collection is materialized."); }To avoid this issue, ensure the collection is materialized (e.g., using
.ToList()or.ToArray()) before applying operations.