Overload of RegisterLifetimeScope in LifetimeScopeExtensions

Apr 8, 2012 at 6:57 PM

Hi, thanx for  a great package.
I wonder if it is possible to have a overload in RegisterLifetimeScope that looks like this:

public static void RegisterLifetimeScope<TService>(this Container container, Func<TService> instanceCreator)

I can't figure out how to do this, so I can use ut as Register or RegisterPerWebRequest.

/Jonathan

 

Coordinator
Apr 8, 2012 at 7:24 PM

You need to add the following code to the LifetimeScopeExtensions class:

public static void RegisterLifetimeScope<TService>(this Container container,
    Func<TService> instanceCreator) where TService : class
{
    container.ResolveUnregisteredType += (sender, e) =>
    {
        if (e.UnregisteredServiceType == typeof(TService))
        {
            e.Register(() =>
            {
                var scope = CurrentScope;

                if (scope == null)
                {
                    throw new ActivationException(string.Format(
                        CultureInfo.InvariantCulture,
                        NoLifetimeScopeMessage, typeof(TService)));
                }

                return scope.GetInstance<TService>(container, instanceCreator);
            });
        }
    };
}

And you need to add the following code to the private nested LifetimeScopeExtensions.LifetimeScope class:

internal TService GetInstance<TService>(Container container, Func<TService> instanceCreator)
    where TService : class
{
    object instance;

    if (!this.instances.TryGetValue(typeof(TService), out instance))
    {
        instance = instanceCreator();
        this.instances[typeof(TService)] = instance;
    }

    return (TService)instance;
}

 

 

Apr 8, 2012 at 8:29 PM
Edited Apr 8, 2012 at 8:29 PM

Thanx for quick reply, but I was doing something similar to that, and I also pasted your  example, it builds and runs fine - but do not dispose.

This works fine, disposes and everything (the orignal):

But this never dispose (with the added overload):

Any idea why?

/Jonathan

container.RegisterLifetimeScope<IMappingContext>(() => new Core.EntityFramework.Data.MappingContext());

container.RegisterLifetimeScope<IMappingContext, Core.EntityFramework.Data.MappingContext>();
Coordinator
Apr 8, 2012 at 9:07 PM
Edited Apr 9, 2012 at 10:00 AM

I assume that your MappingContext class implements IDisposable, but the IMappingContext interface does not, and I assume that you are registering for disposal using container.DisposeWhenLifetimeScopeEnds<MappingContext>() or container.DisposeWhenLifetimeScopeEnds<IDisposable>(). When this is the case, the following is happing:

Since you are newing up the MappingContext by hand (instead of leaving this to the container), the container has no notion of that type, and the Initializer that is registered for that type (by the LifetimeScopeExtensions) never fires and the object is therefore never registered for disposal by the LifetimeScope.

You can solve this by doing the following:

  1. Let IMappingContext implement IDisposable and register DisposeWhenLifetimeSsopeEnds<IMappingContext>() or DisposeWhenLifetimeScopeEnds<IDisposable>(). Or,
  2. don't use the new factory overload, but let the container do the wiring for you by calling RegisterLifetimeScope<IMappingContext, MappingContext>(). Or,
  3. Use the factory overload, but let the container still do the wiring, by calling RegisterLifetimeScope<IMappingContext>(() => container.GetInstance<MappingContext>()).

It might seem odd to you that the container does not detect that the returned IMappingContext is in fact a MappingContext or that it implements IDisposable, but this is a conscious design decision. One important reason is that of performance. By only using the static information that is made available during registration, and no runtime information, performance is improved considerably.

Apr 9, 2012 at 5:41 AM

Thanx again, but I'm sorry I can't still solve this - and I am intressted in why, I know I can use 2 from above and that works fine.

public interface IUnitOfWork : IDisposable
public interface IMappingContext : IUnitOfWork
public class MappingContext : DbContext, IMappingContext

container.RegisterLifetimeScope<IMappingContext>(() => new MappingContext());
container.DisposeWhenLifetimeScopeEnds<IMappingContext>();

The above is the code structure that I am running, and IMappingContext has IDisposable via IUnitOfWork. am I missing something when you say "but the IMappingContext does not"

/Jonathan

Coordinator
Apr 9, 2012 at 10:08 AM

I'm sorry, the first paragraph of my last reply was misleading. Also option 1, is not a solution to your problem, simply because the initializer never fires for that type, since your are manually newing it up.

So if you can, don't new up objects manually. Let the container do this for you as much as you can (this is a general rule that holds for all DI containers). But if you can't (for instance because MappingContext has multiple constructors), you could do the following:

container.RegisterLifetimeScope<IMappingContext>(() =>
{
    var context = new MappingContext();
    LifetimeScopeExtensions.RegisterForDisposal(context);
    return context;
}

Note that you need to make the LifetimeScopeExtensions.RegisterForDisposal method public (I just added this private RegisterForDisposal method to the wiki page).

Apr 9, 2012 at 1:45 PM

Thanx very much.

Now it is working perfectly. I Agree with you in the scond paragraph above - but there ara always some special cases needed. There is a problem in my architecture but this solves that.

Thanks again

/Jonathan

Coordinator
Jun 5, 2012 at 7:46 AM

Since the release of 1.4.2 new integration and extension packages are available that add custom lifestyles to the Simple Injector, such as the Simple Injector Lifetime Scoping Extensions NuGet package (for .NET 4.0 and up). This package addresses most of your problems:

  • It will automatically dispose instances that are registered with RegisterLifetimeScope when the scope ends (gets disposed). You don't have to call 'RegisterForDisposal' anymore manually.
  • The package contains the following RegisterLifetimeScope overloads:
    • RegisterLifetimeScope<TConcrete>()
    • RegisterLifetimeScope<TService, TImplementation>()
    • RegisterLifetimeScope<TService>(Func<TService> factory)
    • RegisterLifetimeScope<TService>(Func<TService> factory, bool disposeWhenLifetimeScopeEnds)

More information about this package can be found here.

Marked as answer by dot_NET_Junkie on 11/4/2013 at 1:49 AM