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

时间:2018-12-30 21:45:22

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

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

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

然后每种宠物都有多个控制器,即DogControllerCatControllerHamsterController。所有这些都从BasePetController继承而来,而/dog/1234实际上正在完成所有繁重的工作。在您对DogController执行操作但PetId 1234实际上对应于仓鼠的情况下,我希望BasePetController : IResourceController { BasePetController(IResourceController[] resourceControllers){} abstract Type SupportedResourceType { get; } } DogController : BasePetController { DogController(IResourceController[] resourceControllers) : base(resourceControllers) {} Type SupportedResourceType => typeof(Dog); } 返回适当的响应(在Get上重定向,在更新上确认等)。 / p>

所以我有这样的东西:

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

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

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 } 来进一步分离冲突,但没有骰子。

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

ResourceControllerSelector

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

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

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

scala> println(spark.version)
2.4.0

val sq = spark.readStream.format("rate").load
scala> :type sq
org.apache.spark.sql.DataFrame

scala> assert(sq.isStreaming)

scala> sq.writeStream.format("hive").start
org.apache.spark.sql.AnalysisException: Hive data source can only be used with tables, you can not write files of Hive data source directly.;
  at org.apache.spark.sql.streaming.DataStreamWriter.start(DataStreamWriter.scala:246)
  ... 49 elided

2 个答案:

答案 0 :(得分:1)

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

回答问题

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

解决我的问题

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

我像这样装饰每个资源控制器:     [ResourceController(Type = typeof(Dog),ControllerName =“ Dog”)]     公共类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);
}

感谢大家的帮助。我真的很感激,您让我意识到我在吠错一棵树!

答案 1 :(得分:0)

我想我能解决你的问题。

该解决方案很简单,只需为每种宠物混凝土类型创建存根接口即可,如下所示。每个控制器将使用自己的资源。

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