使用Autofac注入控制器

组件并不提供控制器注入的支持,但提供控制器创建事件来支持用户自定义控制器创建;在这个事件里用户可以引入第三方的注入组件来实现控制器相关注入功能。以下是一个技术友人针对Autofac注入的一个扩展。

控制器创建事件定义

apiServer.ActionFactory.ControllerInstance += (o, e) =>
{
                var type = e.Type;
                var constructors = type.GetConstructors();
                var parameters = constructors.Max(c => c.GetParameters());
                if (parameters.Length <= 0) return;
                var args = new object[parameters.Length];
                for (var i = 0; i < parameters.Length; i++)
                {
                    if (AutofacContainer.Container.IsRegistered(parameters[i].ParameterType))
                    {
                        args[i] = AutofacContainer.Resolve(parameters[i].ParameterType);
                    }
                    else
                    {
                        args[i] = parameters[i].ParameterType.IsValueType ? Activator.CreateInstance(parameters[i].ParameterType) : null;
                    }
                }
                e.Controller = Activator.CreateInstance(type, args);
};

AutofacContainer定义

    public class AutofacContainer
    {
        public static IContainer Container { get; private set; }

        public static void Set(IContainer c)
        {
            Container = c;
        }

        public static T Resolve<T>()
        {
            if (Container == null) throw new ArgumentNullException(nameof(Container));
            return Container.Resolve<T>();
        }

        public static object Resolve(Type serviceType)
        {
            if (Container == null) throw new ArgumentNullException(nameof(Container));
            return Container.Resolve(serviceType);
        }
    }

GenericHostAutofacServiceProviderFactory

internal class GenericHostAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        private readonly AutofacServiceProviderFactory _default;

        public GenericHostAutofacServiceProviderFactory(Action<ContainerBuilder> configure = null)
        {
            _default = new AutofacServiceProviderFactory(configure);
        }

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            var host = services.Last(sd => sd.ServiceType == typeof(IHost) && sd.ImplementationType != null);
            services.Remove(host);
            var newSd = new ServiceDescriptor(host.ImplementationType, host.ImplementationType, host.Lifetime);
            services.Add(newSd);

            var builder = _default.CreateBuilder(services);
            builder.Register(ctx => new HostDecorator((IHost)ctx.Resolve(host.ImplementationType),
                    ctx.Resolve<ILifetimeScope>(), ctx.Resolve<ILogger<Container>>()))
                .As<IHost>().ExternallyOwned();
            return builder;
        }

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            return _default.CreateServiceProvider(containerBuilder);
        }

        private class HostDecorator : IHost
        {
            private readonly IHost _host;
            private ILifetimeScope _scope;
            private readonly ILogger<Container> _logger;

            public HostDecorator(IHost host, ILifetimeScope scope, ILogger<Container> logger)
            {
                _host = host;
                _scope = scope;
                _logger = logger;
            }

            public void Dispose()
            {
                var scope = Interlocked.CompareExchange(ref _scope, null, null);
                if (scope != null)
                {
                    scope = Interlocked.CompareExchange(ref _scope, null, scope);
                    if (scope != null)
                    {
                        scope.Dispose();
                        _logger.LogInformation("Autofac container disposed");
                    }
                }

            }

            public Task StartAsync(CancellationToken cancellationToken = new CancellationToken())
                => _host.StartAsync(cancellationToken);

            public Task StopAsync(CancellationToken cancellationToken = new CancellationToken())
                => _host.StopAsync(cancellationToken);

            public IServiceProvider Services => _host.Services;
        }
    }

AutofacHostBuilderExtensions

public static class AutofacHostBuilderExtensions
    {
        public static IHostBuilder UseAutofac(this IHostBuilder builder, Action<ContainerBuilder> configure = null)
        {
            builder.UseServiceProviderFactory(new GenericHostAutofacServiceProviderFactory(configure));
            
            builder.ConfigureContainer<ContainerBuilder>((_, cb) => cb.RegisterBuildCallback(AutofacContainer.Set));
            return builder;
        }

        public static IHostBuilder ConfigureAutofac(this IHostBuilder builder, Action<HostBuilderContext, ContainerBuilder> configure) => builder.ConfigureContainer(configure);

        public static IHostBuilder ConfigureAutofac(this IHostBuilder builder, Action<ContainerBuilder> configure) => builder.ConfigureAutofac((_, cb) => configure(cb));

        public static IHostBuilder AddAutofacModule<T>(this IHostBuilder builder) where T : IModule => builder.ConfigureAutofac((ctx, cb) =>
        {
            var constructors = typeof(T).GetConstructors();
            var knownTypes = new Dictionary<Type, object>
            {
                [typeof(IConfiguration)] = ctx.Configuration,
                [typeof(IHostingEnvironment)] = ctx.HostingEnvironment,
                [typeof(HostBuilderContext)] = ctx
            };
            var cnt = -1;
            ConstructorInfo constructor = null;
            ParameterInfo[] constrParams = null;
            foreach (var item in constructors)
            {
                var parameters = item.GetParameters();
                if (parameters.Length <= cnt) continue;
                if (!parameters.All(info => knownTypes.ContainsKey(info.ParameterType))) continue;

                cnt = parameters.Length;
                constructor = item;
                constrParams = parameters;
            }

            if (constructor == null)
                throw new DependencyResolutionException(
                    $"Cannot find compatible constructor for module {typeof(T)}, can have parametrized constructor with {nameof(IConfiguration)}, {nameof(IHostingEnvironment)} or/and {nameof(HostBuilderContext)} parameters");

            var args = constrParams.Select(p => knownTypes[p.ParameterType]).ToArray();

            var module = (IModule) constructor.Invoke(args);
            cb.RegisterModule(module);
        });

        public static IHostBuilder AddAutofacModule(this IHostBuilder builder, IModule module) => builder.ConfigureAutofac(cb => cb.RegisterModule(module));

        public static IHostBuilder AddAutofacModule(this IHostBuilder builder, Func<HostBuilderContext, IModule> factory) => builder.ConfigureAutofac((ctx, cb) => cb.RegisterModule(factory(ctx)));

    }

 

AutofacAutoRegisterAttribute

    public class AutofacAutoRegisterAttribute : Attribute
    {
    }

如果控制器需要注入,标记相关属性。

使用

class Program
    {
        static void Main(string[] args)
        {
            var builder = new HostBuilder()
                .UseAutofac((cb) =>
                {
                    var assemblies = new[]
                       {
//这里加上你自己的
                                Assembly.Load("My.Domain"),
                                Assembly.Load("My.Service"),
                                Assembly.Load("My.Api.Server.Controllers"),
                                Assembly.Load("My.Api.Server")
                        };
//AutofacAutoRegisterAttribute,这个是我自己写的空Attribute,只有标志这个的才使用
                    cb.RegisterAssemblyTypes(assemblies)
                        .Where(t => t.GetCustomAttribute<AutofacAutoRegisterAttribute>() != null)
                        .AsImplementedInterfaces()
                        .InstancePerLifetimeScope();
                })
                .ConfigureServices((hostContext, services) =>
                {
//这个就是FastHttpApi的HttpServerHosted了。。。
                    services.AddHostedService<HttpServerHosted>();
                });

            builder.Build().Run();
        }
    }

来源

https://github.com/IKende/FastHttpApi/issues/30

posted @ 2019-09-18 14:40  beetlex  阅读(1415)  评论(2编辑  收藏  举报