Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
В небольших приложениях на 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))