如何在责任链中注入下一个处理程序的依赖关系? [英] How to inject the dependency of the next handler in a chain of responsibility?

查看:235
本文介绍了如何在责任链中注入下一个处理程序的依赖关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我当前的项目中,我使用了很多责任链"模式.

In my current project, I'm using quite a few Chain of Responsibility patterns.

但是,我发现通过依赖项注入配置链有点尴尬.

However, I find it a bit awkward to configure the chain via dependency injection.

给出此模型:

public interface IChainOfResponsibility 
{
    IChainOfResponsibility Next { get; }
    void Handle(Foo foo);
}

public class HandlerOne : IChainOfResponsibility 
{
    private DbContext _dbContext;

    public HandlerOne(IChainOfResponsibility next, DbContext dbContext)
    {
        Next = next;
        _dbContext = dbContext;
    }

    public IChainOfResponsibility Next { get; }

    public void Handle(Foo foo) { /*...*/}
}

public class HandlerTwo : IChainOfResponsibility 
{
    private DbContext _dbContext;

    public HandlerTwo(IChainOfResponsibility next, DbContext dbContext)
    {
        Next = next;
        _dbContext = dbContext;
    }

    public IChainOfResponsibility Next { get; }

    public void Handle(Foo foo) { /*...*/}
}

我的启动变为:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IChainOfResponsibility>(x => 
        new HandlerOne(x.GetRequiredService<HandlerTwo>(), x.GetRequiredService<DbContext>())
    );

    services.AddTransient(x => 
        new HandlerTwo(null, x.GetRequiredService<DbContext>())
    );
}

如何更清晰地配置我的责任链?

How to configure my chain of responsibility more cleanly?

推荐答案

我找到了一个简单的解决方案,因为找不到任何可以满足我要求的东西.只要GetRequiredService可以解析链中所有处理程序的所有构造函数依赖项,它似乎都可以正常工作.

I've hacked a simple solution, as I couldn't find anything that did what I wanted. Seems to be working fine, as long as the GetRequiredService can resolve all constructor dependencies of all the handlers of the chain.

我的启动课程变为:

public void ConfigureServices(IServiceCollection services)
{
    services.Chain<IChainOfResponsibility>()
        .Add<HandlerOne>()
        .Add<HandlerTwo>()
        .Configure();
}

执行魔术的代码:

public static class ChainConfigurator
{
    public static IChainConfigurator<T> Chain<T>(this IServiceCollection services) where T : class
    {
        return new ChainConfiguratorImpl<T>(services);
    }

    public interface IChainConfigurator<T>
    {
        IChainConfigurator<T> Add<TImplementation>() where TImplementation : T;
        void Configure();
    }

    private class ChainConfiguratorImpl<T> : IChainConfigurator<T> where T : class
    {
        private readonly IServiceCollection _services;
        private List<Type> _types;
        private Type _interfaceType;

        public ChainConfiguratorImpl(IServiceCollection services)
        {
            _services = services;
            _types = new List<Type>();
            _interfaceType = typeof(T);
        }

        public IChainConfigurator<T> Add<TImplementation>() where TImplementation : T
        {
            var type = typeof(TImplementation);

            if (!_interfaceType.IsAssignableFrom(type))
                throw new ArgumentException($"{type.Name} type is not an implementation of {_interfaceType.Name}", nameof(type));

            _types.Add(type);

            return this;
        }

        public void Configure()
        {
            if (_types.Count == 0)
                throw new InvalidOperationException($"No implementation defined for {_interfaceType.Name}");

            bool first = true;
            foreach (var type in _types)
            {
                ConfigureType(type, first);
                first = false;
            }
        }

        private void ConfigureType(Type currentType, bool first)
        {
            var nextType = _types.SkipWhile(x => x != currentType).SkipWhile(x => x == currentType).FirstOrDefault();

            var ctor = currentType.GetConstructors().OrderByDescending(x => x.GetParameters().Count()).First();

            var parameter = Expression.Parameter(typeof(IServiceProvider), "x");

            var ctorParameters = ctor.GetParameters().Select(x =>
            {
                if (_interfaceType.IsAssignableFrom(x.ParameterType))
                {
                    if (nextType == null)
                        return Expression.Constant(null, _interfaceType);
                    else
                        return Expression.Call(typeof(ServiceProviderServiceExtensions), "GetRequiredService", new Type[] { nextType }, parameter);
                }

                return (Expression)Expression.Call(typeof(ServiceProviderServiceExtensions), "GetRequiredService", new Type[] { x.ParameterType }, parameter);
            });

            var body = Expression.New(ctor, ctorParameters.ToArray());

            var resolveType= first ? _interfaceType : currentType;

            var expressionType = Expression.GetFuncType(typeof(IServiceProvider), resolveType);
            var expression = Expression.Lambda(expressionType, body, parameter);
            var compiledExpr = (Func<IServiceProvider, object>)expression.Compile();
            _services.AddTransient(resolveType, compiledExpr);
        }
    }
}

PS ::我正在回答自己的问题以供将来参考(我自己,并希望是其他人),但我希望对此有一些反馈.

PS.: I'm answering my own question for future reference (myself and hopefully others), but I'd love some feedback on this.

这篇关于如何在责任链中注入下一个处理程序的依赖关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆