Загрузка подзаписей в шаблоне репозитория - PullRequest
12 голосов
/ 03 августа 2009

Использование LINQ TO SQL в качестве основы решения на основе репозитория. Моя реализация выглядит следующим образом:

IRepository

FindAll
FindByID
Insert
Update
Delete

Тогда у меня есть методы расширения, которые используются для запроса результатов как таковые:

WhereSomethingEqualsTrue() ...

Мой вопрос таков:

Хранилище «Мои пользователи» имеет N ролей. Создать ли репозиторий ролей для управления ролями? Я волнуюсь, что в конечном итоге я создам десятки репозиториев (по 1 на таблицу почти за исключением таблиц соединения), если я пойду по этому пути. Распространен ли репозиторий на таблицу?

Ответы [ 4 ]

32 голосов
/ 11 августа 2009

Если вы строите свой репозиторий так, чтобы он был специфичен для одной сущности (таблицы), так чтобы каждая сущность имела список методов в интерфейсе IRepository, который вы перечислили выше, тогда вы действительно делаете реализацию Активная запись шаблон.

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

Я предполагаю из вашего поста, что вы видели этот пример . Проблема с этим примером состоит в том, что все репозитории совместно используют одну и ту же функциональность CRUD на базовом уровне, но он не выходит за рамки этого и не реализует какие-либо функции домена. Все репозитории в этом примере выглядят одинаково - но в действительности реальные репозитории не все выглядят одинаково (хотя они все равно должны быть связаны), с каждой из них будут связаны определенные операции домена.

Ваши операции с доменом хранилища должны выглядеть примерно так:

userRepository.FindRolesByUserId(int userID)
userRepository.AddUserToRole(int userID)
userRepository.FindAllUsers()
userRepository.FindAllRoles()
userRepository.GetUserSettings(int userID)

и т.д ...

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

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

4 голосов
/ 03 августа 2009

Распространен ли репозиторий на таблицу?

Нет, но у вас все еще может быть несколько хранилищ. Вы должны построить хранилище вокруг агрегата.

Кроме того, вы можете абстрагировать некоторые функции от всех репозиториев ... и, поскольку вы используете Linq-to-Sql, вы, вероятно, можете ...

Вы можете реализовать базовый репозиторий, который в общих чертах реализует все эти общие функции.

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

    interface IRepository<T> : IDisposable where T : class
    {
        IEnumerable<T> FindAll(Func<T, bool> predicate);
        T FindByID(Func<T, bool> predicate);
        void Insert(T e);
        void Update(T e);
        void Delete(T e);
    }

    class MyRepository<T> : IRepository<T> where T : class
    {
        public DataContext Context { get; set; }

        public MyRepository(DataContext context)
        {
            Context = Context;
        }

        public IEnumerable<T> FindAll(Func<T,bool> predicate)
        {
            return Context.GetTable<T>().Where(predicate);
        }

        public T FindByID(Func<T,bool> predicate)
        {
            return Context.GetTable<T>().SingleOrDefault(predicate);
        }

        public void Insert(T e)
        {
            Context.GetTable<T>().InsertOnSubmit(e);
        }

        public void Update(T e)
        {
            throw new NotImplementedException();
        }

        public void Delete(T e)
        {
            Context.GetTable<T>().DeleteOnSubmit(e);
        }

        public void Dispose()
        {
            Context.Dispose();
        }
    }
1 голос
/ 16 августа 2009

Если мы напишем наш уровень хранилища так подробно, как предполагает Womp, что мы добавим в наш уровень обслуживания. Должны ли мы повторять одни и те же вызовы методов, которые в основном состоят из вызовов соответствующего метода репозитория, для использования в наших контроллерах или в коде? Это предполагает, что у вас есть сервисный уровень, где вы пишете свой валидацию, кэширование, рабочий процесс, код аутентификации / авторизации, верно? Или я далеко от базы?

1 голос
/ 11 августа 2009

Для меня шаблон репозитория - это создание тонкой оболочки вокруг вашей методологии доступа к данным. LINQ to SQL в твоем случае, но NHibernate, свернутый вручную в других. Я обнаружил, что создаю репозиторий для каждой таблицы, что очень просто (например, списки bruno у вас уже есть). Это отвечает за поиск вещей и выполнение операций CRUD.

Но тогда у меня есть уровень обслуживания, который больше касается совокупных корней, как упоминает Йоханнес. Я хотел бы иметь UserService с методом, подобным GetExistingUser (int id). Это вызвало бы внутренний метод UserRepository.GetById () для получения пользователя. Если ваш бизнес-процесс требует, чтобы пользовательский класс, возвращаемый GetExistingUser (), почти всегда нуждался в заполнении свойства User.IsInRoles (), тогда просто установите UserService как в UserRepository , так и RoleRepository. В псевдокоде это может выглядеть примерно так:

public class UserService
{
    public UserService(IUserRepository userRep, IRoleRepository roleRep) {...}
    public User GetById(int id)
    {
        User user = _userService.GetById(id);
        user.Roles = _roleService.FindByUser(id);
        return user;
}

userRep и roleRep будут созданы с вашими битами LINQ to SQL примерно так:

public class UserRep : IUserRepository
{
    public UserRep(string connectionStringName)
    {
        // user the conn when building your datacontext
    }

    public User GetById(int id)
    {
        var context = new DataContext(_conString);
        // obviously typing this freeform but you get the idea...
        var user = // linq stuff
        return user;
    }

    public IQueryable<User> FindAll()
    {
        var context = // ... same pattern, delayed execution
    }
}

Лично я хотел бы сделать классы репозитория внутренне ограниченными и сделать общедоступными классы UserService и другие XXXXXService, так что держите своих потребителей API сервиса честными. Поэтому я снова считаю, что репозитории более тесно связаны с процессом общения с хранилищем данных, но уровень обслуживания более тесно связан с потребностями вашего бизнес-процесса.

Я часто ловил себя на том, что действительно задумываюсь над гибкостью Linq to Objects и всего такого, и использую IQuerable et al вместо того, чтобы просто создавать методы обслуживания, которые выплевывают то, что мне действительно нужно. При необходимости используйте LINQ, но не пытайтесь заставить репозиторий делать все.

public IList<User> ActiveUsersInRole(Role role)
{ 
    var users = _userRep.FindAll(); // IQueryable<User>() - delayed execution;
    var activeUsersInRole = from users u where u.IsActive = true && u.Role.Contains(role);
    // I can't remember any linq and i'm type pseudocode, but
    // again the point is that the service is presenting a simple
    // interface and delegating responsibility to
    // the repository with it's simple methods.
    return activeUsersInRole;
}

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

...