This project is read-only.

Question about Resolving List of Generics

Jun 27, 2014 at 5:41 AM
In the system I'm working on, I have a list of 20 or so generic handlers, each of which handles a specific type of Product. When a request comes in, I need to dispatch to the correct handler based on the type of product. I've been going through the documentation and am not putting together exactly how I would implement this.

Previously I had a non-generic interface (and a different design) and was relying on something like IEnumerable<IStockChecker> being injected, but now that the stock checker needs to be generic, I'm not sure how to implement it.

In the array section, you mention that it's generally not a great idea to depend on a collection of dependencies, but I don't see how I could refactor to depend on a composite if the types are open generics.

I'm sure I'm missing something that is not allowing it to click, but I appreciate any input you might have!
Thanks!
-Shane
Jun 27, 2014 at 6:04 AM
Can you post the appropriate code to help us visualize what you're trying to achieve? How do your absrrsctons look like, how should they be called, how are you registering them, etc.
Jun 27, 2014 at 6:11 AM
Here's some example code that hopefully will communicate what I'm going for. I know I can implement this with an abstract factory, but I don't want to have to change it if I add a new vendor.
// registration
container.RegisterManyForOpenGeneric(typeof(IVendorStockCheck<>), typeof(IVendorStockCheck<>).Assembly);

// example stock checker
public class SampleVendorStockChecker : IVendorStockChecker<SampleProduct> {}

// class that handles incoming requests
public class RequestHandler
{
    public List<Result> CheckStock(List<StockCheck> checks)
    {
        // do some processing to figure out that the first check is for SampleVendor
        // here I would want to look through my registered IVendorStockCheck and find the one that handles SampleProduct
    }
}
Jun 27, 2014 at 6:19 AM
So you'll have exactly one handler for reach T? Why don't you inject an IVendorStockChecker<SampleProduct> in your RequestHandler? Perhaps I'm stilk missing something in what you're trying to achieve.
Jun 27, 2014 at 6:35 AM
Right, there's just one handler for each T.
This is a WebAPI app where the RequestHandler is essentially a controller - maybe I should have named it that way to be more clear. There's more abstraction than that, but that's the idea. So in each request, based on the data I need to figure out at runtime which StockChecker to use to process the request. So the request handler needs to know about all StockCheckers (directly or indirectly).
Does that make more sense? Sorry if it's not very clear.
Jun 27, 2014 at 7:07 AM
So you mean that the RequestHandler class will handle any type of T, not just the SampleProject?
Jun 27, 2014 at 7:15 AM
Edited Jun 27, 2014 at 7:15 AM
Right - I have 20 or so different Stock Checkers, and based on the data in the request I need to figure out which one to use.
// example stock checkers
public class VendorOneStockChecker : IVendorStockChecker<VendorOne> {}
public class VendorTwoStockChecker : IVendorStockChecker<VendorTwo> {}
public class VendorThreeStockChecker : IVendorStockChecker<VendorThree> {}
public class VendorFourStockChecker : IVendorStockChecker<VendorFour> {}
public class VendorFiveStockChecker : IVendorStockChecker<VendorFive> {}

public class ApiController
{
    // either a list, or factory or something that will allow me access to the IVendorStockCheckers that have been registered
    public List<Result> CheckStock(List<StockCheck> checks)
    {
         foreach (var check in checks)
         {
             // I figure out which vendor this check should be for
             // ** Here I need to get the stock checker instance corresponding to the vendor for this check and call a method on it to handle the check
         }
    }
}
Jun 27, 2014 at 7:59 AM
Can you post the definition of your IVendorStockChecker<T>?
Jun 27, 2014 at 3:02 PM
The T isn't actually used, but is just metadata to tie in a decorator that does require T. So maybe there's a better solution for that if I make the interface non-generic.
    public interface IVendorStockCheck<T>
    {
        Task<List<ProductStockInfo>> CheckStockAsync(List<StockCheck> stockChecks);
        Vendor Vendor { get; }
        StockCapabilities StockCapabilities { get; }
    }
Jun 27, 2014 at 3:37 PM
I have a few instances of dummy generics that were being used to tie together the correct classes for different actions, but I'm thinking it may be better to get rid of all the dummy generics and have some of those classes depend on a list (or a wrapper class), and then pull the correct one out based on it's Vendor key.
Jun 27, 2014 at 3:40 PM
Edited Jun 28, 2014 at 1:35 PM
You need some sort of mediator that will be able to resolve the correct IVendorStackCheck<T> and operate on it. For instance:
sealed class VendorStockCheckMediator : IVendorStockCheckMediator
{
    private readonly Container container;

    public VendorStockCheckMediator(Container container)
    {
        this.container = container;
    }

    public StockCapabilities GetStockCapabilities(StockCheck check)
    {
        Type checkerType =typeof(IVendorStockCheck<>).MakeGenericType(check.GetType());

        // Notice the non-generic IVendorStockCheck interface. IVendorStockCheck<T> should 
        // inherit from this.
        var checker = (IVendorStockCheck)container.GetInstance(checkerType);

        return checker.StockCapabilities;
    }
}
This VendorStockCheckMediator only implements some infrastructure logic (no business logic) and you can place this implementation inside your composition root. You can inject an IVendorStockCheckMediator into your controller and use it there.
Marked as answer by dot_NET_Junkie on 8/11/2014 at 12:36 PM
Jun 28, 2014 at 7:53 PM
Edited Jun 28, 2014 at 7:53 PM
Great - that set me on the right track. Thanks for your feedback and all your work on Simple Injector!
Jun 28, 2014 at 8:45 PM
Also take a look at the QueryProcessor implementation from this article. It shows a mediator that uses dynamic typing to do the dispatching on your behalf.