网络核心Web API项目的类库内的依赖注入

时间:2020-09-11 08:16:39

标签: c# .net-core dll inversion-of-control webapi

我有一个.NET Core WEB API项目,我想作为.NET CORE类库的多个“项目”的容器。

我拥有的是:

  • 带有.NET Core Web API项目的解决方案“轨道”。

  • 带有.NET Core类库的解决方案“ SpaceRadar”。

首先,在我的“轨道”项目中,启动类是我到目前为止所做的:

  public void ConfigureServices(IServiceCollection services)
    {

        this.ConfigureMVC(services);
        this.ConfigureIOC(services);

    }

    private void ConfigureMVC(IServiceCollection services)
    {
        IMvcBuilder mvcBuilder = services.AddMvc(option => { option.EnableEndpointRouting = false; });
        // for each assembly inside modules directory, add the controllers
        foreach (string assemblyPath in Directory.GetFiles($"{System.AppDomain.CurrentDomain.BaseDirectory}/Modules", "*.dll", SearchOption.AllDirectories))
        {
            var assembly = Assembly.LoadFile(assemblyPath);
            mvcBuilder.AddApplicationPart(assembly);
        }
    }

这部分效果很好,因为我们可以触发在SpaceRadar项目中定义的Controller。 但是我想在类库项目中使用依赖项注入,如果可能的话,通过扫描dll来获取扩展IScopedServices / ISingletonServices / ITransientServices的所有类型。

但是,老实说,我不知道在哪里注册我的接口及其相应的实现。 我尝试了这种解决方案:

private void ConfigureIOC(IServiceCollection services)
    {

        // Store all the type that need to be injected in the IOC system
        List<Type> implementationTypes = new List<Type>();
        List<Type> singletons = new List<Type>();
        List<Type> scopeds = new List<Type>();
        List<Type> transients = new List<Type>();

        // for each assembly, load it, populate the type list of things to be injected 
        foreach (string assemblyPath in Directory.GetFiles(System.AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.AllDirectories))
        {    
            var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
            implementationTypes.AddRange(assembly.GetExportedTypes().Where(type => type.IsClass && (type.GetInterface("ISingletonServices") != null || type.GetInterface("IScopedServices") != null || type.GetInterface("ITransientServices") != null)));
            singletons.AddRange(assembly.GetExportedTypes().Where(type => type.IsInterface && type.GetInterface("ISingletonServices") != null));
            scopeds.AddRange(assembly.GetExportedTypes().Where(type => type.IsInterface && type.GetInterface("IScopedServices") != null));
            transients.AddRange(assembly.GetExportedTypes().Where(type => type.IsInterface && type.GetInterface("ITransientServices") != null));
        }

        // Register into the service collection
        foreach (Type type in singletons)
        {
            services.AddSingleton(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
        }
        foreach (Type type in scopeds)
        {
            services.AddScoped(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
        }
        foreach (Type type in transients)
        {
            services.AddTransient(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
        }

}

但是似乎存在程序集上下文的问题。 我也尝试了Assembly.LoadFrom(),但无法正常运行,博客上的ReaderLoadContext解决方案

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;

namespace Orbit
{
    public class ReaderLoadContext : AssemblyLoadContext
    {
        private AssemblyDependencyResolver _resolver;

        public ReaderLoadContext(string readerLocation)
        {
            _resolver = new AssemblyDependencyResolver(readerLocation);
        }

        protected override Assembly Load(AssemblyName assemblyName)
        {
            string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
            if (assemblyPath != null)
            {
                return LoadFromAssemblyPath(assemblyPath);
            }

            return null;
        }

        protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
        {
            string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);

            if (libraryPath != null)
            {
                return LoadUnmanagedDllFromPath(libraryPath);
            }

            return IntPtr.Zero;
        }
    }
}

甚至在程序集内部调用“ setup”方法


        private void ConfigureIOC(IServiceCollection services)
        {
            // for each assembly, load it, add the controller into the mvcBuilder and populate the type list of things to be injected 
            foreach (string assemblyPath in Directory.GetFiles($"{System.AppDomain.CurrentDomain.BaseDirectory}/Modules", "*.dll", SearchOption.AllDirectories))
            {
                Assembly assembly = Assembly.LoadFrom(assemblyPath);
                Type startup = assembly.GetTypes().SingleOrDefault(t => t.Name == "Startup");
                if (startup != null)
                {
                    var setupMethod = startup.GetMethod("Setup");
                    setupMethod.Invoke(setupMethod, new Object[] { services });
                }
            }
        }

并在我的课程库中

public static void Setup(IServiceCollection services)
        {
            
            List<Type> implementationTypes = new List<Type>();
            List<Type> singletons = new List<Type>();
            List<Type> scopeds = new List<Type>();
            List<Type> transients = new List<Type>();

            foreach (string assemblyPath in Directory.GetFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "*.dll", SearchOption.AllDirectories))
            {
                var assembly = Assembly.Load(assemblyPath);
                // get all Singleton, Scoped and Transient interfaces
                implementationTypes.AddRange(assembly.GetTypes().Where(type => type.IsClass && (type.GetInterface("ISingletonServices") != null || type.GetInterface("IScopedServices") != null || type.GetInterface("ITransientServices") != null)));
                singletons.AddRange(assembly.GetTypes().Where(type => type.IsInterface && type.GetInterface("ISingletonServices") != null));
                scopeds.AddRange(assembly.GetTypes().Where(type => type.IsInterface && type.GetInterface("IScopedServices") != null));
                transients.AddRange(assembly.GetTypes().Where(type => type.IsInterface && type.GetInterface("ITransientServices") != null));
            }

            // Register into the service collection
            foreach (Type type in singletons)
            {
                services.AddSingleton(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
            }
            foreach (Type type in scopeds)
            {
                services.AddScoped(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
            }
            foreach (Type type in transients)
            {
                services.AddTransient(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
            }
            
        }

我还尝试了HostingStartup属性

[assembly: HostingStartup(typeof(SpaceRadar.ServiceKeyInjection))]
namespace SpaceRadar
{
    public class ServiceKeyInjection : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices((context, services) => {
                List<Type> implementationTypes = new List<Type>();
                List<Type> singletons = new List<Type>();
                List<Type> scopeds = new List<Type>();
                List<Type> transients = new List<Type>();

                foreach (string assemblyPath in Directory.GetFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "*.dll", SearchOption.AllDirectories))
                {
                    var assembly = Assembly.Load(assemblyPath);
                    // get all Singleton, Scoped and Transient interfaces
                    implementationTypes.AddRange(assembly.GetTypes().Where(type => type.IsClass && (type.GetInterface("ISingletonServices") != null || type.GetInterface("IScopedServices") != null || type.GetInterface("ITransientServices") != null)));
                    singletons.AddRange(assembly.GetTypes().Where(type => type.IsInterface && type.GetInterface("ISingletonServices") != null));
                    scopeds.AddRange(assembly.GetTypes().Where(type => type.IsInterface && type.GetInterface("IScopedServices") != null));
                    transients.AddRange(assembly.GetTypes().Where(type => type.IsInterface && type.GetInterface("ITransientServices") != null));
                }

                // Register into the service collection
                foreach (Type type in singletons)
                {
                    services.AddSingleton(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
                }
                foreach (Type type in scopeds)
                {
                    services.AddScoped(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
                }
                foreach (Type type in transients)
                {
                    services.AddTransient(type, implementationTypes.Single(t => t.GetInterface(type.FullName) != null));
                }
            });
        }
    }
}

所有这些解决方案都不允许我在类库中使用这种控制器:

 public class RadarControllers : BaseControllers
    {
        private readonly IRadarServices _radarServices;

        public RadarControllers(IRadarServices radarServices)
        {
            _radarServices = radarServices;
        }

        [HttpGet("radars")]
        public async Task<IActionResult> GetRadars(CancellationToken cancellationToken)
        {
            IList<string> data = await _radarServices.GetRadars(cancellationToken);
            return Ok(data);
        }
    }

如何进行此依赖项注入? 谢谢。

1 个答案:

答案 0 :(得分:0)

我终于找到了解决该问题的方法。 问题在于,每个projet上的程序集名称都是相同的(例如:“服务”)。 .Net核心不喜欢这样,并且似乎如果我尝试通过AssemblyLoadContext.Default.LoadFromAssemblyPath进行加载,则无论指示路径如何,它都会不断加载相同的程序集。

在projet上,我只需右键单击>属性,然后将程序集名称更改为另一个。因此,AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath)可以正常工作。

例如,如果我有项目A和B,则将程序集名称服务重命名为 A.服务 和B.Services

因此dll现在是A.Services.dll,而不是Services.dll

相关问题