Skip to content

Latest commit

 

History

History
80 lines (69 loc) · 4.52 KB

advanced-scenarios.md

File metadata and controls

80 lines (69 loc) · 4.52 KB

Advanced Scenarios

Aggregation of Option<TValue>

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));
    }
}

Aggregation of Result<TSuccess, TFailure>

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)))));
    )
}