Registering 'half-closed' generic componen

2019-06-15 06:55发布

I have two interfaces:

public interface IQuery<TResult> { }

public interface IQueryHandler<in TQuery, out TResult>
    where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery q);
}

An example of a closed implementation of IQueryHandler:

public class EventBookingsHandler : IQueryHandler<EventBookings, IEnumerable<EventBooking>>
{
    private readonly DbContext _context;

    public EventBookingsHandler(DbContext context)
    {
        _context = context;
    }

    public IEnumerable<EventBooking> Handle(EventBookings q)
    {
        return _context.Set<MemberEvent>()
            .OfEvent(q.EventId)
            .AsEnumerable()
            .Select(EventBooking.FromMemberEvent);
    }
}

I can register and resolve closed generic implementations of IQueryHandler<,> with this component registration:

Classes.FromAssemblyContaining(typeof(IQueryHandler<,>))
    .BasedOn(typeof(IQueryHandler<,>))
    .WithServiceAllInterfaces()

However what I would like to do is resolve an open generic implementation that is "half closed" (ie specifies a generic TQuery type parameter):

public class GetById<TEntity> : IQuery<TEntity> where TEntity : class, IIdentity
{
    public int Id { get; private set; }

    public GetById(int id)
    {
        Id = id;
    }
}

public class GetByIdHandler<TEntity> : IQueryHandler<GetById<TEntity>, TEntity> where TEntity : class, IIdentity
{
    private readonly DbContext _context;

    public GetByIdHandler(DbContext context)
    {
        _context = context;
    }

    public TEntity Handle(GetById<TEntity> q)
    {
        return _context.Set<TEntity>().Find(q.Id);
    }
}

When I tried to resolve IQueryHandler<GetById<Event>, Event> I got this exception:

An exception of type 'Castle.MicroKernel.Handlers.GenericHandlerTypeMismatchException' occurred in Castle.Windsor.dll but was not handled in user code

Additional information: Types Queries.GetById'1[[Models.Event, Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Models.Event don't satisfy generic constraints of implementation type Queries.GetByIdHandler'1 of component 'Queries.GetByIdHandler'1'. This is most likely a bug in your code.

It looks like the type has successfully registered, and the constraints are satisfied as far as I can tell (Event is a class and implements IIdentity). What am I missing here? Am I trying to do something that Windsor can't cope with?

1条回答
你好瞎i
2楼-- · 2019-06-15 07:40

(I'm not posting this up as an answer, just as some useful information that is too much info for a comment.)

While this code in Castle fails:

public void Resolve_GetByIdHandler_Succeeds()
{
    var container = new Castle.Windsor.WindsorContainer();

    container.Register(Component
        .For(typeof(IQueryHandler<,>))
        .ImplementedBy(typeof(GetByIdHandler<>)));

    var x = container.Resolve<IQueryHandler<GetById<Event>, Event>>();
}

The same thing in Simple Injector works:

public void GetInstance_GetByIdHandler_Succeeds()
{
    var container = new SimpleInjector.Container();

    container.RegisterOpenGeneric(
        typeof(IQueryHandler<,>),
        typeof(GetByIdHandler<>));

    var x = container.GetInstance<IQueryHandler<GetById<Event>, Event>>();
}
查看更多
登录 后发表回答