Загрузка субкокков в шаблоне репозитория

Использование LINQ TO SQL в качестве основы решения на основе репозитория. Моя реализация такова:

IRepository

FindAll
FindByID
Insert
Update
Delete

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

WhereSomethingEqualsTrue() ...

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

У моего репозитория есть N ролей. Создать репозиторий Roles для управления ролями? Я беспокоюсь, что в конечном итоге я создам десятки репозиториев (1 за таблицу почти за исключением таблиц Join), если я поеду по этому маршруту. Является ли общий репозиторий для таблицы?

Ответ 1

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

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

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

Операции домена вашего репозитория должны выглядеть больше:

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

и т.д...

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

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

Ответ 2

Является ли общий репозиторий для таблицы?

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

Кроме того, вы можете абстрагировать некоторые функции из всех репозиториев... и, поскольку вы используете 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();
        }
    }

Ответ 3

Мне шаблон репозитория касается размещения тонкой оболочки вокруг вашей методологии доступа к данным. 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 для объектов и все это, используя IQuerable и др. вместо того, чтобы просто строить методы обслуживания, которые выплющают то, что мне действительно нужно. Пользователь 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 simple methods.
    return activeUsersInRole;
}

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

Ответ 4

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