简体   繁体   English

如何将一个接口的多个实现注入一个实现中

[英]How to Inject multiple Implementations of an Interface into one Implementation

I am trying to inject a collection of implementations of IResourceService into another implementation of IResourceService so that the first implementation is able to redirect to the correct service in the case where it can't operate on the resource.我试图注入的实现集合IResourceService进入另一个实施IResourceService使得第一实现能够重定向到正确的服务在其无法对资源进行操作的情况。 Every implementation of this I have tried so far has lead to a Cyclic Dependency - so I either want to know how to work around this or how to design this so that it isn't a problem.到目前为止,我尝试过的每一个实现都导致了循环依赖——所以我要么想知道如何解决这个问题,要么想知道如何设计它以便它不是问题。

I'm creating a number of controllers that all look after different resources.我正在创建许多控制器,它们都负责处理不同的资源。 I'll simplify for the case of this question - but in effect I have a PetController that is able to do simple operations on pets which are all provided Ids.我将简化这个问题的情况 - 但实际上我有一个PetController能够对所有提供 Id 的宠物进行简单的操作。 So for example navigating to /pet/1234 will give you a summary of the pet's details and what type of pet it is.例如,导航到/pet/1234将为您提供宠物详细信息的摘要以及它是什么类型的宠物。

I then have a number of controllers for each pet type Ie DogController , CatController and HamsterController .然后我为每种宠物类型设置了许多控制器,即DogControllerCatControllerHamsterController All of these inherit from a BasePetController which is actually doing all of the heavy lifting.所有这些都继承自一个BasePetController ,它实际上完成了所有繁重的工作。 In the scenario where you perform an operation on /dog/1234 but PetId 1234 actually corresponds to a Hamster, I want the DogController to return an appropriate response (Redirect on a Get, Confict on an update etc).在您对/dog/1234执行操作但 PetId 1234 实际上对应于仓鼠的DogController ,我希望DogController返回适当的响应(获取时重定向、更新时发生冲突等)。

So I have something like this:所以我有这样的事情:

BasePetController : IResourceController
{
    BasePetController(IResourceController[] resourceControllers){}

    abstract Type SupportedResourceType { get; }
}

DogController : BasePetController
{
     DogController(IResourceController[] resourceControllers) : base(resourceControllers) {}

     Type SupportedResourceType => typeof(Dog);
}

This is then added to DI like so:然后将其添加到 DI 中,如下所示:

services.AddSingleton<IResourceController, DogController>();
services.AddSingleton<IResourceController, CatController>(); // etc.

This leads to a cyclic dependency because the DogController needs itself in order to create itself effectively.这会导致循环依赖,因为DogController需要自己才能有效地创建自己。

I have tried to inject a ResourceControllerSelector to separate out this conflict a little further but no dice.我试图注入一个ResourceControllerSelector来进一步分离这个冲突,但没有骰子。

What I want is for the controller to go (In Pseudo Code)我想要的是控制器去(在伪代码中)

If (can't update dog because it isn't a dog) {
    controller = getResourceControllerForPet()
    action = controller.GetActionForUpdateOperation()
    return information for the user to go to the correct action
}

I think the individual pet controllers should be responsible for knowing whether or not that can handle a specific resource - rather than another service knowing all of this information + which actions to call etc. But can't figure out how to give each pet controller the ability to go and get this information themselves and/or in the API layer without creating a cyclic dependency.我认为单个宠物控制器应该负责知道它是否可以处理特定资源 - 而不是另一个服务知道所有这些信息 + 调用哪些操作等。但无法弄清楚如何给每个宠物控制器能够自己和/或在 API 层中获取这些信息,而无需创建循环依赖。

Any help on this would be much appreciated.对此的任何帮助将不胜感激。 Whether its help with the dependency injection problem, or the design of the services required or even the API itself.无论是对依赖注入问题的帮助,还是对所需服务的设计甚至 API 本身的帮助。

Adding implementation of the ResourceControllerSelector where resourceControllers is an IEnumerable<IResourceController> .添加ResourceControllerSelector实现,其中resourceControllers是一个IEnumerable<IResourceController>

public IResourceController SelectController(Type resourceType)
{
    EnsureArg.IsNotNull(resourceType, nameof(resourceType));

    return this.resourceControllers.SingleOrDefault(x => x.SupportedResourceType == resourceType)
        ?? throw new ArgumentOutOfRangeException(nameof(resourceType), resourceType, "No resource controller has been configured to handle the provided resource.");
}

I think this question has diverged, there is the question itself (which was my first attempt at solving the problem I had) and then the problem I had.我认为这个问题有分歧,有问题本身(这是我第一次尝试解决我遇到的问题),然后是我遇到的问题。 So I thought I'd update the question with what I have learned.所以我想我会用我学到的东西来更新这个问题。

Answering the question回答问题

Short answer is this isn't possible with the ASP.Net standard dependency injection, it isn't advanced enough.简短的回答是 ASP.Net 标准依赖注入是不可能的,它不够先进。 However with things like Autofac and/or SimpleInjector you can do conditional injection or context driven injection.但是,使用 Autofac 和/或 SimpleInjector 之类的东西,您可以进行条件注入或上下文驱动注入。 In this world you can say inject all Interface implementations that don't meet a certain criteria.在这个世界中,您可以说注入所有不符合特定标准的接口实现。 So you can say don't inject if the implementation matches the on you are trying to create at the moment.因此,您可以说如果实现与您目前尝试创建的实现相匹配,则不要注入。 I haven't tried this in anger but in theory this would get around the problem我没有在愤怒中尝试过这个,但理论上这可以解决问题

Solving my problem解决我的问题

The reason I wanted this was so that I could return accurate SeeOther (303) responses if you tried to perform an action using the DogController on a Hamster.我想SeeOther的原因是,如果您尝试在仓鼠上使用DogController执行操作,我可以返回准确的SeeOther (303) 响应。 In my mind that meant injecting the other controllers so that it could query them for information (Ie find the actionName and controllerName of the correct action).在我看来,这意味着注入其他控制器,以便它可以查询它们的信息(即找到正确操作的 actionName 和 controllerName)。 However another way is to use reflection and attributes to denote what the controller is responsible for.然而,另一种方法是使用反射和属性来表示控制器负责什么。 So I instead went with something like this:所以我改为使用这样的方法:

I decorate each resource controller like so: [ResourceController(Type = typeof(Dog), ControllerName = "Dog")] public class DogController : BasePetController我像这样装饰每个资源控制器: [ResourceController(Type = typeof(Dog), ControllerName = "Dog")] public class DogController : BasePetController

And then I have a helper class here called SeeOtherResultResolver that scans the repository for the correct controller and then for the right HttpGet or HttpDelete etc attribute.然后我在这里有一个名为SeeOtherResultResolver的帮助程序类,它扫描存储库以获取正确的控制器,然后扫描正确的HttpGetHttpDelete等属性。

private static readonly IDictionary<string, Type> AttributeMap
     = new Dictionary<string, Type>
     {
         { "GET", typeof(HttpGetAttribute) },
         { "DELETE", typeof(HttpDeleteAttribute) },
         { "PUT", typeof(HttpPutAttribute) },
     };

public string ResolveForSeeOtherResult(IUrlHelper urlHelper, object resource, object routeValues = null)
{
    string scheme = urlHelper.ActionContext.HttpContext.Request.Scheme;
    var httpAttribute = AttributeMap[urlHelper.ActionContext.HttpContext.Request.Method];

    var resourceControllerTypes = resource.GetType().Assembly.GetTypes()
            .Where(type => type.IsDefined(typeof(ResourceControllerAttribute), false));

    foreach (var type in resourceControllerTypes)
    {
        var resourceControllerAttribute = type.GetCustomAttributes(false).OfType<ResourceControllerAttribute>().Single();

        if (resourceControllerAttribute.Value == resource.GetType())
        {
            var methodInfo = type.GetMethods().Where(x => x.IsDefined(httpAttribute, false)).Single();

            var result = urlHelper.Action(methodInfo.Name, resourceControllerAttribute.ControllerName, routeValues, scheme);

            return result;
         }
    }

    var errorMessage = string.Format(ErrorMessages.UnrecognisedResourceType, resource.GetType());

    throw new InvalidOperationException(errorMessage);
}

Thanks for the help everyone.谢谢大家的帮助。 I really do appreciate it, you made me realise I was barking up the wrong tree a little bit!我真的很感激,你让我意识到我有点不对劲!

i think i get your problem.我想我明白你的问题了。

The solution is simple just create stubbed interfaces for each pet concrete type so it will look like this.解决方案很简单,只需为每个宠物具体类型创建存根接口,使其看起来像这样。 And each controller will use its own resource.每个控制器将使用自己的资源。

public DogController {
public DogController(IDogResource dogResource)
}

public CatController {
   public CatController(ICatResource dogResource)
}

public interface IDogResource : IResource
{
}

public interface ICatResource : IResource
{
}

public interface IResource{
  //your common logic
}

services.AddSingleton<DogResource, IDogResource>();
services.AddSingleton<CatResource, ICatResource>();

**edit in case, if you have some common logic in base controller you need to modify above code to this form and the rest is same **编辑以防万一,如果您在基本控制器中有一些通用逻辑,则需要将上面的代码修改为这种形式,其余部分相同

public BasePetController  {
  protected IResource resource;
  public BasePetController (IResource resource)
  {
      this.resource = resource;
  }
}

public DogController : BasePetController 
{
  public DogController(IDogResource dogResource): base (dogResource)
}

public CatController  : BasePetController
{
  public CatController(ICatResource dogResource): base (dogResource)
}

** one more edit ** 再编辑一次

Thanks for the response but this isn't quite what I was looking for - what I effectively want here is to inject ICatResource and IHamsterResource into the DogController and the Dog+Hamster into the Cat one etc. But I wanted that done automatically (in theory it doesn't matter if the Dog one gets itself)感谢您的回复,但这并不是我想要的 - 我在这里实际上想要的是将 ICatResource 和 IHamsterResource 注入 DogController 并将 Dog+Hamster 注入 Cat 等等。但我希望自动完成(理论上)如果狗得到自己并不重要)

So if you want do to smth like this just take the above code and change it.因此,如果您想这样做,只需使用上面的代码并进行更改即可。 Dont use concrete interface types like IDogResource etc, only use IResource, and the code will look like this不要使用IDogResource等具体的接口类型,只使用IResource,代码看起来像这样

public class BasePetController  {
  protected IResource[] resources;
  public BasePetController (IResource[] resources)
  {
    this.resources = resources;
  }
}

public class DogController : BasePetController 
{
  public DogController(IResource[] resources): base (resources)
}

public class CatController  : BasePetController
{
  public CatController(IResource[] resources): base (resources)
}

public class DogResource : IResource 
{

}
public class DogResource : IResource 
{

}
public interface IResource{

}

services.AddSingleton<DogResource, IResource>();
services.AddSingleton<CatResource, IResource>();

暂无
暂无

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

相关问题 如何从公共接口注入Spring.NET的多个实现? - How to inject with Spring.NET multiple implementations from common interface? 一个接口与 DI 的多种实现 - Multiple implementations for one interface with DI IoC / DI:当存在同一接口的多个实现时,如何注入特定实例 - IoC/DI: How to inject specific instance when there are multiple implementations of same interface C#-一个接口,两个实现:如何将方法调用从一个实现重定向到另一个? - C# - One Interface, two implementations: How to redirect a method call from one implementation to another? 在多个构造函数参数中注入具有相同接口的不同实现 - Inject different implementations that have same interface in multiple constructor parameters 温莎城堡:我如何将接口的所有实现注入ctor? - Castle Windsor: How do I inject all implementations of interface into a ctor? 如何在没有条件/上下文DI的情况下将不同的实现注入相同的接口? - How to inject different implementations to same interface without conditional/context DI? 将不同的实现注入相同的接口,然后在正确的项目/程序集中选择正确的实现 - Inject different implementations into same interface then pick up right implementation in right project / assembly 如何统一解决一个接口的所有实现? - How to resolve all implementations of one interface in unity? C#如何使用多个接口实现 - C# How to use multiple interface implementations
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM