繁体   English   中英

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

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

我试图注入的实现集合IResourceService进入另一个实施IResourceService使得第一实现能够重定向到正确的服务在其无法对资源进行操作的情况。 到目前为止,我尝试过的每一个实现都导致了循环依赖——所以我要么想知道如何解决这个问题,要么想知道如何设计它以便它不是问题。

我正在创建许多控制器,它们都负责处理不同的资源。 我将简化这个问题的情况 - 但实际上我有一个PetController能够对所有提供 Id 的宠物进行简单的操作。 例如,导航到/pet/1234将为您提供宠物详细信息的摘要以及它是什么类型的宠物。

然后我为每种宠物类型设置了许多控制器,即DogControllerCatControllerHamsterController 所有这些都继承自一个BasePetController ,它实际上完成了所有繁重的工作。 在您对/dog/1234执行操作但 PetId 1234 实际上对应于仓鼠的DogController ,我希望DogController返回适当的响应(获取时重定向、更新时发生冲突等)。

所以我有这样的事情:

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

    abstract Type SupportedResourceType { get; }
}

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

     Type SupportedResourceType => typeof(Dog);
}

然后将其添加到 DI 中,如下所示:

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

这会导致循环依赖,因为DogController需要自己才能有效地创建自己。

我试图注入一个ResourceControllerSelector来进一步分离这个冲突,但没有骰子。

我想要的是控制器去(在伪代码中)

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
}

我认为单个宠物控制器应该负责知道它是否可以处理特定资源 - 而不是另一个服务知道所有这些信息 + 调用哪些操作等。但无法弄清楚如何给每个宠物控制器能够自己和/或在 API 层中获取这些信息,而无需创建循环依赖。

对此的任何帮助将不胜感激。 无论是对依赖注入问题的帮助,还是对所需服务的设计甚至 API 本身的帮助。

添加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.");
}

我认为这个问题有分歧,有问题本身(这是我第一次尝试解决我遇到的问题),然后是我遇到的问题。 所以我想我会用我学到的东西来更新这个问题。

回答问题

简短的回答是 ASP.Net 标准依赖注入是不可能的,它不够先进。 但是,使用 Autofac 和/或 SimpleInjector 之类的东西,您可以进行条件注入或上下文驱动注入。 在这个世界中,您可以说注入所有不符合特定标准的接口实现。 因此,您可以说如果实现与您目前尝试创建的实现相匹配,则不要注入。 我没有在愤怒中尝试过这个,但理论上这可以解决问题

解决我的问题

我想SeeOther的原因是,如果您尝试在仓鼠上使用DogController执行操作,我可以返回准确的SeeOther (303) 响应。 在我看来,这意味着注入其他控制器,以便它可以查询它们的信息(即找到正确操作的 actionName 和 controllerName)。 然而,另一种方法是使用反射和属性来表示控制器负责什么。 所以我改为使用这样的方法:

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

然后我在这里有一个名为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);
}

谢谢大家的帮助。 我真的很感激,你让我意识到我有点不对劲!

我想我明白你的问题了。

解决方案很简单,只需为每个宠物具体类型创建存根接口,使其看起来像这样。 每个控制器将使用自己的资源。

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>();

**编辑以防万一,如果您在基本控制器中有一些通用逻辑,则需要将上面的代码修改为这种形式,其余部分相同

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)
}

** 再编辑一次

感谢您的回复,但这并不是我想要的 - 我在这里实际上想要的是将 ICatResource 和 IHamsterResource 注入 DogController 并将 Dog+Hamster 注入 Cat 等等。但我希望自动完成(理论上)如果狗得到自己并不重要)

因此,如果您想这样做,只需使用上面的代码并进行更改即可。 不要使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM