Внедрение зависимостей

Инверсия управления

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 31.10.2015

В небольших приложениях на ASP.NET MVC мы относительно легко можем заменить одни классы на другие, вместо одного контекста данных использовать другой. Однако в крупных приложениях это уже будет проблематично сделать, особенно если у нас десятки контроллеров с сотней методов. В этой ситуации нам на помощь может прийти такой механизм как внедрение зависимостей

Рассмотрим простой пример. Допустим, у нас есть некая модель Book:

public class Book
{
    public int Id { get; set; }
    public string Name { get; set; }
}

И пусть у нас определен контекст данных и репозиторий для управления объектами данной модели:

public class BookContext : DbContext
{
    public BookContext() :base("DefaultConnection"){}
	
    public DbSet<Book> Books { get; set; }
}
public class BookRepository : IDisposable
{
    private BookContext db = new BookContext();

	public void Save(Book b)
    {
        db.Books.Add(b);
        db.SaveChanges();
    }
    public IEnumerable<Book> List()
    {
        return db.Books;
    }
    public Book Get(int id)
    {
        return db.Books.Find(id);
    }
	
    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (db != null)
            {
                db.Dispose();
                db = null;
            }
        }
    }
	public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

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

public class HomeController : Controller
{
    BookRepository repo;
	public HomeController()
    {
        repo = new BookRepository();
    }
    public ActionResult Index()
    {
        return View(repo.List());
    }
}

Контроллер содержит ссылку на BookRepository и в данном случае он зависит от BookRepository. А сам объект BookRepository является зависимостью.

Контроллер ссылается на конкретную реализацию репозитория, поэтому между ними существует жесткая связь (tight coupling). На первый взгляд ничего плохого здесь нет, но, например, у меня изменилась схема подключения к базе данных: вместо MS SQL Servera я решил использовать MongoDB или MySql. Кроме того, может потребоваться динамически менять один класс на другой.

В данном случае жесткая связь привязывает контроллер к конкретной реализации репозитория. Такой код по мере расширения приложения сложнее поддерживать и сложнее тестировать. Поэтому рекомендуется уходить от использования жесткосвязанных компонентов к слабосвязанным. Для этого определим новый интерфейс IRepository и изменим класс репозитория:

public interface IRepository
{
    void Save(Book b);
    IEnumerable<Book> List();
    Book Get(int id);
}

public class BookRepository : IDisposable, IRepository
{
    private BookContext db = new BookContext();
	// методы

}

Параллельно с этим я могу определить и другой класс репозитория, например, для использования MySql:

public class MySqlRepository : IDisposable, IRepository
{
    // методы
}

И теперь мы можем избавить контроллер от жесткой зависимости от компонента:

public class HomeController : Controller
{
    IRepository repo;
    public HomeController(IRepository r)
    {
        repo = r;
    }
}

Теперь контроллер ничего не знает и не зависит от конкретной реализации репозитория. Теперь его создание выносится из класса контроллера во вне. Подобный процесс еще называется инверсией управления (inversion of control (IoC))

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850