jeudi 10 janvier 2019

Architecture for Dynamic Service Registration With Optional Dependencies

I'm trying to figure out a standard way to dynamically register services with Optional Dependencies.

I have a method with dynamic registers rest services based on a configuration in your start up. (For Simplicity sake I will only put in things relevant to the question) I have a service Configuration which contains a builder to register validators:

public interface IServiceConfiguration<TEntity, TDTO>
{
    IValidatorOptionsBuilder<TEntity> ValidatorOptions { get; set; }
}

The Builders internally have a way to register with the service collection

public interface IValidatorOptionsBuilder<TEntity>
{
    void RegisterDynamicValidator(IServiceCollection serviceCollection);
}

I would like to provide 2 options of builders to use to my clients (with and with out optional dependencies) So I've additionaly created an interface to provide a single dependency to the client.

public interface IValidatorOptionsBuilder<TEntity, TDep> : IValidatorOptionsBuilder<TEntity>
{
}

To provide a full picture, the options builder can provide dependencies to the client like so:

public class ValidatorOptionBuilder<TEntity, TDep> : ValidatorOptionsBuilder<TEntity>,
    IValidatorOptionsBuilder<TEntity,TDep>
{
    private List<Action<DynamicContextualValidator<TEntity, TDep>>> _rules
        = new List<Action<DynamicContextualValidator<TEntity, TDep>>>();

    public ValidatorOptionBuilder<TEntity, TDep> AddRule(Action<DynamicContextualValidator<TEntity,TDep>> rule)
    {
        this._rules.Add(rule);
        return this;
    }

    public virtual void RegisterDynamicValidator(IServiceCollection services)
    {
        services.AddScoped<IContextualValidator>(this.GetValidatorFactory);
        services.AddScoped<IContextualValidator<TEntity>>(this.GetValidatorFactory);
    }

    private DynamicContextualValidator<TEntity, TDep> GetValidatorFactory(IServiceProvider serviceProvider)
    {
        var dependency = serviceProvider.GetRequiredService<TDep>();
        var dynamicValidatorOptions = new DynamicValidatorOptions<TEntity, TDep>(dependency);
        //TODO: this should be done in a more immutable way
        dynamicValidatorOptions.Rules = this._rules;

        return new DynamicContextualValidator<TEntity, TDep>(dynamicValidatorOptions);
    }
}

This is complicating my registration because it requires the user to actively set the the options builder

s.ValidatorOptions = new ValidatorOptionsBuilder<User, UserAuthenticationManager>().AddRule(
    (u) => {
        u.AddRuleForEntityNotChanged(e => e.UniqueIdentifier).When(x => x.IsModified());
        u.RuleForEntity(e => e.Id == u.Dependency.GetUserId())
            .WithMessage("You do not have permission")
            .When(x => x.HasChanges());
    });

I could use a factory to provider the Options Builder, but I'm wondering if there's a simpler/standardized way to do what I'm attempting.

Aucun commentaire:

Enregistrer un commentaire