简体   繁体   中英

Is it okay to mix lifestyles with auto-wiring in Simple Injector

I'm using Simple Injector, to enabled my code be auto-wired to event bus messages . During auto-wiring of my INotificationHandlers I have allowed the potential use of different lifestyles for different concrete implementations, as shown here:

public static Lifestyle GetLifestyleForType(
    Type type, Container container, Dictionary<Type,Lifestyle> list = null)
{
    if (list is null) return container.Options.DefaultLifestyle;

    return list.TryGetValue(type, out var lifestyle)
        ? lifestyle : container.Options.DefaultLifestyle;
}

public static void Wire(Container container)
{
    var lifestyles = new Dictionary<Type, Lifestyle> {
        { typeof(FirstClass), Lifestyle.Singleton },
        { typeof(OtherClass), Lifestyle.Transient }
    };

    var handlerTypes = container
        .GetTypesToRegister(typeof(INotificationHandler<>), typeof(OtherClass).Assembly);

    var handlerProducers = (
        from type in handlerTypes
        from interfaceType in type.GetClosedTypesOf(typeof(INotificationHandler<>))
        let producer = GetLifestyleForType(type, container, lifestyles)
            .CreateProducer(interfaceType, type, container)
        group new { type, producer } by interfaceType into interfaceGroup
        select new
        {
            interfaceGroup.Key,
            Value = (
                from pair in interfaceGroup
                group pair.producer by pair.type into concreteGroup
                select new { concreteGroup.Key, Value = concreteGroup.ToArray() })
                .ToDictionary(i => i.Key, i => i.Value)
        }).ToDictionary(i => i.Key, i => i.Value);
}

I note with all other auto-wiring examples, it assumes the same lifestyle is used (as haven't seen one specified) - any reason for this (to prevent doing dangerous things)?

In addition does Lifestyle.Transient work differently with async/await operators? Just realised have been using the Transient lifecycle all along with SimpleInjector without any observed ill effects that i know of, presume I should switch by default (as i am wiring above) to Lifestyle.Scoped and container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle()

I note with all other auto-wiring examples, it assumes the same lifestyle is used (as haven't seen one specified) - any reason for this (to prevent doing dangerous things)?

By default, Simple Injector uses Transient as its default lifestyle. This means that, unless you explicitly supply a registration with a lifestyle, Simple Injector uses the default lifestyle, ie Transient .

Although there is nothing inherently wrong with using multiple lifestyles for your handlers, using multiple lifestyles does increase the complexity of your solution (as you probably already noticed), so you should ask yourself whether you really need this complexity. Unless you wish to make your object graphs singletons from top to bottom (but that leads to a completely different model ), I suggest keeping the solution simple and stick with the use of Transient instead. As a general rule of thumb, make your root types (handlers, controllers, view models, etc) Transient .

The Transient lifestyle comes with some consequences though. Transient components aren't disposed of, and since Transient components aren't reused, they can't be used for any caching of data.

But those problems can be easily solved by moving any logic concerning disposal and caching to dependencies of the root types. You can make those dependencies Scoped or even Singleton . I would generally frown upon any handler in the system that implements IDisposable or caches any state internally. Moving that logic out of your handlers -not only- 'works around' the limitations of the Transient lifestyle, I would argue that this leads to a better-designed system. It also makes it easier to reason about your handlers; what they can and cannot do, and what their lifestyle is. The easiest thing to understand for any developer working on the system (and any future developer) is when all handlers always have the same lifestyle.

In addition does Lifestyle.Transient work differently with async/await operators?

The Transient lifestyle is the simplest lifestyle there is; for Simple Injector Transient is just a no-op. Instance is created and forgotten about (it's not tracked). It won't work any different when running in an asynchronous operation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM