Службы .NET REST, Entity Framework и слабая связь

Я работаю над проектом веб-приложения с использованием ASP.NET MVC3 и базы данных в SQL Server. Также существует мобильное приложение, использующее данные из той же базы данных через службы REST. Вот некоторые из слоев моего приложения:

  • Модель - модель данных ADO.NET с использованием Entity Framework.

  • Уровень доступа к данным - репозитории с запросами на получение данных из базы данных.

  • Веб-приложение - проект MVC3, использующий репозитории, слабая связь с использованием карты структуры и DI, контекст базы данных удаляется в конце HttpRequest.

  • Ядро - еще один уровень между DAL и уровнем обслуживания, использует репозитории и предоставляет данные на уровень обслуживания. Вроде уровня бизнес-логики.

  • Уровень обслуживания - службы REST, знают о базовом уровне, но не знают о DAL. Сопоставляет данные с DTO и предоставляет клиенту

Проблема, с которой я столкнулся с такой архитектурой приложения, заключается в слабой связи на уровне обслуживания. Уровень обслуживания ссылается на базовый уровень. Базовый уровень имеет ссылку на уровень доступа к данным и использует его репозитории. Однако в репозиториях нет конструктора по умолчанию. Они ожидают 1 параметр и контекст его объекта базы данных (одноразовый объект).

Использование репозиториев прямо на моем сайте не проблема. Я использую карту структуры, и DI делает ее слабо связанной. Каждый контекст удаляется в конце HttpRequest.

Проблема в том, что уровень обслуживания и уровень ядра. Я бы тоже хотел иметь слабую связь, но не знаете, как этого добиться? Как ввести в них контекст данных и убедиться, что они удаляются в определенный момент? Я хотел бы услышать несколько предложений, как все это собрать.


person Nebojsa Veron    schedule 11.07.2011    source источник


Ответы (2)


Уровень обслуживания ссылается на базовый уровень.

Хорошо.

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

Это не нормально.

Вашим «Ядром» должен быть ваш домен с бизнес-правилами и логикой. У него не должно быть никаких зависимостей.

Начать снизу стопки:

  1. Репо - никаких зависимостей от других слоев.
  2. Сервисы - зависимость от Core и Repo.
  3. Ядро - никаких зависимостей от других слоев.
  4. Интернет - зависит от всего.

Вот как мы это делаем. Мы используем комбинацию программирования, управляемого интерфейсом, и внедрения зависимостей, чтобы справиться со слабой связью.

Пример потока:

  1. Приходит HTTP-запрос (API, веб-уровень и т. Д.)
  2. Контроллер найден. Контейнер DI видит, что контейнер зависит от ISomethingService, и разрешает его, включая любые дальнейшие зависимости (сервис, репо и т. Д.).
  3. Контроллер вызывает метод на ISomethingService.
  4. ISomethingService реализация (выбранная DI) вызывает метод ISomeRepo.
  5. ISomeRepo реализация (выбранная DI) вызывает EF / DB, возвращает "объект-данные" сервису.
  6. Сервис сопоставляет «объект-данные» с объектом «Ядро» и возвращает контроллеру.

Создание экземпляров этих объектов должно выполняться вашим контейнером DI. Единственное, что мы используем выше, - это «Единица работы», которая, по сути, является оболочкой для контекста EF.

person RPM1984    schedule 11.07.2011
comment
У меня есть модель в другом проекте, поэтому репозитории зависят от этого уровня. Репозитории создаются из Model.edmx с использованием шаблона T4. У меня также нет проблем с DI и слабой связью в Интернете, он отлично работает, и контекст EF удаляется в конце запроса. Я также хотел бы упомянуть, что я использую не службы для получения данных в Интернете, а напрямую репозитории. Сервисы будут использоваться только для мобильных приложений. Я не уверен, как добиться слабой связи на стороне службы, поскольку служба создает репозиторий, которому требуется EF Context в качестве параметра в своем конструкторе. - person Nebojsa Veron; 12.07.2011
comment
@Nebo - хорошо ли Repo принимает контекст EF или интерфейс? Он должен иметь интерфейс, например IUnitOfWork. Под капотом вы бы реализовали EntityFrameworkUnitOfWork, который обертывает контекст EF - вот как вы получаете слабую связь. Только ваш репозиторий должен ссылаться на сборку EF. - person RPM1984; 12.07.2011
comment
Это один из моих репозиториев: screencast.com/t/IVHzTVIx. Это один из менеджеров в Core (между Repo и Service): screencast.com/t/qCgoVTlGLZy. И это сервис (здесь нет конструктора, использующего UserManager, потому что у меня проблемы с его размещением): screencast.com/t / Is3zpkrVP. ServiceHost поддерживает только типы обслуживания классов, поэтому я не могу разместить интерфейс, а только класс. - person Nebojsa Veron; 12.07.2011
comment
Я понял то, что вы сказали, и теперь у DI работает хорошо. У меня есть эта строка кода, чтобы она работала: ObjectFactory.Configure(x => x.For<Entities>().Use(() => new Entities()));. Я использую StructureMap, но не уверен, что мой DataContext будет удален? Поскольку это не приложение MVC, я не могу удалить его в конце HttpRequest. Как заставить его утилизировать? - person Nebojsa Veron; 12.07.2011
comment
@Nebo - попробуйте использовать For<Entities>().HybridHttpOrThreadLocalScoped().Use(() => new Entities());. Я также использую структурную карту, и это то, что я использую. В вопросе говорится, что вы работаете над приложением MVC 3, но теперь вы говорите, что нет? - person RPM1984; 13.07.2011
comment
@Nebo - глядя на ваши картинки, вы не используете интерфейсы для зависимостей, это ваша проблема. Ваш репозиторий должен иметь интерфейс, а не объекты. Интерфейс может быть IUnitOfWork или другим классом, который является оболочкой для контекста EF. То же самое и с вашим менеджером - IRepository<Activity> или IActivityRepository, а не ActivityRepository. Вы не используете реальные преимущества DI, если не программируете через интерфейсы. - person RPM1984; 13.07.2011
comment
Мой Web - это MVC3, и у меня нет проблем с этим, но с сервисами отдыха WCF. Спасибо за решение Scope. Тот же вопрос, что и ниже, зачем нужны интерфейсы репозитория? В каждом репозитории есть определенные методы для извлечения данных, но не все методы нужны в каждом. Моя практика заключалась в том, чтобы писать частичные классы репозитория с разными запросами в каждом. Работа с интерфейсами означает объявление всех методов, которые каждый интерфейс будет реализовывать, независимо от того, нужно ли оно ему. Альтернативой является другой интерфейс для каждого репо (IActivityRepository, как вы сказали). - person Nebojsa Veron; 13.07.2011
comment
@Nebo - у вас должен быть абстрактный класс с именем GenericRepository<T>, который реализует основные функции EF (запрос, сохранение, удаление и т. Д.), А затем определенные репозитории, наследующие от этого, и предоставляющие дополнительные функции. В любом случае решать вам - это все вопрос ваших предпочтений. Не уверен, чем еще я могу помочь. - person RPM1984; 13.07.2011

public ServiceLayerClass()
{
    private ICoreLayerClass coreLayerClass;

    public ServiceLayerClass(ICoreLayerClass coreLayerClass)
    {
        this.coreLayerClass = coreLayerClass;
    }

    public void DoSomeWork()
    {
        coreLayerClass.DoSomeWork();
    }
}

public CoreLayerClass()
{
    private ISomeRepository someRepository;

    public CoreLayerClass(ISomeRepository someRepository)
    {
        someRepository = this.someRepository;
    }

    public void DoSomeWork()
    {
        someRepository.DoSomeWork();
    }
}

public SomeRepository()
{
    public SomeRepository(IUnitOfWork uow)
    {
    }

    public void DoSomeWork()
    {
        //do some work
    }
}

Примечания. В идеале UnitOfWork следует создавать для HttpContext. то есть ваш datacontext начнет свою жизнь в начале Request и будет удален в конце. Вы будете использовать только один для каждого запроса.

person ravi    schedule 11.07.2011
comment
Вот как у меня это сейчас есть. Просто не знаю, как настроить DI для разрешения всех зависимостей, от службы до репозитория, и как обеспечить удаление IUnitOfWork (контекст данных). - person Nebojsa Veron; 12.07.2011
comment
Я пытаюсь создать свой DataContext для HttpContext, но поскольку это веб-служба, а не приложение MVC (где оно удаляется в конце запроса), я получаю неизвестную ошибку StructureMap, если я настроил жизненный цикл для HttpContextScoped для мой DataContext. - person Nebojsa Veron; 12.07.2011
comment
Это WCF Rest или ASP.Net MVC Rest? - person ravi; 13.07.2011
comment
Это сервис отдыха WCF. Другой вопрос, зачем нужны интерфейсы репозитория? В каждом репозитории есть определенные методы для извлечения данных, но не все методы нужны в каждом. - person Nebojsa Veron; 13.07.2011
comment
Конечно, в каждом репозитории будут свои методы. Но наличие интерфейсов помогает при модульном тестировании, поскольку вы можете внедрить другую имплантацию в определенные сценарии модульного тестирования. - person ravi; 13.07.2011
comment
Что касается DataContext и StructureMap, вам лучше задать вопрос, конкретно упомянув эти ключевые слова, и вы можете привлечь внимание экспертов по StructureMap. - person ravi; 13.07.2011