ASP.NET MVC с использованием шаблона репозитория - PullRequest
11 голосов
/ 30 сентября 2010

В настоящее время я использую EF и использую его datacontext напрямую во всех моих действиях, но так как я начал читать о слабой связи и тестируемости, я думаю, что это не лучший путь. Я пытаюсь понять все «за» и «против» перед тем, как начать рефакторинг всего моего текущего кода.

Задача 1: Учитывая, что каждому объекту нужен свой собственный репозиторий, и, следовательно, он должен настроить свое собственное соединение с источником данных (допустим, база данных использует EF), разве это не даст много накладных расходов, если мне понадобятся данные от 5 различных объектов на одной странице?

Задача 2: Во всех примерах, которые я нашел в Интернете, я вижу также то, что большинство людей (даже таких как Шансельман) реализуют шаблон хранилища, используя классы сущностей, которые генерируются либо LINQ, либо EF, разве это не противоречит цели шаблона хранилища с помощью С уважением к слабой связи? С другой стороны, что является альтернативой, используя классы POCO в сочетании, например, с AutoMapper? (это меня немного пугает)

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

Ответы [ 6 ]

3 голосов
/ 30 сентября 2010

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

Для реализации Скотта Н, я предполагаю, что вы имеете в виду приложение Nerd Dinnerкоторый, по его собственному признанию, на самом деле не является шаблоном репозитория.

Цель шаблона репозитория, как вы предполагаете, состоит в том, чтобы изолировать хранилище данных от слоев над ним.Это не просто для целей тестирования, оно также позволяет вам изменить хранилище резервных копий, не затрагивая ваш пользовательский интерфейс / бизнес-логику.

С точки зрения пуристов, вы создадите POCO, которые вы вернете из репозитория в свой BL, используяинтерфейс для определения контракта с репозиторием, который вы могли бы затем передать и использовать интерфейс, а не конкретную реализацию.Это позволит вам передать любой объект, который реализует интерфейс Repository, будь то ваш живой репозиторий или фиктивный репозиторий.

На самом деле я использую репозиторий с MVC с Linq to SQL в качестве резервного хранилища, что позволяет мнестепень гибкости по отношению к фактическому резервному хранилищу, поэтому в моем BL я использую объекты L2S, созданные вручную, у них есть дополнительные поля и функции, которые не сохраняются в резервном хранилище.Таким образом, я получаю отличные функциональные возможности от аспектов L2S, отслеживания изменений, иерархии объектов и т. Д., А также позволяю мне заменять фиктивный репозиторий на TDD.

3 голосов
/ 30 сентября 2010

Вы можете прочитать эту книгу .Есть хороший пример использования шаблона Repository и LINQ.
Также есть эта статья Использование шаблонов Repository и Unit of Work с Entity Framework 4.0 .

3 голосов
/ 30 сентября 2010

ObjectContext использует пул соединений, поэтому он не будет таким неэффективным, как вы думаете.Кроме того, SQL-серверы (то есть MSSQL) действительно оптимизированы для множества одновременных соединений.

Что касается того, как реализовать это, я бы выбрал некоторый интерфейс IRepository.Затем вы можете создать определенные интерфейсы, например PostRepository> IRepository, и, наконец, реализовать их в конкретных классах (например, реальном классе и поддельном в памяти для тестирования).

2 голосов
/ 01 февраля 2011

Проблема 2. Чтобы избежать этого, можно использовать что-то вроде « ADO.NET C # POCO Entity Generator ».

2 голосов
/ 30 сентября 2010

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

Наше приложение разделено на модули, и каждый модуль разделен на три уровня: Веб (фронтальный)конец), ядро ​​(бизнес) и данные.В нашем случае каждому из этих уровней присваивается свой собственный проект, поэтому существует жесткое принудительное применение, предотвращающее тесную связь наших зависимостей.

Слой Core содержит служебные классы, POCO,и интерфейсы репозитория.

Слой Web использует эти классы и интерфейсы для получения необходимой информации.Например, контроллер MVC может принимать конкретный интерфейс репозитория в качестве аргумента конструктора, поэтому наша инфраструктура IoC внедряет правильную реализацию этого репозитория при создании контроллера.Интерфейс репозитория определяет методы селектора, которые возвращают наши объекты POCO (также определенные на бизнес-уровне Core).

Вся ответственность уровня Data заключается в реализации интерфейсов репозитория, определенных на уровне Core.Он имеет контекст Entity Framework, который представляет наше хранилище данных, но вместо того, чтобы возвращать сущности (которые технически являются «объектами данных»), он возвращает POCO, определенные на уровне Core (наши «бизнес-объекты»).

Чтобы уменьшить количество повторений, у нас есть абстрактный обобщенный класс EntityMapper, который предоставляет базовые функциональные возможности для сопоставления сущностей с POCO.Это делает большинство наших реализаций репозитория чрезвычайно простыми.Например:

public class EditLayoutChannelEntMapper : EntityMapper<Entity.LayoutChannel, EditLayoutChannel>,
    IEditLayoutChannelRepository
{
    protected override System.Linq.Expressions.Expression<Func<Entity.LayoutChannel, EditLayoutChannel>> Selector
    {
        get
        {
            return lc => new EditLayoutChannel
                             {
                                 LayoutChannelId = lc.LayoutChannelId,
                                 LayoutDisplayColumnId = lc.LayoutDisplayColId,
                                 ChannelKey = lc.PortalChannelKey,
                                 SortOrder = lc.Priority
                             };
        }
    }
    public EditLayoutChannel GetById(int layoutChannelId)
    {
        return SelectSingle(c => c.LayoutChannelId == layoutChannelId);
    }
}

Благодаря методам, реализованным базовым классом EntityMapper, в вышеуказанном репозитории реализован следующий интерфейс:

public interface IEditLayoutChannelRepository
{
    EditLayoutChannel GetById(int layoutChannelId);
    void Update(EditLayoutChannel editLayoutChannel);
    int Insert(EditLayoutChannel editLayoutChannel);
    void Delete(EditLayoutChannel layoutChannel);
}

EntityMappers очень мало делают в своих конструкторах, поэтомухорошо, если у контроллера есть несколько зависимостей репозитория.Entity Framework не только повторно использует соединения, но и сами контексты Entity создаются только при вызове одного из методов репозитория.

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

1 голос
/ 30 сентября 2010

ADO.NET пул соединений будет управлять подключениями за кулисами.В принципе не имеет значения, сколько разных сущностей (и, следовательно, хранилищ с собственным контекстом) вы используете;каждая операция БД будет принимать соединения из одного и того же пула.

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

...