I have the below classes:
public interface IDbCommandHandler<in TCommand, out TOutput>
where TCommand : IDbCommand
{
TOutput Handle(TCommand command);
}
public class SubIdentifierItemCreateCommand<TItemType, TDefaultValues>
: BaseDbCommand
where TItemType: TDefaultValues
{
}
public class SubIdentifierItemCreateCommandHandler<TItemType, TDefaultValues>
: BaseDbCommandHandler<SubIdentifierItemCreateCommand<TItemType, TDefaultValues>, TItemType>,
IDbCommandHandler<SubIdentifierItemCreateCommand<TItemType, TDefaultValues>, TItemType>
where TItemType: class, TDefaultValues, IItemForGenericItemByIdentifierRetriever , new()
{
}
I need to register the SubIdentifierItemCreateCommandHandler
as singleton-open-generic, to handle any requests for services of type
IDbCommandHandler<SubIdentifierItemCreateCommand<,>,>
.
Is this possible? I've tried in various ways and I always get an error.
_container.RegisterSingleOpenGeneric(
typeof(IDbCommandHandler<,>),
typeof(SubIdentifierItemCreateCommandHandler<,>));
_container.RegisterOpenGeneric(
typeof(IDbCommandHandler<,>),
typeof(SubIdentifierItemCreateCommandHandler<,>));
// this one is throws a compile-time error, that you cannot
// use partial open types.
_container.RegisterManyForOpenGeneric(
typeof(IDbCommandHandler<SubIdentifierItemCreateCommand<,>,>),
typeof(SubIdentifierItemCreateCommandHandler<,>));
I want to be able to call the below and work:
var item = _container.GetInstance<
IDbCommandHandler<
SubIdentifierItemCreateCommand<
SectionData,
ISectionDataDefaultValues>,
SectionData>>();
Unfortunately, you stumbled upon a bug in the framework. Simple Injector 1.6.1 does not correctly handle the "where TItemType: TDefaultValues" constraint correctly.
The solution is simple, migrate to Simple Injector 2.0 (via NuGet).
If you for what ever reason can't switch to Simple Injector 2.0, you can register a ResolveUnregisteredType
event for the registration of the type to work around the bug in the 1.6.1 release:
container.ResolveUnregisteredType += (sender, e) =>
{
var serviceType = e.UnregisteredServiceType;
if (serviceType.IsGenericType &&
serviceType.GetGenericTypeDefinition() == typeof(IDbCommandHandler<,>))
{
var commandArg = serviceType.GetGenericArguments()[0];
var outputArg = serviceType.GetGenericArguments()[1];
if (commandArg.IsGenericType &&
commandArg.GetGenericTypeDefinition() ==
typeof(SubIdentifierItemCreateCommand<,>))
{
var itemTypeArgument = commandArg.GetGenericArguments()[0];
var defaultValuesArgument = commandArg.GetGenericArguments()[1];
if (itemTypeArgument != outputArg)
{
return;
}
Type typeToRegister;
try
{
typeToRegister =
typeof(SubIdentifierItemCreateCommandHandler<,>)
.MakeGenericType(itemTypeArgument.GetGenericArguments());
}
catch (ArgumentException)
{
// Thrown by MakeGenericType when the type constraints
// do not match. In this case, we don't have to register
// anything and can bail out.
return;
}
object singleInstance = container.GetInstance(typeToRegister);
// Register the instance as singleton.
e.Register(() => singleInstance);
}
}
};
I know, it is ugly, but at least there's a work around ;-)