Внедрение зависимостей с n-уровневым решением Entity Framework

12

В настоящее время я разрабатываю n-уровневое решение, которое использует Entity Framework 5 (.net 4) в качестве стратегии доступа к данным, но беспокоюсь о том, как включить внедрение зависимостей, чтобы сделать его тестируемым / гибким.

Моя текущая схема решения выглядит следующим образом (мое решение называется Alcatraz):

Alcatraz.WebUI : проект веб-формы asp.net, интерфейс пользователя, ссылки на проекты Alcatraz.Business и Alcatraz.Data.Models .

Alcatraz.Business : проект библиотеки классов, содержит бизнес-логику, ссылки на проекты Alcatraz.Data.Access , Alcatraz.Data.Models

Alcatraz.Data.Access : проект библиотеки классов, содержит AlcatrazModel.edmx и AlcatrazEntitiesDbContext, ссылается на проекты Alcatraz.Data.Models .

Alcatraz.Data.Models : проект библиотеки классов, содержит POCO для модели Alcatraz, без ссылок.

Мое видение того, как это решение будет работать, заключается в том, что web-интерфейс мог бы создавать репозиторий в бизнес-библиотеке. Этот репозиторий будет иметь зависимость (через конструктор) от строки соединения (не AlcatrazEntitiesэкземпляра). Веб-интерфейс знал бы строки подключения к базе данных, но не то, чтобы это была строка подключения структуры сущностей.

В бизнес-проекте:

public class InmateRepository : IInmateRepository
{
    private string _connectionString;

    public InmateRepository(string connectionString)
    {
        if (connectionString == null)
        {
            throw new ArgumentNullException("connectionString");
        }

        EntityConnectionStringBuilder connectionBuilder = new EntityConnectionStringBuilder();

        connectionBuilder.Metadata = "res://*/AlcatrazModel.csdl|res://*/AlcatrazModel.ssdl|res://*/AlcatrazModel.msl";
        connectionBuilder.Provider = "System.Data.SqlClient";
        connectionBuilder.ProviderConnectionString = connectionString;

        _connectionString = connectionBuilder.ToString();
    }

    public IQueryable<Inmate> GetAllInmates()
    {
        AlcatrazEntities ents = new AlcatrazEntities(_connectionString);

        return ents.Inmates;
    }
}

В веб-интерфейсе:

IInmateRepository inmateRepo = new InmateRepository(@"data source=MATTHEW-PC\SQLEXPRESS;initial catalog=Alcatraz;integrated security=True;");

List<Inmate> deathRowInmates = inmateRepo.GetAllInmates().Where(i => i.OnDeathRow).ToList();

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

  1. Имеет ли этот дизайн смысл с точки зрения возможностей Entity Frameworks? Я слышал, что Entity Framework уже использует шаблон Unit-of-Work, я просто излишне добавляю еще один слой абстрактных данных?

  2. Я не хочу, чтобы мой веб-интерфейс напрямую связывался с Entity Framework (или даже ссылался на него), я хочу, чтобы весь доступ к базе данных проходил через бизнес-уровень, поскольку в будущем у меня будет несколько проектов, использующих один и тот же бизнес-уровень. (веб-сервис, приложение Windows и т. д.), и я хочу, чтобы его было легко поддерживать / обновлять, располагая бизнес-логику в одной центральной области. Это подходящий способ для достижения этого?

  3. Должен ли бизнес-уровень даже содержать репозитории или он должен содержаться на уровне доступа? Если там, где они находятся, все в порядке, является ли передача строки соединения хорошей зависимостью?

Спасибо, что нашли время, чтобы прочитать!

Мэтью
источник

Ответы:

11

То, как вы делаете DI, неверно.

Во-первых, строка подключения относится к уровню данных. Или в файле web.config.

Следующая абстракция, с которой вы будете иметь дело, это DbContext, а не строка соединения. Ваши репозитории не должны знать о строках подключения. Ваша бизнес-логика не будет знать о DbContext и т. Д.

Ваш пользовательский интерфейс не будет иметь представления и не будет создавать ничего, связанного с EF.

Конкретные ответы на ваши вопросы:

  1. Не добавляйте абстракции, пока вы не очень хорошо знакомы с EF. Он уже добавляет хорошие абстракции, такие как UoW, запросы, использование POCO и т. Д.

  2. Чтобы DI работал, у вас есть Composition Root, который ссылается на все необходимые компоненты. Это может или не может быть в проекте WebUI. Если это не так, вы должны ожидать, что он не ссылается на EF или любые другие технологии, связанные с данными.

  3. Стоп прямо здесь. Хватит добавлять абстракции поверх абстракций. Начните с прямой и «наивной» архитектуры и развивайте ее с течением времени.

Абстракции - это инструмент для борьбы со сложностью. Отсутствие сложности означает, что абстракции не нужны (пока).

Борис Янков
источник
Чтобы было ясно, что я понимаю, что вы говорите: хранилище (какой интерфейс существует в бизнесе, а конкретный существует в Alcatraz.Data.Access?) Принимает a DbContextкак свою зависимость. Бизнес-классы имеют репозитории в качестве зависимости. Для внедрения зависимости я делаю это вручную (поэтому я понимаю, что происходит). Причина, по которой я хочу установить строку подключения, DbContextзаключается в том, что я использую сегментирование базы данных, поэтому в некоторых случаях мне нужна структура сущностей для подключения к различным базам данных (с одинаковой структурой). Я правильно тебя понимаю?
Мэтью
Судя по предоставленному коду, вы вообще не используете DI. Основная цель DI - освободить вас и ваш код от управления зависимостями. Я не могу представить, чтобы вы делали это эффективно вручную без DI-контейнера.
Борис Янков
также держите свой разум открытым с DI. я уже, для забавы, задал точно такой же вопрос здесь, на другом форуме, чтобы получить противоположные ответы. DI это шаблон, а не архитектура. В зависимости от вашей цели вы можете решить использовать это или нет. Я использую это, но не по причинам, которые большинство людей говорят мне использовать это.
Бастьен Вандамм,
4

Несколько быстрых комментариев. Я лично, вероятно, не передал бы строку подключения. Во всяком случае, я бы попытался создать интерфейсы, может быть, для репозиториев и просто передать интерфейсы? Попросите репозитории внедрить или предоставить интерфейс IOW.

Таким образом, событие не обязательно должно быть базой данных, которая реализует ваши репозитории. они могут быть в кэш-памяти или что-то еще. Может быть, тогда вы могли бы использовать какую-то инфраструктуру внедрения зависимости, чтобы даже создать их экземпляр?

Итак, отвечая на некоторые ваши вопросы:

  1. Да думаю нормально
  2. Я все еще хотел бы, чтобы пользовательский интерфейс ссылался на проект EF и эталонные интерфейсы уровня бизнеса, которые реализует уровень репозитория EF. Таким образом, другие проекты могут по-прежнему использовать те же сборки, но у них есть возможность замены при желании?
  3. хммм, наверное репозитории на уровне доступа, но реализующие определение интерфейса выставлены на бизнес уровне ??

Это всего лишь некоторые мысли для размышления.

dreza
источник
Что касается пункта 2, одна цель, которую я пытался сделать, это не иметь CRUD непосредственно в пользовательском интерфейсе. Я имею в виду, что хочу убедиться, что только CRUD может произойти, пройдя бизнес-уровень, таким образом, он управляется.
Матфея