【问题标题】:Resolve IEnumerable of All Types that Implement Generic Interface in Autofac解决 Autofac 中实现通用接口的所有类型的 IEnumerable
【发布时间】:2020-08-06 13:41:38
【问题描述】:

我希望能够解析来自 Autofac 的服务集合,这些服务代表所有实现开放通用接口的注册类型。

public interface IEntityService<in T> where T : Entity
{
    void DoEntityWork(T entity);
}

我有许多继承 Entity 的类和许多为该实体实现 IEntityService 的相应服务类。

public class EntityA : Entity { }
public class EntityB : Entity { }
public class EntityC : Entity { }

public class EntityAService : IEntityService<EntityA>
{
    public void DoEntityWork(EntityA entity)
}

public class EntityBService : IEntityService<EntityB>
{
    public void DoEntityWork(EntityB entity)
}

public class EntityCService : IEntityService<EntityC>
{
    public void DoEntityWork(EntityB entity)
}

这是我使用 Autofac 注册它们的方式:

builder.RegisterType<EntityAService>().As<IEntityService<EntityA>();
builder.RegisterType<EntityBService>().As<IEntityService<EntityB>();
builder.RegisterType<EntityCService>().As<IEntityService<EntityC>();

我希望能够解决集合中的每个IEntityService 注册。但是,尝试使用以下代码注入它们会返回一个空集合:

public class MyProcessingClass(IEnumerable<IEntityService<Entity>> entityServices)
{
    _entityServices = entityServices;
}

我尝试将它们全部注册As&lt;IEntityService&lt;Entity&gt;&gt;(),但这会引发ArgumentException,并显示以下消息:

The type 'MyProject.Services.EntityAService' is not assignable to service 'MyProject.Interfaces.IEntityService'1[[MyProject.Models.Entity]]

如何使用实现Entity 的类型参数解析所有实现IEntityService 的类型?

【问题讨论】:

  • 有人已经提到了逆变,所以我将插话 Autofac 部分。我不相信 Autofac 支持解析所有类型的开放通用接口 (IEntityService)。您必须解析接口的封闭类型 (IEntityService。如果您的用例允许,您可以考虑使用非泛型接口。然后您可以解析实现非泛型接口的所有类型的集合。跨度>
  • @jandrew Guru Stron 也提到了使用非通用接口。自从提出建议以来,我一直在尝试这个想法,但尚未确定如何在保持特定类型使用的同时实现该想法。

标签: c# .net-core autofac


【解决方案1】:

Autofac 不支持这种开箱即用的东西。有一个ContravariantRegistrationSource,但它确实与此相反——如果您注册IEntityService&lt;Entity&gt;,然后解析IEntityService&lt;EntityA&gt;,它将为您解析IEntityService&lt;Entity&gt; 实例。它不允许您解析 IEnumerable&lt;IEntityService&lt;Entity&gt;&gt; 并定位所有派生类型。

【讨论】:

  • 感谢您对此的回复。我基于此响应和其他响应尝试了其他一些解决方法,但没有一个可以让我以我想要的方式解决我的封闭类型。我现在将坚持单独解决它们,直到我可以弄清楚如何制作自己的 RegistrationSource 来完成此任务或将我的设计更改为不需要它。
【解决方案2】:

这就是 contravariance 在 C# 中的工作方式,EntityAService 不是 IEntityService&lt;Entity&gt;,这种关系是另一种方式:

public class EntityService : IEntityService<Entity>
{
    public void DoEntityWork(Entity entity) { }
}

IEntityService<EntityA> x = new EntityService();

一种解决方法是引入IEntityService 的非通用版本,将所有实体服务注册为该版本并解析IEnumerable&lt;IEntityService&gt;

【讨论】:

  • 我对软件开发还比较陌生,所以请原谅我的无知...我使用IEntityService&lt;T&gt; where T : Entity 的目的是强制实现者指定该类将处理的特定Entity 类型然后只能在实现中对特定的Entity 类型进行操作。非泛型 IEntityService 只允许我将每个成员限制为 Entity 而不是更派生的类型。我想错了吗?我确信有更好的方法来完成我想要完成的工作。
  • @draconastar 是的,你的想法是正确的。想法是服务应该实现两个接口 - 通用用于单一接口使用和非通用 IEnumerable&lt;IEntityService&lt;Entity&gt;&gt;IEntityService&lt;Entity&gt; 在这种情况下可以替换为非通用版本)。
  • 非常感谢您的意见!我觉得我懂了。如果我要创建一个非通用的IEntityService 接口,那么我可以同时实现这两个接口并将其作为集合检索。我的问题是,既然我将它们检索为非泛型接口,那么我是否仅限于调用非泛型接口方法?我真的需要对通用接口成员的特定访问来以不同方式处理每个实体。
  • @draconastar 你想通过解决服务集合来实现什么?如果您需要使用泛型接口,则单独解析每个封闭类型更有意义。即IEntityService&lt;EntityA&gt;IEntityService&lt;EntityB&gt;等)。
  • @jandrew 实际上,我有 30 个 Entity 类型。目前我正在按照您所说的进行并单独解决每个问题,但是它们被注入的类有 30 个字段、30 个构造函数参数等。我希望能够清理它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多