【发布时间】:2020-09-11 08:16:39
【问题描述】:
我有一个 .NET Core WEB API 项目,我想将多个“项目”作为 .NET CORE 类库的容器。
我拥有的是:
-
带有 .NET Core Web API 项目的“Orbit”解决方案。
-
使用 .NET Core 类库的解决方案“SpaceRadar”。
首先,在我的“Orbit”项目中,Startup 类,我目前所做的:
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 项目中定义的控制器。 但是我想在我的类库项目中使用依赖注入,如果可能的话,通过扫描 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;
}
}
}
甚至在程序集中调用“设置”方法
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);
}
}
如何进行这种依赖注入? 谢谢。
【问题讨论】:
-
tldr;我通常做的是在 IServiceCollection 上创建一个 ExtensionMethod 并只注册服务,就像我在 Startup 中所做的那样。因此,如果我有 lib“SomeServiceLib”,我将在该 lib 中有
public static void AddSomeService(this IServiceCollection services){ ...。 -
这不像我的帖子中描述的那样工作。
标签: c# .net-core dll inversion-of-control webapi