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

Я знаю две вещи неясного выхода, которые делают их отличающимися. Пойдите меня!

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

    // A list of actions to execute later
    List actions = new List();

    // Numbers 0 to 9
    List numbers = Enumerable.Range(0, 10).ToList();

    // Store an action that prints each number (WRONG!)
    foreach (int number in numbers)
        actions.Add(() => Console.WriteLine(number));

    // Run the actions, we actually print 10 copies of "9"
    foreach (Action action in actions)
        action();

    // So try again
    actions.Clear();

    // Store an action that prints each number (RIGHT!)
    numbers.ForEach(number =>
        actions.Add(() => Console.WriteLine(number)));

    // Run the actions
    foreach (Action action in actions)
        action();

Список. Метод ForEach не имеет этой проблемы. Текущий объект повторения передается значением как аргумент внешней лямбде, и затем внутренняя лямбда правильно получает тот аргумент в своем собственном закрытии. Проблема решена.

(Печально я верю, ForEach является членом Списка, а не дополнительного метода, хотя легко определить его самостоятельно, таким образом, у Вас есть это средство на любом перечислимом типе.)

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

Больше здесь

12
задан Matt Kocaj 16 February 2010 в 15:36
поделиться

4 ответа

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

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

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

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

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

и т.д ...

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

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

32
ответ дан 2 December 2019 в 03:54
поделиться

Является ли репозиторий на таблицу общим?

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

Кроме того, вы можете абстрагироваться от некоторых функций из всех репозиториев ... и, поскольку вы используете 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();
        }
    }
4
ответ дан 2 December 2019 в 03:54
поделиться

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

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

1
ответ дан 2 December 2019 в 03:54
поделиться

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

1
ответ дан 2 December 2019 в 03:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: