我有一个.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);
}
}
如何进行此依赖项注入? 谢谢。
答案 0 :(得分:0)
我终于找到了解决该问题的方法。 问题在于,每个projet上的程序集名称都是相同的(例如:“服务”)。 .Net核心不喜欢这样,并且似乎如果我尝试通过AssemblyLoadContext.Default.LoadFromAssemblyPath进行加载,则无论指示路径如何,它都会不断加载相同的程序集。
在projet上,我只需右键单击>属性,然后将程序集名称更改为另一个。因此,AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath)可以正常工作。
例如,如果我有项目A和B,则将程序集名称服务重命名为 A.服务 和B.Services
因此dll现在是A.Services.dll,而不是Services.dll