简体   繁体   中英

Why Simple Injector will not dispose instances resolved by a Func?

I'm trying to resolve instances by key as proposed in the How To section on the Simple Injector website.

I use a factory based on an dictionary. This dict will contain Func<> references to the DI container. When a instance should be created, than the DI container will be asked. In the original code the factory was created with a new() operator. I changed this to let the DI container dispose the factory automatically. (Please let me now if there exists another way to achive this.)

        var diContainer = new Container();

        //diContainer.RegisterSingleton<IBasicFactory>(new BasicFactory
        //{
        //    { "A", () => diContainer.GetInstance<A>() },
        //    { "B", () => diContainer.GetInstance<B>() },
        //});

        diContainer.RegisterSingleton<IBasicFactory, BasicFactory>();
        var instance = (BasicFactory) diContainer.GetInstance<IBasicFactory>();
        instance.Add("A", () => diContainer.GetInstance<A>());
        instance.Add("B", () => diContainer.GetInstance<B>());
        diContainer.Verify();

        var factory = diContainer.GetInstance<IBasicFactory>();
        factory.CreateInstance("A").SayHello();
        factory.CreateInstance("B").SayHello();
        diContainer.Dispose();

The creation on instances works well, but non of those returned by the factory (A and B) will be disposed when the DI contanier is disposed.

What I am doing wrong?


Here follows the other code:

using System;
using System.Collections.Generic;

public interface IBasic
{
    void SayHello();
}

public abstract class Basic : IBasic, IDisposable
{
    protected Basic()
    {
        System.Console.WriteLine("Creating instance of Basic");
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if(disposing)
            System.Console.WriteLine("Disposing instance of Basic");
    }

    public abstract void SayHello();
}

public interface IBasicFactory
{
    IBasic CreateInstance(string key);
}

public class BasicFactory : Dictionary<string, Func<IBasic>>, IBasicFactory, IDisposable
{
    public BasicFactory()
    {
        System.Console.WriteLine("Creating instance of BasicFactory");
    }

    public IBasic CreateInstance(string key)
    {
        Func<IBasic> createObject;
        if (this.TryGetValue(key, out createObject))
            return createObject();

        var msg = $"The parameter ${key} is not supported by this factory";
        System.Console.WriteLine(msg);
        throw new NotSupportedException(msg);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            System.Console.WriteLine("Disposing instance of BasicFactory");
            this.Clear();
        }
    }
}

public class A : Basic
{
    public override void SayHello()
    {
        System.Console.WriteLine("Hello A!");
    }
}

public class B : Basic
{
    public override void SayHello()
    {
        System.Console.WriteLine("Hello B!");
    }
}

You can see what you're doing wrong when you add an extra call to .Verify() in your demo application just before disposing the container:

factory.CreateInstance("A").SayHello();
factory.CreateInstance("B").SayHello();
diContainer.Verify();
diContainer.Dispose();

This call to verify will fail with an exception explaining everything you done wrong :-)

The error you made is that you didn't register your root components A and B explicitly in the container. A and B are considered root components because you resolve them directly from the container (using GetInstance<T> ) instead of injecting them into another component.

Registering your root componens explicitly is important, because it allows Simple Injector to analyse your complete object graph in a reliable manner.

Because Simple Injector wasn't aware of the existence of A and B during the time you called Verify it was unable to warn you that you registered a disposable component as transient. Simple Injector does not track transient components. If you need disposal, you will have to register them as scoped.

The advise given in the documentation about those named factories is in fact too simplistic and misses a warning about registring root components explicitly. My advise is to use a construct similar to the RequestHandlerFactory example in the documentation, because that example correctly registers all types allowing your configuration to be successfully verified:

public class BasicFactory : IBasicFactory {
    private readonly Dictionary<string, InstanceProducer> producers =
        new Dictionary<string, InstanceProducer>(StringComparer.OrdinalIgnoreCase);

    private readonly Container container;

    public BasicFactory(Container container) {
        this.container = container;
    }

    Basic IBasicFactory.CreateNew(string name) => (Basic)this.producers[name].GetInstance();

    public void Register<TImplementation>(string name, Lifestyle lifestyle = null)
        where TImplementation : class, Basic {
        lifestyle = lifestyle ?? Lifestyle.Transient;
        var producer = lifestyle.CreateProducer<Basic, TImplementation>(container);
        this.producers.Add(name, producer);
    }
}

Example:

var factory = new BasicFactory(container);
factory.Register<A>("A", Lifestyle.Scoped);
factory.Register<B>("B", Lifestyle.Scoped);

container.RegisterSingleton<IBasicFactory>(factory)

Your container does not store the object it created, only the method responsible for creating it.

I could try to come up with a solution, but I don't think that associating the disposing process of the container with the instance created by it (and, in this case, created by a factory created by the container) is a nice pattern. Imagine if you're using the container on, let's say, another thread, and you dispose an instance that it's being used by some other unrelated process. I would treat the disposing process separately.

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