1

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. 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. So for example navigating to /pet/1234 will give you a summary of the pet's details and what type of pet it is.

I then have a number of controllers for each pet type I.e. DogController, CatController and HamsterController. All of these inherit from a BasePetController which is actually doing all of the heavy lifting. 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).

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:

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.

I have tried to inject a ResourceControllerSelector to separate out this conflict a little further but no dice.

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.

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.

Adding implementation of the ResourceControllerSelector where resourceControllers is 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.");
}
8
  • 1
    I'd be interested in seeing your elaboration of ResourceControllerSelector as that seems about right to me. If asking for an instance of IResourceController I'd expect only one type to be resolved. Commented Dec 30, 2018 at 21:50
  • not the same, but this seems to be similar to what you are trying: stackoverflow.com/a/53642638/991609 Commented Dec 30, 2018 at 21:56
  • Yes, the ResourceControllerSelector does seem like the right way to go if you don't change how the code is structured. Unfortunately since we don't know your problem domain, we can't really help with that. Commented Dec 31, 2018 at 1:02
  • @KrikWoll The resource controller selector would look something like this (I'll add it to the original question) Commented Dec 31, 2018 at 10:58
  • @DavidG I don't know how to inject both the ResourceControllerSelector into the controllers, but also the controllers into the ResourceControllerSelector Commented Dec 31, 2018 at 11:04

2 Answers 2

1

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. However with things like Autofac and/or SimpleInjector you can do conditional injection or context driven injection. 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. In my mind that meant injecting the other controllers so that it could query them for information (I.e. find the actionName and controllerName of the correct action). 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

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.

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!

Sign up to request clarification or add additional context in comments.

Comments

0

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)

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

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

6 Comments

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)
I have edited my answer, look at lask edit. Is it what you wanted?
Cheers for the update, I think the problem is that I need to inject the controller itself and I don't think I can separate it out like this because what the controller needs access to is the CatControllers update method for example. I feel like I'm trying to solve something that shouldn't really be a problem
I dont fill why are you going to do this, it should't be done like this. Decide what you want.
If it comes from proper design perspective in me opinion you have two options. 1. Create many controllers for concrete pet (so you will have many endpoints cat, pet controller) and each controler will have its own implementation of resource. 2. Create one controller for 'pet' and add layer which will determine what resource to user Now i fill that you want to have both but with addition , isnt it a little bit wrong? I dont think that injecting another controler to controler is good idea besides it is imposible how you have saw.Could explain me your problem a little bit more precisely.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.