Unity – IoC-контейнер от Microsoft, разработанный группой Patterns&Practicies. Найти его можно по адресу http://www.codeplex.com/unity. Также Unity включен в состав Enterprise Library.

Будет использоваться версия 1.2, последняя на данный момент.

Итак приступим.
Как завещал дядька Фаулер будем рассматривать использования контейнера на примере программы, работающей  с базой данных фильмов.

Чтобы использовать Unity в своей программе надо подключить сборки Microsoft.Practices.ObjectBuilder2 и Microsoft.Practices.Unity.

Нам надо написать класс, который позволяет искать фильмы по различным параметрам.

Предположим что фильмы описываются классом Movie

public class Movie
{
    public string Title { get; set; }
    public string Director { get; set; }
    public int Year { get; set; }
}

Работу с БД завернем в интерфейс

public interface IMovieRepository
{
    IQueryable<Movie> GetMovies();
}

Теперь можем написать класс, который нам нужен. Следуя принципам IoC будем передавать зависимости через конструктор.

public class MovieFinder
{
    IMovieRepository _repository;

    public MovieFinder(IMovieRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable<Movie> FindByTitle(string q)
    {
        return _repository
                .GetMovies()
                .Where(m => m.Title.Contains(q));
    }
}

В целях тестирования напишем простую реализацию IMovieRepository.

public class InMemoryMovieRepository : IMovieRepository
{
    public IQueryable<Movie> GetMovies()
    {
        return new[]
        {
            new Movie
            {
                Title = "Гарри Поттер и узник Азкабана",
                Director = "Альфонсо Куарон",
                Year = 2004
            },
            new Movie
            {
                Title = "Звездные войны: Эпизод 2 - Атака клонов",
                Director = "Джордж Лукас",
                Year = 2002
            },
            new Movie
            {
                Title = "Властелин колец: Братство кольца",
                Director = "Питер Джексон",
                Year = 2001
            },
        }.AsQueryable();
    }
}

Напишем короткий код, который собирает все вместе.

//Создаем контейнер
var container = new UnityContainer();

//Помещаем в контейнер реализацию для используемого интерфейса
container.RegisterType<IMovieRepository, InMemoryMovieRepository>();

//Собираем нужный объект
var finder = container.Resolve<MovieFinder>();

Обратите внимание, что сам тип MovieFinder необязательно помещать в контейнер.

Теперь можно заняться базой данных.
Создадим EF модель с одной сущностью Movie, эту сущность будем использовать вместо нашего класса Movie.

Реализация IMovieRepository для БД будет выглядеть так:

public class EFMovieRepository: IMovieRepository
{
    MoviesContainer _context;

    public EFMovieRepository(MoviesContainer context)
    {
        _context = context;
    }

    public IQueryable<Movie> GetMovies()
    {
        return _context.Movies;
    }
}

Где MoviesContainer – тип контекста EF.

Чтобы использовать этот репозитарий слегка изменим код основной программы.

var container = new UnityContainer();
container
    .RegisterType<IMovieRepository, EFMovieRepository>()
    .RegisterType<MoviesContainer>(new InjectionConstructor());

var finder = container.Resolve<MovieFinder>();

Параметр new InjectionConstructor() для второго вызова RegisterType говорит контейнеру что надо использовать конструктор без параметров для создания объекта этого класса. Если конструктор в классе всего один, то задавать такой параметр не требуется.

В приведенном выше коде есть один недостаток. Все объекты создаются каждый раз при вызове Resolve. Если мы не хотим пересоздавать каждый раз контекст EF (это длительная операция), то можем сделать его синглтоном.
Делается это очень просто, достаточно заменить вызов
.RegisterType<MoviesContainer>(new InjectionConstructor())
на
.RegisterType<MoviesContainer>(
                          new ContainerControlledLifetimeManager(),
                          new InjectionConstructor())

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

В принципе этого достаточно чтобы начать использовать Unity в своем проекте.

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