【问题标题】:Covariant service resolution by IoC containerIoC 容器的协变服务解析
【发布时间】:2018-06-26 09:31:25
【问题描述】:

我在没有任何 IoC 容器的情况下为我的应用程序使用依赖注入模式。现在我决定使用一些 IoC 容器,因为我的 Composition Root 包含数千行代码,但我未能使其与我的类一起工作,这些类积极使用方差。比如下面这个界面

public interface IQuery<in TIn, out TOut>
{
    IReadOnlyCollection<TOut> Get(TIn key);
}

和服务

public class FakeRepository : IQuery<object, string>
{
    public IReadOnlyCollection<string> Get(object key)
    {
        return new[] { key.ToString() };
    }
}

纯 DI 工作正常

IQuery<string, object> service = new FakeRepository();

但是 Autofac 和 DryIoc 都无法解决它。

service = autofacContainer.Resolve<IQuery<string, object>>(); // exception
service = dryIocContainer.Resolve<IQuery<string, object>>(); // exception

我需要一些额外的设置吗?是否有任何其他 IoC 容器支持这一点?我是不是要求太多了?

完整代码:https://dotnetfiddle.net/vlw17R

【问题讨论】:

  • 如何在 autofac 或 dryloc 中注册实现类?标准的 dotnet 核心 ServiceCollection 有一个 .AddSingleton&lt;TInterface, TImplementation&gt;() 方法,只有在查询到 TInterface 时才会解析实现类。还有一个.AddSingleton&lt;TImplementation&gt; 助手,它只调用AddSingleton&lt;TImplementation, TImplementation&gt;()
  • 我打算使用基于约定的注册。 autofacBuilder.RegisterAssemblyTypes(typeof(FakeRepository).Assembly).AsImplementedInterfaces()dryIocContainer.RegisterMany(new[] { typeof(FakeRepository).Assembly })
  • 协变和逆变的使用通常在解析服务集合时很有用,例如在发布事件时,超类型的处理程序也应该处理已发布的事件。但是,非收集解决方案(正如您所做的那样)对共同/对立差异的需求更为罕见,尤其是容器无法为您开箱即用的事情,因为它不知道如何在有多个可分配服务需要解决时做出响应。
  • 您能否解释一下为什么在您的特定情况下需要协方差(与仅解析实际类型相比)并描述您希望容器如何处理重复分配,或者保证永远不会有任何重复/重叠?
  • 我目前的服务遵循“返回最具体的类型,接受最通用的类​​型”的原则。例如Authorizer(IAuthenticator&lt;IIdentity&gt; authenticator){...}public class WindowsAuthenticator: IAuthenticator&lt;WindowsIdentity&gt;。要使用 IoC 容器,我必须将构造函数更改为 Authorizer(IAuthenticator&lt;WindowsIdentity&gt; authenticator){...}。不仅工作量大,还让服务耦合度更高!

标签: c# autofac ioc-container dryioc


【解决方案1】:

它不适用于当前的 DryIoc 版本(稳定版v2.12.6 和预览版v3.0.0-preview-03)。

但是理论上没有任何东西可以阻止将开放通用服务类型注册为封闭或非通用实现类型。

对于单个服务到实现的注册:

container.Register(typeof(IQuery<,>), typeof(FakeRepository));

DryIoc 将由于对已实现类型的内部检查而引发异常。如果我调整检查以包括服务类型的开放通用版本,那么这可行:

container.Resolve<IQuery<string, object>>();

可以进行类似的调整以在RegisterMany 中包含开放通用服务类型。

但是剩下的问题是ResolveMany。这只是一个实现细节,但同时具有封闭和开放通用版本的服务可能在解析集合时会产生两个实例。

最后,我在 DryIoc 跟踪器中创建了an issue,并将考虑如何以安全的方式启用它。

更新

新的DryIoc v2.12.7Register 一起发布,能够注册开放通用服务类型。但不是RegisterMany。查看issue了解更多详情。

【讨论】:

  • 听起来不错!我可以通过一行代码注册所有服务,还是必须手动注册每项服务?
  • 理想情况下,一行。在更坏的情况下,RegisterMany 加上特定的Register 调用替换变体注册。
  • 更新了我的答案。
  • 我印象深刻!感谢那。这是使用此功能更新的基于约定的注册:dotnetfiddle.net/BDgdwu
猜你喜欢
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 2011-11-14
相关资源
最近更新 更多