You can cache a collection of Option
-producing functions and use LINQ's Aggregate
method to produce a single result.
public interface ICustomValidationAttributeAdapterProvider
{
Option<IAttributeAdapter> GetAttributeAdapter(
ValidationAttribute attribute,
IStringLocalizer stringLocalizer);
}
internal class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider _fallbackAdapterProvider = new ValidationAttributeAdapterProvider();
private readonly ICustomValidationAttributeAdapterProvider[] _adapterProviderCollection;
public CustomValidationAttributeAdapterProvider(IEnumerable<ICustomValidationAttributeAdapterProvider> adapterProviderCollection)
{
_adapterProviderCollection = adapterProviderCollection?.ToArray() ?? throw new ArgumentNullException(nameof(adapterProviderCollection));
}
public IAttributeAdapter GetAttributeAdapter(
ValidationAttribute attribute,
IStringLocalizer stringLocalizer)
{
// use LINQ's Aggregate method to execute each function in the collection
// begin with Option<IAttributeAdapter> holding no value
// functions from the collection are executed until one produces an Option<IAttributeAdapter> holding some value
// if all functions produce no value, then ValueOrDefault creates a fallback value
return _adapterProviderCollection.Aggregate(
Option.None<IAttributeAdapter>(),
(current, provider) => current.BindOnNone(() => provider.GetAttributeAdapter(attribute, stringLocalizer)))
.ValueOrDefault(() => _fallbackAdapterProvider.GetAttributeAdapter(attribute, stringLocalizer));
}
}
You can cache a collection of Result
-producing functions and use LINQ's Aggregate
method to produce a single result.
public abstract class OutputModel { /* DATA */ }
public sealed class OutputModelType1 : OutputModel { /* DATA */ }
public sealed class OutputModelType2 : OutputModel { /* DATA */ }
public sealed class OutputModelType3 : OutputModel { /* DATA */ }
// the potential return values of each function are...
// - Option.Some<OutputModel> if the InputModel could be mapped to the specified OutputModel
// - Option.None<OutputModel> if the InputModel is being discarded
// - an Error discriminated union indicating "unknown output model" (holding the InputModel) or "an exception occurred" (holding the exception)
private static readonly Func<InputModel, Result<Option<OutputModel>, Error>> _funcCollection =
{
(inputModel) => inputModel.ToOutputModelType1(),
(inputModel) => inputModel.ToOutputModelType2(),
(inputModel) => inputModel.ToOutputModelType3()
};
// use LINQ's Aggregate method to execute each function in the collection
// begin with a Result<Option<OutputModel>, Error> holding "unknown input model" error
// functions from the collection are executed until any of the following occurs:
// - a function returns a success Result holding Option.Some<OutputModel>; ToOutputModel returns that success Result
// - a function returns a success Result holding Option.None<OutputModel>; ToOutputModel returns that success Result
// - a function returns a failure Result holding Error.Exception; ToOutputModel returns that failure Result
//
// if a function returns a failure Result holding Error.UnknownInputModel, then processing continues
// if all functions produce a failure Result holding Error.UnknownInputModel, then ToOutputModel returns a failure Result holding Error.UnknownInputModel
public static Result<Option<OutputModel>, Error> ToOutputModel(this InputModel source)
{
return _funcCollection.Aggregate(
Result.Failure<Option<OutputModel>, Error>(Error.DueToUnknownInputModel(source)),
(currentResult, functionToExecute) => currentResult.BindOnFailure(error => error.Match(
unknown => functionToExecute.Invoke(unknown.InputModel),
exception => Result.Failure<Option<OutputModel>, Error>(Error.DueToException(exception)))));
)
}