简体   繁体   English

XXX类型无法分配给服务YYY

[英]The type XXX is not assignable to service YYY

I need help with my autofac generics configuration. 我的autofac泛型配置需要帮助。 I get the below error which I can't seem to get around. 我收到以下错误,但似乎无法解决。 I have an interface using generics called IScript<TOptionType> . 我有一个使用泛型的接口,称为IScript<TOptionType>

Implementing that interface is an abstract class called Script<TOptionType> : IScript<TOptionType> . 实现该接口的方法是一个名为Script<TOptionType> : IScript<TOptionType>的抽象类Script<TOptionType> : IScript<TOptionType> Derived from that abstract class are two concrete classes that set their preferred TOptionType . 从该抽象类派生出两个具体的类,它们设置了它们的首选TOptionType

I've uploaded a sample .net core application that exhibits the problem here: https://github.com/Strandedpirate/agr 我已经上传了一个示例.net核心应用程序,该应用程序在此处显示了问题: https : //github.com/Strandedpirate/agr

To run go to agr\\autofac-generic-registration and type in dotnet run . 要运行,请转到agr\\autofac-generic-registration并输入dotnet run

C# seems to have no problems boxing the concrete types to either the interface or abstract base class. C#似乎没有问题,将具体类型装入接口或抽象基类。 So why is autofac complaining here? 那么为什么autofac在这里抱怨呢?

C:\Users\strandedpirate\source\repos\agr\autofac-generic-registration (master -> origin)
λ dotnet run

Unhandled Exception: System.ArgumentException: The type 'agr.TableScript' is not assignable to service 'agr.Script`1'.
   at Autofac.Builder.RegistrationBuilder.CreateRegistration(Guid id, RegistrationData data, IInstanceActivator activator, Service[] services, IComponentRegistration target) in C:\projects\autofac\src\Autofac\Builder\RegistrationBuilder.cs:line 192
   at Autofac.Builder.RegistrationBuilder.CreateRegistration[TLimit,TActivatorData,TSingleRegistrationStyle](IRegistrationBuilder`3 builder) in C:\projects\autofac\src\Autofac\Builder\RegistrationBuilder.cs:line 132
   at Autofac.Builder.RegistrationBuilder.RegisterSingleComponent[TLimit,TActivatorData,TSingleRegistrationStyle](IComponentRegistry cr, IRegistrationBuilder`3 builder) in C:\projects\autofac\src\Autofac\Builder\RegistrationBuilder.cs:line 249
   at Autofac.ContainerBuilder.Build(IComponentRegistry componentRegistry, Boolean excludeDefaultModules) in C:\projects\autofac\src\Autofac\ContainerBuilder.cs:line 240
   at Autofac.ContainerBuilder.Build(ContainerBuildOptions options) in C:\projects\autofac\src\Autofac\ContainerBuilder.cs:line 148
   at agr.Program.Main(String[] args) in C:\Users\strandedpirate\source\repos\agr\autofac-generic-registration\Program.cs:line 22

Program.cs Program.cs中

class Program
    {
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();

            // comment out the next registrations to see the program run.
            builder.RegisterType<TableScript>()
              .As<Script<TableScriptOptions>>()
              .InstancePerLifetimeScope();
            builder.RegisterType<TableScript>()
              .As(typeof(Script<>))
              .InstancePerLifetimeScope();
            builder.RegisterType<TableScript>()
              .As(typeof(IScript<>))
              .InstancePerLifetimeScope();

            // explodes here during autofac building.
            var container = builder.Build();

            // if you comment out the above autofac configuration this will succeed compile and run-time.
            // why does c# have no problems converting TableScript to IScript<T> and Script<T> but autofac is complaining?
            TestInterface(new TableScript());
            TestAbstractBase(new TableScript());

            using (var scope = container.BeginLifetimeScope())
            {
                Console.WriteLine("Resolving IScript instance...");
                var instance = scope.Resolve(typeof(IScript<>)) as IScript<object>;
                instance.Run();
            }
        }

        static void TestInterface<T>(IScript<T> a)
            where T : class, new()
        {
            Console.WriteLine($"{nameof(TestInterface)} called - {a.CanHandle("table")}");
        }

        static void TestAbstractBase<T>(Script<T> a)
            where T : class, new()
        {
            Console.WriteLine($"{nameof(TestAbstractBase)} called - {a.CanHandle("table")}");
        }
    }

IScript.cs IScript.cs

public interface IScript<TOptionType>
        where TOptionType : class, new()
    {
        bool CanHandle(string key);
        Task Run();
        bool Validate(TOptionType options);
    }

Script.cs Script.cs

public abstract class Script<TOptionType> : IScript<TOptionType>
        where TOptionType : class, new()
    {
        public abstract bool CanHandle(string key);

        public abstract Task Run();

        public virtual bool Validate(TOptionType options)
        {
            return true;
        }
    }

TableScript.cs TableScript.cs


    public class TableScript : Script<TableScriptOptions>
    {
        public override bool CanHandle(string key)
        {
            return key == "table";
        }

        public override Task Run()
        {
            Console.WriteLine($"{nameof(TableScript)} executed");
            return Task.CompletedTask;
        }
    }

FileScript.cs FileScript.cs

    public class FileScript : Script<FileScriptOptions>
    {
        public override bool CanHandle(string key)
        {
            return key == "file";
        }

        public override Task Run()
        {
            Console.WriteLine($"{nameof(FileScript)} executed");
            return Task.CompletedTask;
        }
    }

There are a couple of issues. 有几个问题。

First, your TableScript registration: 首先,您的TableScript注册:

builder.RegisterType<TableScript>()
       .As(typeof(Script<>))
       .InstancePerLifetimeScope();
builder.RegisterType<TableScript>()
       .As(typeof(IScript<>))
       .InstancePerLifetimeScope();

You're trying to register a closed generic as an open generic . 您正在尝试将封闭的泛型注册为开放的泛型 If you think about what that means, it'd be like saying " TableScript can allow any T for Script<T> and IScript<T> ". 如果您考虑这意味着什么,那就像在说“ TableScript可以为Script<T>IScript<T>允许任何T ”。 For example, I see TableScript : Script<TableScriptOptions> - what the open generic registration is saying is it should somehow also work for Script<IntegerScriptOptions> or anything else that could possibly go inside those angle brackets. 例如,我看到TableScript : Script<TableScriptOptions> -开放的通用注册所说的是,它应该也可以以某种方式适用于Script<IntegerScriptOptions>或其他可能放在尖括号中的东西。

Instead, register it as the closed generic it is. 而是将其注册为封闭的通用名称。 And I would recommend doing it on the same registration or you could get two different instances of TableScript per lifetime scope depending on which service gets resolved. 而且我建议您在同一注册中执行此操作,或者根据要解析的服务,每个生命周期范围可以得到两个不同TableScript 实例

builder.RegisterType<TableScript>()
       .As<Script<TableScriptOptions>>()
       .As<IScript<TableScriptOptions>>()
       .InstancePerLifetimeScope();

Next, the resolution of IScript<T> : 接下来, IScript<T>的分辨率:

scope.Resolve(typeof(IScript<>)) as IScript<object>;

Think of Resolve as being a lot like new . 认为Resolve非常像new If you can't use new or Activator.CreateInstance in its place (basically) then it won't work. 如果您不能(基本上)使用newActivator.CreateInstance ,它将无法正常工作。 For example, you can't do this: 例如,您不能执行以下操作:

// This isn't a thing
new Script<>();

You also can't put an open generic into a constructor of an object quite like this: 您也不能像这样将开放的泛型放入对象的构造函数中:

public class MyClass
{
  // This also isn't a thing
  public MyClass(IScript<> script) { /* ... */ }
}

You can't new-up an open generic. 您无法更新开放的通用名称。 The compiler needs to know what the T is in Script<T> . 编译器需要知道Script<T>T是什么。 By that same token, you can't resolve an open generic. 同样,您无法解析开放的泛型。 That doesn't make sense. 那没有道理。 You have to resolve a closed generic. 您必须解决一个封闭的泛型。

scope.Resolve<IScript<TableScriptOptions>>();

If you really want to use reflection, you still have to make it a closed generic. 如果确实要使用反射,则仍然必须使其成为封闭的泛型。

var script = typeof(IScript<>);
var options = typeof(TableScriptOptions);
var closed = script.MakeGenericType(new Type[] { options });
scope.Resolve(closed);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 InvalidOperationException:尝试激活“ XXX.CustomExceptionFilter”时无法解析类型为“ YYY.ILogDataAccess”的服务 - InvalidOperationException: Unable to resolve service for type 'YYY.ILogDataAccess' while attempting to activate 'XXX.CustomExceptionFilter 无法从单例yyy中使用作用域服务xxx - Cannot consume scoped service xxx from singleton yyy 类型&#39;***&#39;不能分配给Autofac中的服务&#39;***&#39; - The type '***' is not assignable to service '***' in Autofac Autofac:类型“…”不可分配给服务“…”。 - Autofac: The type '…' is not assignable to service '…'. MS Bot构建器IForm无效类型异常:具有xxx期望yyy - MS Bot builder IForm Invalid Type Exception:Has xxx Expect yyy Visual Studio Online-类型或名称空间名称yyy在名称空间xxx中不存在 - Visual Studio Online - The type or namespace name yyy does not exist in the namespace xxx Blazor:尝试激活 yyy 时无法解析 xx 类型的服务 - Blazor: Unable to resolve service for type xx while attempting to activate yyy 未捕获的TypeError:对象# <YYY> 没有方法“ xxx” - Uncaught TypeError: Object #<YYY> has no method 'xxx' 拒绝访问路径“ Global \\ {xxx} _YYY-YYY:13552”。 迟发型? - Access to the path 'Global\{xxx}_YYY-YYY:13552' is denied. Hangfire? 值xxx对yyy无效。 错误的日期时间格式 - The value xxx is not valid for yyy. Wrong datetime format
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM