簡體   English   中英

將依賴項注入動態加載的.dll (.net core)

[英]Injecting dependencies into dynamically loaded .dll (.net core)

類似於How to Inject Dependencies to Dynamically Loaded Assemblies ,我想將依賴項注入到動態加載程序集中的類中。 使用 .NET 6.0 DI 容器是否可行? 如果是這樣,如何? 如果沒有,您是否可以推薦一個輕量級的 IOC 容器? (最好不要向項目添加第二個 IOC 系統。)(注意:最多只能注入 2-4 個依賴項,因此可以接受帶有 if/switch 語句的假注入系統。)
一個挑戰: ILogger<>通常需要一個類型,但 loading.dll 沒有動態加載程序集中類型的編譯時知識,反之亦然。 我可以使用非通用ILogger接口,但不確定它是否適用於 DI。

編輯:根據要求擴展示例:

給定:要注入的所有潛在依賴項都來自 Microsoft.Extensions.Hosting nuget package。我們最初希望使用的兩個是 ILogging<> 和 IConfiguration。

Type desiredClass = <Type found in the dynamically loaded assembly>;
//The below line does not inject dependencies.  I am trying to find out what will.
object classInstace = Activator.CreateInstance(desiredClass); 
MethodInfo selectedMethod = desiredClass.GetMethods
    .Single(m=>m.Name=="Execute" && m.GetParameters().All(p=>p.IsOptional));
//Schedule the method in HangFire
RecurringJob.AddOrUpdate(
    () => selectedMethod!.Invoke(
        ClassInstance,
        Array.Empty<object?>(),
        scheduleForThisTask);

我找到了一種非常丑陋的方法來完成這項工作,並且非常希望有一種更簡潔的方法來完成這項工作。 (請讓我錯過了一個內置的方法來做到這一點。)

為 IService Provider 創建一個擴展方法 GetInjectedObject:

public static object GetInjectedObject(this IServiceProvider serviceProvider, Type type)
{
    //Dependency injection the ugly way
    //Find the constructor that asks for the most injected parameters
    var constructor = type.GetConstructors().Where(cn =>
            cn.GetParameters().All(par => serviceProvider.GetServices(par.ParameterType).Any()))
        .OrderByDescending(cn => cn.GetParameters().Length).FirstOrDefault();
    if (null == constructor)
        throw new Exception($"Type {type.Name} does not have a constructor without non-injectible parameters.");
    //Get the needed parameters from the IServiceProvider
    var constructorParameters =
        constructor.GetParameters().Select(par => serviceProvider.GetService(par.ParameterType)).ToArray();
    //Create the object with the parameters
    var classInstance = Activator.CreateInstance(type, constructorParameters);
    return classInstance;
}

然后像這樣創建對象:

_classInstance = serviceProvider.GetInjectedObject(Class);

也許我誤解了這個問題,它對你有用嗎:

  • 加載程序集(即您的Composition Root )和動態加載程序集共享一個程序集嗎? 該程序集可以包含動態加載類型必須實現的接口。 (您可能已經有一個共享程序集)
  • 在啟動時加載動態加載的程序集,同時您仍在連接 DI 容器?

在那種情況下,我希望界面類似於:

namespace MySharedAssembly
{
    public interface ITask
    {
        void Execute(TaskSchedule schedule);
    }

    public class TaskSchedule { ... }
}

在動態加載的程序集中:

namespace MyDynamicAssembly
{
    public class HelloWorldTask : ITask
    {
        public void Execute(TaskSchedule schedule)
        {
            Console.WriteLine("Hello world!");
        }
    }
}

通過這樣做,您可以:

  • 通過ITask界面輕松找到動態加載程序集中的所有類型。
  • 在 DI 容器中按具體類型注冊它們。
  • 在需要時通過它們的具體類型解析它們,而它們的依賴項由 DI 容器注入。

例如:

string dynamicallyLoadedAssemblyPath = "c:\\...\etc\etc\myAssembly.dll";

// Load plugin assembly
Assembly assembly =
    Assembly.Load(AssemblyName.GetAssemblyName(dynamicallyLoadedAssemblyPath));

// Load plugin types
Type plugins = assembly.GetExportedTypes().Where(typeof(ITask).IsAssignableFrom);

// Register plugins in DI Container
foreach (var plugin in plugins)
{
    services.AddTransient(plugin, plugin);
}

// Add jobs after container was constructed
IServiceProvider provider = ...
foreach (var plugin in plugins)
{
    var scheduleForThisTask = GetSchedule(plugin);
    RecurringJob.AddOrUpdate(() =>
    {
        // It might be important to execute each task in its own scope.
        using (var scope = provider.CreateScope())
        {
            var task = (ITask)scope.ServiceProvider.GetRequiredService(plugin);
            task.Execute(scheduleForThisTask);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM