Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple injector register per web request and with context

There is RegisterPerWebRequest and RegisterWithContext (this one is not originally shipped with simple injector, however it supplied on their advanced-scenarios section). Separately both approaches works fine but I need to combine them.

I figured out that in RegisterPerWebRequest used new WebRequestLifestyle() lifestyle (found it there). So instead of using Lifestyle.Transient RegisterWithContext I supplied new WebRequestLifestyle() but it appears that DependencyContext.ImplementationType and DependencyContext.ServiceType are null.

What is wrong with that?

Update 1.

So I wanted to Register type per web request as does RegisterPerWebRequest but also to be able to provide instance creator with access to type where registered type is injected.

I modified (extracted lifestyle as a parameter) RegisterWithContext to be:

public static void RegisterWithContext<TService>(
    this Container container,
    Func<DependencyContext, TService> contextBasedFactory, Lifestyle lifestyle)
    where TService : class
{
    //original code

    container.Register<TService>(rootFactory, lifestyle);

    //original code
}

And for "per web request and with context" registration I expected to be able use:

container.RegisterWithContext<IUnitOfWork>(dependencyContext =>
{
      var implementationType = dependencyContext.ImplementationType;
      //do some stuff and return preconfigured UnitOfWork
}, new WebRequestLifestyle());

And as I already mentioned dependencyContext.ImplementationType is NULL

I am using SimpleInjector 2.3.0.0

like image 501
Vladimirs Avatar asked Feb 27 '14 17:02

Vladimirs


1 Answers

The RegisterWithContext extension method explicitly registers the supplied delegate as Transient. This is done, because registering the type with any other lifestyle makes little sense.

The idea of a lifestyle such as the WebRequestLifestyle is to cache and reuse the same instance throughout the object graph (and possibly beyond that). This concept however, makes little sense when dealing with a context-based registration, simply because a context-based instance is expected to be different each time it gets injected. In other words, supplying each consumer with a unique instance conflicts with the concept of reusing the same instance.

Take for instance a look at the following object graph:

new HomeController(
    new Logger("HomeController"),
    new LoggingRepositoryDecorator<User>(
        new Logger("LoggingRepositoryDecorator<User>"),
        new SqlRepository<User>(
            new DbContext())),
    new LoggingCommandHandlerDecorator<ShipOrder>(
        new Logger("LoggingCommandHandlerDecorator<ShipOrder>"),
        new ShipOrderCommandHandler(
            new DbContext())));

In this object graph we create a HomeController with its dependencies. The Logger component is clearly a context-based component, since its initialized differently every time, based on its parent. This would be the registration for Logger:

container.RegisterWithContext<ILogger>(context =>
    new Logger(context.ImplementationType.Name));

But if we allowed the ILogger registration to be registered with the WebRequestLifestyle, the same instance should be applied every time, which might lead to the following object graph:

ILogger logger = new Logger(typeName: "HomeController");

new HomeController(
    logger,
    new LoggingRepositoryDecorator<User>(
        logger,
        new SqlRepository<User>(
            new DbContext())),
    new LoggingCommandHandlerDecorator<ShipOrder>(
        logger,
        new ShipOrderCommandHandler(
            new DbContext())));

In this object graph the same Logger("HomeController") is injected, which is clearly not what we want. Furthermore, the behavior becomes very unpredictable, since it is the consumer that gets created first, that will determine the logger's typeName that gets reused throughout the graph. But nobody would expect that removing the ILogger from the HomeController's constructor, would cause the logger of the LoggingCommandHandlerDecorator<ShipOrder> to change.

That's why there is no Lifestyle argument in the RegisterWithContext extension method.

like image 178
Steven Avatar answered Nov 15 '22 00:11

Steven