This project is read-only.

Lifetime confusion! please explain

Mar 23, 2014 at 4:18 AM
I am new to Simple Injector. i am having to support a MVC 4 web site that is using SImpleInjector in its app_start area.

I must say it seems to work very well!.

i installed version 2.5,
i am getting like 48 warnings like this:

IAntibioticDiscReasonRepository AntibioticDiscReasonRepository (Singleton) depends on IDataContext (Transient). SimpleInjector.Diagnostics.Debugger.DebuggerViewItem

IAntibioticRepository AntibioticRepository (Singleton) depends on IDataContext (Transient). SimpleInjector.Diagnostics.Debugger.DebuggerViewItem

IAttendingNoteCptModuleRepository AttendingNoteCptModuleRepository (Singleton) depends on IDataContext (Transient). SimpleInjector.Diagnostics.Debugger.DebuggerViewItem

i get that transient is a different life expectancy than singleton.

I changed transient to singleton in the datacontext but, when the site is used by several people is begins to blow up?!!

the services, and repositories are registered as singleton, maybe because it is faster? or more efficient? or because each request gets an explicit instance which i dont see it as a problem.

so, i need some direction as to how to register the objects.
it seems that the datacontext cant be singleton because it does not allow multi users very well,
should i changed the singleton declarations to transient? and make the datacontext transitent as well?

i just don want to continue to try all combinations until i get a better understanding.

any insight will be greatly appreciated it!!

Here are some snippets:
public static class SimpleInjectorInitializer
{
    public static Container Container { get; set; }
    public static void Initialize()
    {
        var container = new Container();

        InitializeContainer(container);

        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

        container.RegisterMvcAttributeFilterProvider();

        container.Verify();

        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

        GlobalConfiguration.Configuration.DependencyResolver =
            new SimpleInjectorWebApiDependencyResolver(container);

    }

    private static void InitializeContainer(Container container)
    {
        var services = GlobalConfiguration.Configuration.Services;
        var controllerTypes = services.GetHttpControllerTypeResolver()
            .GetControllerTypes(services.GetAssembliesResolver());

        foreach (var controllerType in controllerTypes)
        {
            container.Register(controllerType);
        }
        // NOT SURE IF THIS IS TRUE....
        // since we used multithreading in our app we want to allow background 
        // threads access to injected services with or without the web context.
        var webToTransientLifestyle = Lifestyle.CreateHybrid(
            () => HttpContext.Current != null,
            new WebRequestLifestyle(),
            Lifestyle.Transient);
        var webToLifetimeLifestyle = Lifestyle.CreateHybrid(
            () => HttpContext.Current != null,
            new WebRequestLifestyle(),
            new LifetimeScopeLifestyle());

        var scopedLifestyleWithSingletonFallback = Lifestyle.CreateHybrid(
            () => HttpContext.Current != null,
            new LifetimeScopeLifestyle(),
            Lifestyle.Singleton);




        //container.Register<IDataContext, RoundsManagerDatabaseContext>(webToTransientLifestyle);
        //container.Register<IDataContext, RoundsManagerDatabaseContext>(new WebRequestLifestyle());
        container.Register<IDataContext, RoundsManagerDatabaseContext>(Lifestyle.Transient);
        //container.Register<IDataContext, RoundsManagerDatabaseContext>();
        //Container.Register<IAppStateStorage, WebStateStorage>(webToTransientLifestyle);
        container.Register<IUnitOfWork, UnitOfWork>(webToLifetimeLifestyle);
        container.Register<ICommandBus, CommandBus>(webToLifetimeLifestyle);
        container.Register<IQueryBus, QueryBus>(webToLifetimeLifestyle);
        //container.Register<IQueryBus, QueryBus>(webToLifetimeLifestyle);
        container.Register<IUserContext, UserContext>(webToLifetimeLifestyle);
        //container.Register<IUserContext, UserContext>(webToLifetimeLifestyle);
        container.Register<IClinicalStaffContext, ClinicalStaffContext>(Lifestyle.Singleton);
        container.Register<IPhysicianContext, PhysicianContext>(Lifestyle.Singleton);
        //container.Register<IClinicalStaffContext, ClinicalStaffContext>(webToLifetimeLifestyle);
        //container.Register<IPhysicianContext, PhysicianContext>(webToLifetimeLifestyle);

        InfrastructureDI.Initialize(container);

        // register all the command and query handlers in batch
        container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
            AccessibilityOption.PublicTypesOnly, container.RegisterAll,
            Assembly.Load("UM.Surgery.MobileCare.TM.CommandHandlers"));
        container.RegisterManyForOpenGeneric(typeof(IQueryHandler<>),
            Assembly.Load("UM.Surgery.MobileCare.TM.QueryHandlers"));


        const string domainNamespace = "UM.Surgery.MobileCare.TM.Domain";
        const string infrastructureNamespace = "UM.Surgery.MobileCare.TM.Infrastructure";

        var infrastructureTypes = Assembly.Load(infrastructureNamespace).GetTypes();
        RegisterRepositories(container, infrastructureTypes, infrastructureNamespace);
        RegisterInfrastructureServices(container, infrastructureTypes, domainNamespace, infrastructureNamespace);
    }

    private static void RegisterRepositories(Container container, IEnumerable<Type> infrastructureTypes, string repositoriesNamespace)
    {

        var repositoryInterfaces =
            (from t in infrastructureTypes
             where
                 t.IsInterface &&
                 t.Name.EndsWith("Repository") &&
                 t.Namespace.StartsWith(repositoriesNamespace)
             select new
             {
                 i = t,
                 s = infrastructureTypes.FirstOrDefault(x => t.IsAssignableFrom(x) &&
                                                    x.IsInterface == false &&
                                                    x.Namespace.StartsWith(repositoriesNamespace))
             }).ToList();
        foreach (var service in repositoryInterfaces)
        {
            container.RegisterSingle(service.i, service.s);
        }
    }

    private static void RegisterInfrastructureServices(Container container, IEnumerable<Type> infrastructureTypes, string domainServicesNamespace,
                                                      string infrastructureServicesNamespace)
    {
        var domainTypes = Assembly.Load("UM.Surgery.MobileCare.TM.Domain").GetTypes();
        var serviceInterfaces =
            (from t in domainTypes
             where
                 t.IsInterface &&
                 t.Name.EndsWith("Service") &&
                 t.Namespace.StartsWith(domainServicesNamespace)
             select new
             {
                 i = t,
                 s = infrastructureTypes.FirstOrDefault(x => t.IsAssignableFrom(x) &&
                                                    x.Namespace.StartsWith(infrastructureServicesNamespace))
             }).ToList();
        foreach (var service in serviceInterfaces)
        {
            container.RegisterSingle(service.i, service.s);
        }
    }

Coordinator
Mar 23, 2014 at 7:34 AM
Mar 23, 2014 at 5:00 PM
Yes i did, just read it again. thing seem to be working on the site right now even though i still get the 48 warnings due to the singleton depending on a shorter webrequest or one of the hybrid requests i declare.

I am running some use cases for when the site is having multiple users on it and see what errors pop up.

would the diagnostic system report inconsistencies when multiple users are accessing the site?

my basic question #1 is this: if singleton registration of an object is persistent throughout the life of the application, why not always register objects that way?

my basic question #2 is this: if we need to provide unique instances of an object, maybe to keep a dbcontext transaction open, then it makes sense that those should be disposed of once used? and so the singletons should not depend upon those.

So, should i change my singletons to transient?

you can obviously tell that i am still guessing at this. I read the main page and subsequent pages about this issue. Maybe i need to see more practical examples.

Maybe i need someone to help me tune the beast!

My best kindest regards for your responses
Coordinator
Mar 23, 2014 at 10:37 PM
Your current registration will be a serious concurrency problem in your application, because although you have registered the IDataContext as transient, all the repositories that depend on it are registered as singleton. Transient means that every time the IDataContext is requested, a new instance is created, but Singleton means that only one IAntibioticDiscReasonRepository instance is ever created. This means that that IAntibioticDiscReasonRepository will keep referencing the same IDataContext instance for the duration of the application. Since you have many repository instances, you will have as many IDataContext instances as repositories, one for each repository. Since the IAntibioticDiscReasonRepository is a singleton, it will be used over all threads in the application, which implies that the IDataContext instance it depends on, will also be reused throughout the application. So although each repository will get its own IDataContext instance, those instance are shared across threads which is a really bad idea for an unit of work.

I'd like to advice to you read more about the lifestyle of a unit of work here.

So in general, components should not live longer than their dependencies are intended to live, because this 'upgrades' the lifetime of the dependencies as well, which is usually not what you want. A component should have a lifetime that is equal or shorter to the lifetime of all of its dependencies. I usually advice to give every component the transient lifestyle, unless there's a strong reason to deviate from this. A good reason to deviate from this is when registering a Unit of Work, that often need to live for the duration of the request.

Another thing you need to look at is the lifetime of instances when executing on background threads. It's very unlikely that instances that are registered with the Web Request lifestyle, need to be resolved as Transient on a background thread. More obvious is that the background operation functions as a request and that a single instance is needed during the background operation. So this is not a Transient lifestyle but a scoped lifestyle, such as the Lifetime Scope. What you need to do is explicitly start a new LifetimeScope directly after starting the background operation, but before you resolve the new object graph for that background operation. Take a look at this stackoverflow question for instance. So instead of using the webToTransientLifestyle, use the webToLifetimeLifestyle.

One last thing. It seems that you're manually registering your ApiControllers. There's a Web API integration package on NuGet. This allows you to register all your controllers as the Wiki describes.
Marked as answer by dot_NET_Junkie on 4/15/2014 at 1:48 AM