Exception 'No service for type 'TestPlugins.Plugin1.Service1' has been registered.' after dynamic assembly loading in .NET7

huangapple go评论63阅读模式
英文:

Exception 'No service for type 'TestPlugins.Plugin1.Service1' has been registered.' after dynamic assembly loading in .NET7

问题

I have a plugin based solution using C# on .NET 7, using dynamic assembly loading.

Actually, the IoC container says that the service has not been registered, but in fact it has!

Exception 'No service for type 'TestPlugins.Plugin1.Service1' has
been registered.'

The solution architecture is as follows:

  • TestPlugins.PluginInterface
    1. IPlugin.cs
  • TestPlugins.Plugin1
    1. Plugin1.cs
    2. Service1.cs
  • TestPlugins.Plugin2
    1. Plugin2.cs
    2. Service2.cs
  • TestPlugins.App
    1. Program.cs
    2. Plugins/TestPlugins.Plugin1.deps.json
    3. Plugins/TestPlugins.Plugin1.dll
    4. Plugins/TestPlugins.Plugin2.deps.json
    5. Plugins/TestPlugins.Plugin2.dll

The interface:

using Microsoft.Extensions.DependencyInjection;

namespace TestPlugins.PluginInterface
{
    public interface IPlugin
    {
        void Init(IServiceCollection services);

        void Run(IServiceProvider serviceProvider);
    }
}

The plugin:

using Microsoft.Extensions.DependencyInjection;
using TestPlugins.PluginInterface;

namespace TestPlugins.Plugin1
{
    public class Plugin1 : IPlugin
    {
        public void Init(IServiceCollection services)
        {
            Console.WriteLine("Plugin1 Initing.");

            services.AddTransient<IService1, Service1>();

            Console.WriteLine("Plugin1 Inited.");
        }

        public void Run(IServiceProvider serviceProvider)
        {
            var service1 = serviceProvider.GetRequiredService<Service1>();
        }
    }
}

The service:

namespace TestPlugins.Plugin1
{
    public interface IService1
    { }

    public class Service1 : IService1
    {
        public Service1()
        {
            Console.WriteLine("Service1 Ctor.");
        }
    }
}

The program:

using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using TestPlugins.PluginInterface;

namespace TestPlugins.App
{
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            Console.WriteLine("TestPlugins.App Starting.");

            using (IHost host = CreateHostBuilder(args).Build())
            {
                foreach (var plugin in _plugins)
                {
                    plugin.Run(host.Services);
                }

                await host.RunAsync().ConfigureAwait(false);
            }

            Console.WriteLine("TestPlugins.App Ending.");
        }

        // ... (remaining code)
    }
}

The exception occurs there:

using (IHost host = CreateHostBuilder(args).Build())
{
  foreach (var plugin in _plugins)
  {
    plugin.Run(host.Services); // Exception  'No service for type 'TestPlugins.Plugin1.Service1' has been registered.'
  }

  await host.RunAsync().ConfigureAwait(false);
}

This is the repo with the solution, you can check it.
Any idea to solve the problem.

Best regards

英文:

I have a plugin based solution using C# on .NET 7, using dynamic assembly loading.

Actually, the IoC container says that the service has not been registered, but in fact it has!

> Exception 'No service for type 'TestPlugins.Plugin1.Service1' has
> been registered.'

The solution architecture like:

  • TestPlugins.PluginInterface
    1. IPlugin.cs
  • TestPlugins.Plugin1
    1. Plugin1.cs
    2. Service1.cs
  • TestPlugins.Plugin2
    1. Plugin2.cs
    2. Service2.cs
  • TestPlugins.App
    1. Program.cs
    2. Plugins/TestPlugins.Plugin1.deps.json
    3. Plugins/TestPlugins.Plugin1.dll
    4. Plugins/TestPlugins.Plugin2.deps.json
    5. Plugins/TestPlugins.Plugin2.dll

The interface:

using Microsoft.Extensions.DependencyInjection;

namespace TestPlugins.PluginInterface
{
    public interface IPlugin
    {
        void Init(IServiceCollection services);

        void Run(IServiceProvider serviceProvider);
    }
}

The plugin:

using Microsoft.Extensions.DependencyInjection;
using TestPlugins.PluginInterface;

namespace TestPlugins.Plugin1
{
    public class Plugin1 : IPlugin
    {
        public void Init(IServiceCollection services)
        {
            Console.WriteLine(&quot;Plugin1 Initing.&quot;);

            services.AddTransient&lt;IService1, Service1&gt;();

            Console.WriteLine(&quot;Plugin1 Inited.&quot;);
        }

        public void Run(IServiceProvider serviceProvider)
        {
            var service1 = serviceProvider.GetRequiredService&lt;Service1&gt;();
        }
    }
}

The service:

namespace TestPlugins.Plugin1
{
    public interface IService1
    { }

    public class Service1 : IService1
    {
        public Service1()
        {
            Console.WriteLine(&quot;Service1 Ctor.&quot;);
        }
    }
}

The program:

using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using TestPlugins.PluginInterface;

namespace TestPlugins.App
{
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            Console.WriteLine(&quot;TestPlugins.App Starting.&quot;);

            using (IHost host = CreateHostBuilder(args).Build())
            {
                foreach (var plugin in _plugins)
                {
                    plugin.Run(host.Services);
                }

                await host.RunAsync().ConfigureAwait(false);
            }

            Console.WriteLine(&quot;TestPlugins.App Ending.&quot;);
        }

        private static readonly HashSet&lt;IPlugin&gt; _plugins = new();

        private static IHostBuilder CreateHostBuilder(string[] args)
        {
            var assemblies = GetPlugins();

            return Host.CreateDefaultBuilder(args)
                .ConfigureServices((context, services) =&gt;
                {
                    foreach (var assembly in assemblies)
                    {
                        var obj = Activator.CreateInstance(assembly.Value) as IPlugin;
                        if (obj != null)
                        {
                            obj.Init(services);
                            _plugins.Add(obj);
                        }
                    }
                });
        }

        private static Dictionary&lt;Assembly, Type&gt; GetPlugins()
        {
            var ipluginType = typeof(IPlugin);
            var pluginDir = Path.Combine(Environment.CurrentDirectory, &quot;Plugins&quot;);
            var pluginsDlls = Directory.GetFiles(pluginDir, &quot;*.dll&quot;, SearchOption.AllDirectories).ToList();
            return pluginsDlls.Select(dll =&gt;
            {
                return ReflectionsExcCatch(() =&gt;
                {
                    var alc = new AssemblyLoadContext(dll);
                    var asm = alc.LoadFromAssemblyPath(dll);
                    var types = asm.GetTypes();
                    var ipluginTypes = types.Where(t =&gt; ipluginType.IsAssignableFrom(t) &amp;&amp; t != ipluginType);
                    return new KeyValuePair&lt;Assembly, Type?&gt;(
                        asm,
                        ipluginTypes.FirstOrDefault());
                });
            })
            .Where(x =&gt; x.Value != null)
            .ToDictionary(x =&gt; x.Key, y =&gt; y.Value)!;
        }

        private static KeyValuePair&lt;Assembly, Type?&gt; ReflectionsExcCatch(Func&lt;KeyValuePair&lt;Assembly, Type?&gt;&gt; action)
        {
            try
            {
                return action();
            }
            catch (ReflectionTypeLoadException ex)
            {
                var sb = new StringBuilder();
                foreach (Exception exSub in ex.LoaderExceptions)
                {
                    sb.AppendLine(exSub.Message);
                    FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
                    if (exFileNotFound != null)
                    {
                        if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
                        {
                            sb.AppendLine(&quot;Fusion Log:&quot;);
                            sb.AppendLine(exFileNotFound.FusionLog);
                        }
                    }
                    sb.AppendLine();
                }
                string errorMessage = sb.ToString();
                Debug.WriteLine(errorMessage);
                throw;
            }
        }
    }
}

The exception occurs there:

using (IHost host = CreateHostBuilder(args).Build())
{
  foreach (var plugin in _plugins)
  {
    plugin.Run(host.Services); // Exception  &#39;No service for type &#39;TestPlugins.Plugin1.Service1&#39; has been registered.&#39;
  }

  await host.RunAsync().ConfigureAwait(false);
}

This is the repo with the solution, you can check it.
Any idea to solve the problem.

Best regards

答案1

得分: 1

这与动态加载程序集无关。问题仅仅是由于 Plugin1 中的编程错误引起的。

您正在注册:

services.AddTransient<IService1, Service1>();

但是您在解析时使用了:

serviceProvider.GetRequiredService<Service1>();

换句话说,您正在注册 IService1 抽象,但正在解析 Service1 实现。您应该将解析更改为:

serviceProvider.GetRequiredService<IService1>();
英文:

This has little to do with the fact that you are loading assemblies dynamically. The problem is simply caused by a programming error in Plugin1.

You are registering:

services.AddTransient&lt;IService1, Service1&gt;();

But you are resolving:

serviceProvider.GetRequiredService&lt;Service1&gt;();

In other words, you are registering the IService1 abstraction, but are resolving Service1 implementation. You should change the resolve to:

serviceProvider.GetRequiredService&lt;IService1&gt;();

huangapple
  • 本文由 发表于 2023年3月31日 16:04:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75896180.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定