26 июля 2011 г.

Загрузка связей по требованию в StructureMap

В одной из последних версий StructureMap появилась фича dependency lazy load. Суть простая - вместо того чтобы создавать все связанные объекты и передавать их в конструктор, StructureMap позволяет передавать Func в качестве аргумента конструктора. Тем самым клиентский код сам решает когда создать необходимый инстанс.

Рассмотрим пример кода:

namespace Test
{
    using System;

    public class TestMain
    {
        public static void Main(string[] args)
        {
            StructureMap.ObjectFactory.Configure(x =>
                {
                    x.For< IConnection >().Use< Connection >();
                    x.For< IReceiver >().Use< Receiver >();
                });

            IReceiver receiver = StructureMap.ObjectFactory.GetInstance< IReceiver >();
            receiver.StartListening();
            receiver.StopListening();
        }
    }

    public interface IReceiver
    {
        void StartListening();
        void StopListening();
    }
    public interface IConnection
    {
        void Open();
        void Close();
    }

    public class Receiver : IReceiver
    {
        private IConnection connection;

        public Receiver(IConnection connection)
        {
            Console.WriteLine("Receiver.ctor is called");
            this.connection = connection;
        }

        public void StartListening()
        {
            Console.WriteLine("Receiver.StartListening is called");
            this.connection.Open();
        }
        public void StopListening()
        {
            Console.WriteLine("Receiver.StopListening is called");
            this.connection.Close();
        }
    }
    public class Connection : IConnection
    {
        private Guid code;

        public Connection()
        {
            Console.WriteLine("Connection.ctor is called");
            code = Guid.NewGuid();
        }

        public void Open()
        {
            Console.WriteLine("Connection.Open is called");
            Console.WriteLine("Connection \"{0}\" is opened", this.code);
        }
        public void Close()
        {
            Console.WriteLine("Connection.Close is called");
            Console.WriteLine("Connection \"{0}\" is closed", this.code);
        }
    }
}

Код довольно простой и вряд ли нуждается в детальном пояснении. У нас есть 2 класса, один использует другой и оба реализуют свои интерфейсы.

При таком описании, вызов конструктора Connection произойдет до вызова конструктора Receiver. Более того, если бы Connection зависел бы от других объектов, то и их конструкторы были бы вызваны. Т.е. все дерево объектов было бы создано в памяти, лишь для того, чтобы передать Connection в Receiver.

В StructureMap появилась фича Lazy загрузки таких зависимостей. Для этого достаточно переписать объявление конструктора Receiver следующим образом:


public class Receiver : IReceiver
{
    private Func< IConnection > connection;

    public Receiver(Func< IConnection > connection)
    {
        Console.WriteLine("Receiver.ctor is called");
        this.connection = connection;
    }

    public void StartListening()
    {
        Console.WriteLine("Receiver.StartListening is called");
        this.connection().Open();
    }
    public void StopListening()
    {
        Console.WriteLine("Receiver.StopListening is called");
        this.connection().Close();
    }
}

Код биндинга StructureMap при этом не меняется.

Посмотрим на результат выполнения кода:




Из результатов видно, что вызов конструктора Connection происходит в методе Receiver.StartListening. Т.е. именно с этого момента мы создаем все дерево объектов. В результатах виден еще один важный момент: при вызове Func всегда создается новый инстанс объекта. Это видно в строчках, которые выводят "код" Connection - он разный для открытия и закрытия подключения.

Вот и все, что я хотел сегодня рассказать. Надеюсь, что кое-кому из моих читателей теперь не придется вызывать Factory.GetInstance< T > напрямую в коде, для того чтобы управлять временем жизни объектов ;)

Комментариев нет:

Отправить комментарий