Question

In this code:

container.Register<IDataHoldingSession<DbContext>, EntityFrameworkSession>();
container.RegisterAll<ISession>(typeof(IDataHoldingSession<DbContext>));
container.RegisterDecorator(typeof(IDataHoldingSession<>), typeof(ValidatingSession<>));

When the container is asked for IEnumerable<ISession>, it is given a collection containing EntityFrameworkSession, but the decorator is not applied. How can I make it apply the decorator in the above code?

I believe this occurs because the container is not asked for IEnumerable<IDataHoldingSession<T>>. I interpret the XML documentation for RegisterAll as implying that the container will be asked for an instance of IDataHoldingSession<DbContext> in this case, which I figure should cause the decorator to be applied. However, it appears that the decorator is bypassed for this situation because RegisterAll is called with the ISession service type.

Était-ce utile?

La solution 2

A registration in Simple Injector contains of two parts:

  1. An InstanceProducer. This is the object produces instances and is responsible for interception and decorating instances. The InstanceProducer doesn't know the TImplementation of a registration, only the TService.
  2. The InstanceProducer wraps an Registration object. This object is responsible for building an Expression that creates the instance, does constructor injection, property injection, custom initialization, and it applies the lifestyle to the expression. The Registration (only) knows about the TImplementation.

When you make a RegisterAll registration, you supply types that reference other registrations that are made in the container. You are doing this by doing both Register<IDataHoldingSession<DbContext>> and using that interface type in your RegisterAll registration.

What RegisterAll does under the covers, is querying the container for a registration of the given type (in your case IDataHoldingSession<DbContext>). Since the requested type might be different from the service type of the collection (it usually does, and does in your case) the container grabs the Registration from the found InstanceProducer and it creates a new InstanceProducer based on that Registration with the actual service type of the collection. This looks as follows in the Simple Injector source code (in ContainerControlledCollection):

return new InstanceProducer(typeof(TService), instanceProducer.Registration);

But since decorators are applied by the InstanceProducer, we lose the decorator that you applied to IDataHoldingSession<T>.

To be frank, I'm not sure why we implemented the behavior like this, since the most intuitive behavior would let the decorator for IDataHoldingSession<T> still be applied. I think we tried to prevent having decorators being applied twice; this was a problem in earlier releases of Simple Injector.

I'll try to look into this and look if I can come up with a fix for v2.4, but I can only do this if I don't introduce a breaking change. In the meantime, I think the workaround you found yourself it very good. If I make the fix, I'll probably implement it internally much like you are doing now.

I'm sorry that Simple Injector isn't working the way you expected it to be.

UPDATE:

I just checked in a bug fix in the main branch that fixes this issue. The v2.4 release contains this fix.

Autres conseils

I was able to resolve this by supplying Registrations to RegisterAll. I set the service type of each registration to its specific service type rather than ISession.

Here's an extension method that demonstrates this:

/// <summary>
/// Same as <see cref="Container.RegisterAll"/>, except each implementation service
/// is individually resolved from the container for compatibility with decorators.
/// </summary>
public static void RegisterAllBugFixed<TService>(this Container container, 
    params Type[] serviceTypes)
{
    var registrations = 
        from serviceType in serviceTypes
        select Lifestyle.Transient.CreateRegistration(
            serviceType,
            () => container.GetInstance(serviceType), 
            container);

    container.RegisterAll(typeof(TService), registrations);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top