欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

在ASP.NET Core中使用托管启动(hosting startup)程序集,实现批量注册service

程序员文章站 2023-10-29 08:39:04
在启动ASPNET Core时可以从外部程序集向应用添加增强功能。例如,外部库可以用托管启动( hosting startup) 实现为应用程序提供附加配置(Configuration)或服务(service)。 具体实现如下: 1、实现 IHostingStartup 接口 2、标注程序集(Hos ......

在启动aspnet core时可以从外部程序集向应用添加增强功能。例如,外部库可以用托管启动( hosting startup) 实现为应用程序提供附加配置(configuration)或服务(service)。

具体实现如下:

1、实现 ihostingstartup 接口

2、标注程序集(hostingstartup)属性。

[assembly: hostingstartup(typeof(startupenhancement.startupenhancementhostingstartup))]

3、在createhostbuilder中配置加载的程序集,如果多个程序集 分号 隔开

        public static ihostbuilder createhostbuilder(string[] args) =>
            host.createdefaultbuilder(args)
                .configurewebhostdefaults(webbuilder =>
                {
                    //增加外部启动项fap.core.di.servicesinjection,初始化所有service
                    webbuilder.usesetting(webhostdefaults.hostingstartupassemblieskey,"fap.core")
                    
                    .usestartup<startup>();
                });

如果阻止hosting startup加载,需要以下设置

 webbuilder.usesetting(webhostdefaults.preventhostingstartupkey, "true")

如果排除某些程序集的hosting startup加载

 webbuilder.usesetting(webhostdefaults.hostingstartupexcludeassemblieskey, "assembly1;assembly2; ...")

多个程序集 分号 隔开。

 

aspnet core 中的 di 没有批量注册service的功能。下面我就实现一个批量注册service的功能。

采用注解的形式,在需要注册为service的类上进行标注。

定义一个attribute

    [attributeusage(attributetargets.class)]
    public class serviceattribute:attribute
    {
        public serviceattribute(servicelifetime servicelifetime)
        {
            servicelifetime = servicelifetime;
        }
        public servicelifetime servicelifetime { get; set; } = servicelifetime.transient;
    }

定义需要注册的类和接口

    public interface iuser
    {
        string get(string username);
    }
    public interface iuser1
    {
        string get1(string username);
    }

    [service(microsoft.extensions.dependencyinjection.servicelifetime.singleton)]
    public class user : iuser,iuser1
    {
        private string s = guid.newguid().tostring();
        public string get(string username)
        {
            return $"{s}===={username}";
        }

        public string get1(string username)
        {
            return s;
        }
    }

如上,在user类上标注serviceattribute属性,设置servicelifetime为单利模式servicelifetime.singleton

接下来实现 利用host startup来实现自动注册功能。根据class上标注的service attribute 来自动注册service

//标注程序集属性 hostingstartup
[assembly: hostingstartup(typeof(fap.core.di.servicesinjection))] namespace fap.core.di { public class servicesinjection : ihostingstartup { public void configure(iwebhostbuilder builder) { builder.configureservices(services => { var basepath = appdomain.currentdomain.relativesearchpath ?? appdomain.currentdomain.basedirectory; var assemblies = directory.getfiles(basepath, "*.dll").select(assembly.loadfrom).toarray(); var types = assemblies.selectmany(a => a.definedtypes).select(type => type.astype()).where(t => t.getcustomattribute<serviceattribute>() != null).toarray(); var implementtypes = types.where(t => t.isclass).toarray(); foreach (var implementtype in implementtypes) { var interfacetypes = implementtype.getinterfaces(); foreach (var interfacetype in interfacetypes) { var attr = implementtype.getcustomattribute<serviceattribute>();
              //根据servicelifetime来向容器中注册 _ = (attr.servicelifetime switch { servicelifetime sl when sl == servicelifetime.scoped => services.addscoped(interfacetype, implementtype), servicelifetime sl when sl == servicelifetime.singleton => services.addsingleton(interfacetype, implementtype), servicelifetime sl when sl == servicelifetime.transient => services.addtransient(interfacetype, implementtype), _ => throw new filenotfoundexception("未找到此类型") }); } } }); } } }

下面在host builder时设置hoststartup

        public static ihostbuilder createhostbuilder(string[] args) =>
            host.createdefaultbuilder(args)
                .configurewebhostdefaults(webbuilder =>
                {
                    //增加外部启动项fap.core.di.servicesinjection,初始化所有service
                    webbuilder.usesetting(webhostdefaults.hostingstartupassemblieskey,"fap.core")
                    
                    .usestartup<startup>();
                });

 

这样我们在controller中就可以使用已经自动注册到servicecontainer中的service了。

   public class homecontroller : controller
    {
        private readonly ilogger<homecontroller> _logger;
        private readonly iuser1 _userservice1;
        private readonly iuser _userservice;
        public homecontroller(ilogger<homecontroller> logger,iuser1 userservice1,iuser user)
        {
            _logger = logger;
            _userservice1 = userservice1;
            _userservice = user;
        }

        public iactionresult index()
        {
            viewbag.cc = _userservice.get("zhangsan")+_userservice1.get1("lisi");
            return view();
        }
}

除了利用ihostingstartup为应用提供服务注册,还可以提供额外配置。

[assembly: hostingstartup(typeof(hostingstartuplibrary.servicekeyinjection))]

namespace hostingstartuplibrary
{
    public class servicekeyinjection : ihostingstartup
    {
        public void configure(iwebhostbuilder builder)
        {
            builder.configureappconfiguration(config =>
            {
                var dict = new dictionary<string, string>
                {
                    {"devaccount_fromlibrary", "dev_1111111-1111"},
                    {"prodaccount_fromlibrary", "prod_2222222-2222"}
                };

                config.addinmemorycollection(dict);
            });
        }
    }
}

在controller中就可以访问到如上配置项

        public indexmodel(iconfiguration config)
        {
            servicekey_development_library = config["devaccount_fromlibrary"];
            servicekey_production_library = config["prodaccount_fromlibrary"];
          
        }

 

-----------------------------------------------------------------------------------------------------------

具体源码实现:

webbuilder.usesetting(webhostdefaults.hostingstartupassemblieskey,"fap.core")

设置了webhostoptions中的hostingstartupassemblies属性,存放我们要加载的ihostingstartup的程序集。

在iwebhostbuilder 调用 builder()返回iwebhost方法中进行调用。下面为关键代码

            _options = new webhostoptions(_config, assembly.getentryassembly()?.getname().name);
       //没有设置阻止加载webbuilder.usesetting( webhostdefaults.preventhostingstartupkey, "false")
            if (!_options.preventhostingstartup)
            {
                var exceptions = new list<exception>();

                // 执行 hosting startup assemblies
                foreach (var assemblyname in _options.getfinalhostingstartupassemblies().distinct(stringcomparer.ordinalignorecase))
                {
                    try
                    {
                        var assembly = assembly.load(new assemblyname(assemblyname));

                        foreach (var attribute in assembly.getcustomattributes<hostingstartupattribute>())
                        {
                 //实例化自定义的hostingstartup var hostingstartup = (ihostingstartup)activator.createinstance(attribute.hostingstartuptype);
                 //调用configure方法,执行我们自定的逻辑 hostingstartup.configure(this); } } catch (exception ex) { // capture any errors that happen during startup exceptions.add(new invalidoperationexception($"startup assembly {assemblyname} failed to execute. see the inner exception for more details.", ex)); } } if (exceptions.count > 0) { hostingstartuperrors = new aggregateexception(exceptions); } }
_options.getfinalhostingstartupassemblies()方法代码如下:
   public ienumerable<string> getfinalhostingstartupassemblies()
        {
       //返回hostingstartupassemblies中排除掉hostingstartupexcludeassemblies的程序集 return hostingstartupassemblies.except(hostingstartupexcludeassemblies, stringcomparer.ordinalignorecase); }