This project is read-only.

Exception when attempting to register unimplemented Open Generic

Feb 4, 2015 at 2:20 PM
Hi Guys

Just wondering if it is by design that the container seems to throw up :) when I try to RegisterAllForOpenGeneric for a type that has no implementation in the Current Domain and is there any easy way to prevent this.

I want to be able to do this as I am working on framework where implementing this type is optional. I had assumed that if none were implemented I would simply be injected an empty enumeration.

Many Thanks
Feb 4, 2015 at 2:33 PM
Can you share your configuration and the thrown exception details? Btw, which version are you using?
Feb 4, 2015 at 4:17 PM
Thank you

Using version
2.6.1

Exception Message
'The supplied collection should contain at least one element. Parameter name: openGenericImplementations'
public abstract class RequestContract<TResult> : BaseRequestContract
        where TResult : ResultContract {}

public abstract class RequestContract : BaseRequestContract  {}

public abstract class RequestHandler<TRequest> : BaseRequestHandler
        where TRequest : RequestContract {}

public abstract class RequestHandler<TRequest, TResult> : BaseRequestHandler
        where TRequest : RequestContract<TResult>
        where TResult : ResultContract {}

public class RequestDispatcher : IRequestDispatcher
{
    public RequestDispatcher(
            Func<BaseRequestContract, IEnumerable<BaseRequestHandler>> resolveHandlersFor
        ) { }
}
Registrations
public class CqsRegistrationPackage : IPackage
{
    public void RegisterServices(Container container){
         container.RegisterSingle<Func<BaseRequestContract, IEnumerable<BaseRequestHandler>>>(r => ResolveHadlersFor(r, container));
         container.RegisterAllForOpenGeneric(typeof(RequestHandler<,>));
         container.RegisterAllForOpenGeneric(typeof(RequestHandler<>));
    }

    static IEnumerable<BaseRequestHandler> ResolveHadlersFor(BaseRequestContract request, Container container){
        var typeOfRequest = request.GetType();

        if(request is RequestContract) return GetNullResultRequestHandlers(typeOfRequest, container);

        return GetNonNullRequestHandlers(typeOfRequest, container);
    }

    static IEnumerable<BaseRequestHandler> GetNonNullRequestHandlers(Type typeOfRequest, Container container){
        var typeofNonNullResultRequest = typeof(RequestContract<>);
        var resultType = typeOfRequest.BaseType.GenericTypeArguments[0];
        var handlerType = typeof(RequestHandler<,>).MakeGenericType(typeOfRequest, resultType);

        return container
            .GetAllInstances(handlerType)
            .Cast<BaseRequestHandler>()
            .ToList();
    }
}

static IEnumerable<BaseRequestHandler> GetNullResultRequestHandlers(Type typeOfRequest, Container container){
    var handlerType = typeof(RequestHandler<>).MakeGenericType(typeOfRequest);

    return container
        .GetAllInstances(handlerType)
        .Cast<BaseRequestHandler>()
        .ToList();
}
Feb 4, 2015 at 5:01 PM
Worked around with
static void RegisterHandlers(Container container){
            var typeOfBaseHandler = typeof(BaseRequestHandler);

            var allHandlers =  AppDomain.CurrentDomain
                .GetAssemblies()
                .SelectMany(a => a.GetExportedTypes())
                .Where(t =>
                    !t.IsInterface
                    && !t.IsAbstract
                    && !t.IsGenericTypeDefinition
                    && typeOfBaseHandler.IsAssignableFrom(t)
                )
                .ToList();

            var nullResultHandlers = allHandlers
                .Where(h => h.BaseType.GetGenericTypeDefinition() == typeof(RequestHandler<>))
                .ToList();

            var nonNullResultHandlers = allHandlers
                .Where(h => h.BaseType.GetGenericTypeDefinition() == typeof(RequestHandler<,>))
                .ToList();

            if (nullResultHandlers.Any()) container.RegisterAllOpenGeneric(typeof(RequestHandler<>), nullResultHandlers);
            if (nonNullResultHandlers.Any()) container.RegisterAllOpenGeneric(typeof(RequestHandler<,>), nonNullResultHandlers);
        }
but wonder if there is a better way possibly. ?
Feb 4, 2015 at 6:58 PM
Ah, I see, what you are doing is exactly the same model as explained here and here, except that you have named both sides 'request handlers` (opposed the the more commonly used 'command handlers' and 'query handlers'). But what's in the name.

So what I assume you are trying to achieve is that
  • you want to batch-register all non-generic implementations of both RequestHandler<TRequest> and RequestHandler<TRequest, TResult>.
  • for every closed-generic RequestHandler<TRequest> there is exactly one implementation.
  • for every closed-generic RequestHandler<TRequest, TResult> there is exactly one implementation.
Where you are going wrong however is that you are using RegisterAllForOpenGeneric. This extension method allows you to register collections that consist solely of generic types. So in other words, you can do the following registration with it:
container.RegisterAllForOpenGeneric(typeof(IEventHandler<>), new[] {
    typeof(TracingEventHandler<>),
    typeof(AuditTrailingEventHandler<>)
});
RegisterAllForOpenGeneric is the collection-counter part of RegisterOpenGeneric. RegisterOpenGeneric allows registering a mapping of an open-generic abstraction to an open-generic implementation that will be resolved using unregistered type resolution. RegisterAllForOpenGeneric uses unregistered type resolution as well, but builds up a collection. RegisterAllForOpenGeneric however, does not do batch-registration. Besides, you don't want to register collections at all. You want to do one-to-one mappings.

When doing batch-registration of open-generic types, Simple Injector contains a RegisterManyForOpenGeneric extension method. I understand the confusion, since its name is pretty close to RegisterAllForOpenGeneric. When calling RegisterManyForOpenGeneric, you can supply the assemblies where Simple Injector can look for types. Simple Injector will register all non-generic implementations of the given generic abstraction for you. Here's an example:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
container.RegisterManyForOpenGeneric(typeof(RequestHandler<>), assemblies);
container.RegisterManyForOpenGeneric(typeof(RequestHandler<,>), assemblies);
In your case, this is probably all the configuration that you need. Simple Injector will search through the assemblies and will look for implementations of the supplied abstractions, and will register them one by one. So the following would be the hand-written equivalent:
container.Register<RequestHandler<GetCustomerById>, GetCustomerByIdHandler>();
container.Register<RequestHandler<FindOrdersForCustomer>, FindOrdersForCustomerHandler>();
container.Register<RequestHandler<GetUnshippedOrdersByDepartment>, GetUnshippedOrdersByDepartmentHandler>();
// etc, etc, etc
If on the other hand, you want to register collections, you still don't need the RegisterAllForOpenGeneric method. Instead you can do the following registration:
container.RegisterManyForOpenGeneric(typeof(IEventHandler<>), container.RegisterAll, assemblies);
Here we supply the RegisterManyForOpenGeneric with a delegate that tells it how to register the found types. This delegate will be called once per group of non-generic implementations for one closed-generic IEventHandler<T>.

So when exactly do you need to use RegisterAllForOpenGeneric? The answer is never. Simple Injector 2.6 and 2.7 improved the RegisterAll method, which made the use of RegisterAllForOpenGeneric redundant. It will be marked [obsolete] in a future version and will probably be removed from the API in v3. If you have a generic type that needs to be added to all of the registered collections, this is the way to do it:
container.RegisterManyForOpenGeneric(typeof(IEventHandler<>), container.RegisterAll, assemblies);
// using SimpleInjector.Advanced;
container.AppendToCollection(typeof(IEventHandler<>), typeof(TracingEventHandler<>));
container.AppendToCollection(typeof(IEventHandler<>), typeof(AuditTrailingEventHandler<>));   
Marked as answer by ricardo100671 on 2/8/2015 at 10:18 PM
Feb 5, 2015 at 6:28 AM
@dot_NET_Junkie Yes your articles inspired me and, can I take the opportunity to say thank you and excellent work. I have been looking at CQS for years but could never had the time to get my head around how to apply it to my projects. Your work crystallized it for me, so thanks again.

Actually I decided that there can be more than one Handler for a given request, since I have come across situations where the handling of a request not only depended on its type but also its state. The RequestDispatcher, utilizing a Handler's 'CanHandle' method will determine which handler to invoke and checks that only one returns true, I also wanted to create a unified framework, while most commonly, it is true that commands should not return a result, I did not want to shoe horn the framework resulting in confusing code bases where implementors possibly mix QueryHandlers in with their CommandHandler assemblies for some rare situations where they need a result from a command, for what ever reason.

Thanks for the registration bits I will give it a go.
Feb 5, 2015 at 8:25 AM
Edited Feb 5, 2015 at 8:25 AM
Hi Ricardo, You're more than welcome.

Since you are having one-to-many mappings, you'll need the following registration:
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
container.RegisterManyForOpenGeneric(typeof(RequestHandler<>), 
    container.RegistereAll, assemblies);
container.RegisterManyForOpenGeneric(typeof(RequestHandler<,>), 
    container.RegistereAll, assemblies);
With this, you can simply resolve a collection of RequestHandler<TRequest> or RequestHandler<TRequest, TResult> within your dispatcher using GetAllInstances(Type).

One warning though. There seems to be a regression bug in v2.7.1 concerning the performance of the RegisterManyForOpenGeneric overloads. Functionally everything works though (that's why all the tests were green). We are still investigating, but I advice using 2.7.0 for now.
Marked as answer by ricardo100671 on 2/8/2015 at 10:18 PM
Feb 9, 2015 at 4:07 PM
Thanks again.

This next one may seem a little strange and I am still wrapping my head around a best appouch.

Could you tell me if it may be possible to register Closed Constructed Generic, such that they can be retrieved from the container as a collection by specifying the type parameter bases.
   public abstract class Factory<TIn, TOut> { }

   public abstract class XFactory<TIn, TOut> :  Factory<TIn, TOut>  
        where TIn : ContractX
        where TOut : ContractX
   { }

   public abstract class YFactory<TIn, TOut> :  Factory<TIn, TOut>  
        where TIn : ContractY
        where TOut : ContractY
   { }

   public class MyXContract1 : XContract{}
   public class MyXContract2 : XContract{}

   public class MyYContract1 : YContract{}
   public class MyYContract2 : YContract{}
   public class MyYContract3 : YContract{}
    
   public classs MyXFactory : Factory<MyXFactory1, MyXFactory2> {}

   public classs MyYFactory1 : Factory<MyYFactory1, MyYFactory2> {}
   public classs MyYFactory2 : Factory<MyYFactory1, MyYFactory3> {}

    public class SomeProccess{
        public SomeProccess(
            IEnumerable<Factory<ContractX, ContractX,>> xFactories,
            IEnumerable<Factory<ContractY, ContractY>> yFactories,
        )
    }
Feb 9, 2015 at 4:31 PM
I assume that to made a typo in the definitions of MyXFactory, MyYFactory1 and MyYFactory2. I assume that the base class should be something like Factory<MyYContract1, MyYContract> (so I assume that the generic type arguments should be contracts and not factories). If I'm incorrect, please explain what you are trying to do, because in that case I completely lost you.

So, what it seems you're trying to do is to define a set of factories, where possibly are multiple implementations of the same closed-generic version of Factory<TIn, TOut>.

What I'm unsure of is whether you want to be able to resolve variant implementations as well. For instance, if you resolve
Factory<MyXContract1, ContractX>, do you want the container to resolve implementations forFactory<ContractX, ContractX>` as well? If you want this, that would be a matter of changing the base class to an interface and defining the in and out keywords on it:
public interface class Factory<in TIn, out TOut> { }
But eitherway, what you need is to batch register those implementations. There are multiple ways to do this with Simple Injector, for instance:
var types = OpenGenericBatchRegistrationExtensions.GetTypesToRegister(container,
    typeof(Factory<,>),
    AccessibilityOption.AllTypes,
    typeof(Factory<,>).Assembly);

container.RegisterAll(typeof(Factory<,>), types);