При регистрации класса или объекта в контейнере можно указать объект LifetimeManager, который будет управлять временем жизни экземпляров в контейнере.

Класс LifetimeManager очень простой, в нем всего три метода GetValue, SetValue и RemoveValue, причем последний не используется.
При помещении объекта в контейнер вызывается метод SetValue, а при необходимости получить объект вызывается GetValue и если он вернул null, то создается новый объект.

В библиотеку Microsoft.Practices.Unity входит несколько менеджеров.

TransientLifetimeManager – ничего не сохраняет, GetValue всегда возвращает null, поэтому объект создается каждый раз. Этот менеджер используется по-умолчанию при вызове RegisterType.

ContainerControlledLifetimeManager – сохраняет объект в локальной переменной. Поэтому объект живет столько же, сколько и контейнер. Этот (вернее другой, но с таким же поведением) менеджер используется по-умолчанию при вызове RegisterInstance.

ExternallyControlledLifetimeManager – сохраняет слабую ссылку (WeakReference) на объект. При использовании этого менеджера и вызове RegisterInstance сам вызывающий код должен управлять временем жизни объекта, помещенного в контейнер. Когда используется RegisterType этот менеджер будет выдавать уже существующий экземпляр объекта если он есть.

PerThreadLifetimeManager – сохраняет объекты в ThreadStatic словаре. Таким образом каждый поток в программе будет использовать свой набор объектов.

Для применения Unity в ASP.NET приложениях очень легко реализовать LifetimeManager, который сохраняет объект в контексте или в сессии.

Другие области применения LifetimeManager

В Unity нет возможности регистрации метода создания объектов, но это очень легко исправить с помощью своего LifetimeManager и пары extension-методов.

public class FactoryLifetimeManager<T>: LifetimeManager
{
    Func<T> _factoryMethod;
    LifetimeManager _baseManager;

    public FactoryLifetimeManager(Func<T> factoryMethod, LifetimeManager baseManager)
    {
        _factoryMethod = factoryMethod;
        _baseManager = baseManager;
    }

    public override object GetValue()
    {
        var obj = _baseManager.GetValue();
        if (obj == null)
        {
            obj = _factoryMethod();
            SetValue(obj);
        }
        return obj;
    }

    public override void RemoveValue()
    {
        _baseManager.RemoveValue();
    }

    public override void SetValue(object newValue)
    {
        _baseManager.SetValue(newValue);
    }
}

public static class UnityFactoryMethodExtensions
{

    public static IUnityContainer RegisterFactory<T>(this IUnityContainer container, Func<T> factoryMethod)
    {
        return container.RegisterFactory<T>(factoryMethod, new TransientLifetimeManager());
    }
    
    public static IUnityContainer RegisterFactory<T>(this IUnityContainer container, Func<T> factoryMethod, LifetimeManager lifetimeManager)
    {
        return container.RegisterType<T>(new FactoryLifetimeManager<T>(factoryMethod, lifetimeManager));
    }

    public static IUnityContainer RegisterFactory<T>(this IUnityContainer container, Func<T> factoryMethod, string name)
    {
        return container.RegisterFactory<T>(factoryMethod, name, new TransientLifetimeManager());
    }
    
    public static IUnityContainer RegisterFactory<T>(this IUnityContainer container, Func<T> factoryMethod, string name, LifetimeManager lifetimeManager)
    {
        return container.RegisterType<T>(name, new FactoryLifetimeManager<T>(factoryMethod, lifetimeManager));
    }
}

Теги : Unity, .NET, IoC-контейнер