带有依赖于其他通用服务的通用服务的.NET依赖注入

时间:2019-12-05 11:25:34

标签: c# generics .net-core

简介

两个月前,我开始使用C#进行开发。到目前为止,我来自使用Symfony的PHP背景,到目前为止,我对依赖注入和爱情泛型知识有所了解,但是两者的结合现在让我有些困惑。

概念

我想使用工厂来创建IHandler的特定实例。 IHandler可以收到一个IParser<T>,如您所见,它支持泛型,因此我为每个可能的解析器提供了一个接口。工厂将获得IParser的列表,这些列表具有SupportsParsing方法,该方法将在工厂内部调用以获取正确的IParser并使用选定的{ {1}}。

代码

IServiceFactory

IHandler

EventHandlerFactory

IParser

IParser

public interface IServiceFactory<out T>
{
    T Create(string key);
}

EventCounterParser

public class EventHandlerFactory<T> : IServiceFactory<IHandler>
{
    private readonly IEnumerable<IParser<T>> _parsers;

    public EventHandlerFactory(IEnumerable<IParser<T>> parsers)
    {
        _parsers = parsers;
    }

    public IHandler Create(string key)
    {
        foreach (IParser<T> parser in _parsers)
        {
            if (parser.SupportsParsing(key))
            {
                return new EventHandler<T>(parser);
            }
        }

        throw new NotImplementedException();
    }
}

EventLevelParser

public interface IParser<T>
{
    List<T> Parse();
    bool SupportsParsing(string key);
}

IHandler

public class EventCounterParser : IParser<EventCounter>
{
    public List<EventCounter> Parse()
    {
        throw new System.NotImplementedException();
    }

    public bool SupportsParsing(string key)
    {
        throw new System.NotImplementedException();
    }
}

EventHandler

public class EventLevelParser : IParser<EventLevel>
{
    public List<EventLevel> Parse()
    {
        throw new System.NotImplementedException();
    }

    public bool SupportsParsing(string key)
    {
        throw new System.NotImplementedException();
    }
}

注册服务

所以注册解析器很容易

public interface IHandler
{
    void Handle();
}

意图

但是我现在要做的是:  *注册服务工厂  *注册处理程序  *自动将所有public class EventHandler<T> : IHandler { private readonly IParser<T> _parser; public EventHandler(IParser<T> parser) { _parser = parser; } public void Handle() { throw new System.NotImplementedException(); } } 实例注入services.AddScoped<IParser<EventCounter>, EventCounterParser>(); services.AddScoped<IParser<EventLevel>, EventLevelParser>();

这对我来说很难。我真的不知道如何在泛型内部使用泛型时注册服务,以及如何在此处注入泛型IParserEventHandlerFactory

尝试

自己尝试

我为IList做的尝试:

IEnumerable
  

未处理的异常。 System.AggregateException:某些服务无法构建(验证服务描述符'ServiceType:GenericDITest.Handler.IHandler生存期:范围实现类型:GenericDITest.Handler.Event.EventHa时出错。   ndler IHandler 1 [GenericDITest.Parser.Event.EventLevelParser]”,同时尝试激活“ GenericDITest.Handler.Event.EventHandler services.AddScoped<IHandler, Handler.Event.EventHandler<EventLevelParser>>(); services.AddScoped<IHandler, Handler.Event.EventHandler<EventCounterParser>>(); services.AddScoped(typeof(IServiceFactory<>), typeof(EventHandlerFactory<>)); 1 [GenericDITest.Parser.Event.EventCounterParser]”:Unab   文件来解析类型“ GenericDITest.Parser.IParser 1[GenericDITest.Parser.Event.EventLevelParser]': Unable to resolve service for type 'GenericDITest.Parser.IParser 1 [GenericDITest.Parser.Event.EventCounterParser]”的服务。)

所以我尝试使用指定的注册类型

1[GenericDITest.Pa
  rser.Event.EventLevelParser]'.) (Error while validating the service descriptor 'ServiceType: GenericDITest.Handler.IHandler Lifetime: Scoped ImplementationType: GenericDITest.Handler.Event.EventHandler

但是我得到了错误

  

System.ArgumentException:无法实例化服务类型'GenericDITest.Handler.IHandler'的实现类型'GenericDITest.Handler.Event.EventHandler`1 [T]'。

可悲的是,我对泛型的了解还不足以真正解决此问题。我不太了解错误消息。也许我定义的服务错误?

其他用户的尝试

就像@ user1672994一样,我进行了以下更改

1[GenericDITest.Parser.Event.EventCounterParser]' while attempting to activate 'GenericDITest.Handler.Event.EventHandlerservices.AddScoped(typeof(IHandler), typeof(Handler.Event.EventHandler<>)); services.AddScoped(typeof(IServiceFactory<>), typeof(EventHandlerFactory<>));
IHandlerIHandler<out T> EventHandler<T> : IHandlerEventHandler<T> : IHandler<T>
EventHandlerFactory<T> : IServiceFactory<IHandler>EventHandlerFactory<T> : IServiceFactory<IHandler<T>>

现在,我可以将服务添加为public IHandler Create(string key),这将立即启动应用程序,而不会出现任何错误,但是由于所有泛型,我现在还必须像这样注册我的工厂

public IHandler<T> Create(string key)

但是,当我通过services.AddScoped(typeof(IHandler<>), typeof(Handler.Event.EventHandler<>));访问工厂时,出现了新错误。 services.AddScoped(typeof(IServiceFactory<>), typeof(EventHandlerFactory<>)); 1 [T]'无法转换为服务类型'GenericDITest.Service.IServiceFactory var service = provider.GetService(typeof(IServiceFactory<>));。但这是我不明白的,因为它是相同的界面?

我还将该项目上传到了文件托管服务商,因此您可以检出它:https://mega.nz/#!jf51UALL!sbVrGSXoTtfzZLzOl9i7wwa-OC5s3xlhuf_RJuorIQs

1 个答案:

答案 0 :(得分:1)

  

EventHandler将获得IParser的任何实现

这不是泛型的工作原理。 IParser<T>是与某些IParser完全分开的类型。除非您之间确实有类型层次结构,否则它们是完全无关的。泛型在这里可能会造成混淆,因为泛型实际上并不是真正存在的类型。只有当您应用具体的泛型类型参数时,才存在类型。

因此IParser<EventLevel>IParser<EventCounter>都是实类型(虽然是完全分离的和不相关的!),但是这些类型的通用“模板” {{1} },不是具体类型。

您还必须了解,在泛型类中,泛型类型参数IParser<T>始终指代完全相同的事物。这基本上是一个常量类型,将在某个时刻插入。

因此,您的T可能有一个EventHandlerFactory<T>,但是如果您将工厂用作IEnumerable<IParser<T>>,则该类型将有一个EventHandlerFactory<EventCounter>。因此,这也是通过依赖项注入解决它时将要寻找的类型。因此,它无法获得IEnumerable<IParser<EventCounter>>个实例,因为这些实例与它所请求的类型无关。

IParser<EventLevel>只会注入EventHandlerFactory<EventCounter>的解析器,而不会注入其他解析器(可能不是您要尝试的解析器)。而且,除了使用该类型为EventCounter创建处理程序外,您还必须使用EventLevel类型的不同工厂(它有自己的{ {1}}。


我尚不清楚您到底想完成什么,尤其是因为您的示例中的实现都是空的,但是如果我猜测,我会假设您在这里使用了太多的泛型。因此,从一开始,这些就是我的假设:

  • 您有多个可以使用不同类型的解析器,但每个解析器只能使用一种。
  • 您只有一个事件处理程序类型,该类型使用兼容的解析器来处理某个类型的事件。
  • 您想要一个单个工厂,该工厂允许您使用正确的事件处理程序来处理密钥。

现在,您的方法存在一个基本问题:解析器返回具体类型的列表,例如您的EventHandlerFactory<EventLevel>返回EventLevel对象的列表。如果现在要在事件处理程序中使用该对象,则需要 单独的事件处理程序类型以分别处理每个结果。否则,您将没有通用信息可在通用事件处理程序中使用。

这里正确的解决方案在很大程度上取决于您要对解析器和事件处理程序进行的处理。有了我们掌握的信息,很难解决。

但是,如果我们忽略解析器的那部分,例如事件处理程序将仅以非通用方式使用解析器,则可以将设置减少到以下内容:

EventCounterParser
EventCounter