首页 > 解决方案 > 如何将一个接口的多个实现注入到一个实现中

问题描述

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

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

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

所以我有这样的事情:

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 本身。

添加ResourceControllerSelectorwhere resourceControllersis an 的实现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.");
}

标签: c#asp.netdependency-injection.net-core

解决方案


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

回答问题

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

解决我的问题

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

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

然后我在这里有一个助手类SeeOtherResultResolver,它会扫描存储库中的正确控制器,然后扫描正确的HttpGetHttpDeleteetc 属性。

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

感谢大家的帮助。我真的很感激,你让我意识到我在叫错树了!


推荐阅读