Шаблон репозитория MVC 3 / EF и правильный доступ к данным - PullRequest
1 голос
/ 16 февраля 2012

Будучи довольно новым для MVC 3 и EF, я пытаюсь понять лучший архитектурный подход к разработке приложения для моей компании.Это будет крупномасштабное приложение, которое может одновременно обрабатывать сотни пользователей, поэтому я хочу убедиться, что понимаю и соблюдаю надлежащие процедуры.До сих пор я определил, что простой подход к шаблону репозитория (такой как Controller -> Repository -> EF) - лучший и самый простой для реализации, но я не уверен, что это определенно лучший способ сделать что-то.Приложение будет в основном возвращать данные, которые отображаются пользователю в сетке devexpress, и они могут изменять эти данные / добавлять к ним и т. Д.

Я нашел эту статью, и это довольно запутанно для меня в настоящее время, поэтомуМне интересно, есть ли какая-либо причина пытаться работать с отключенным EF и почему вы бы хотели это сделать: http://www.codeproject.com/Articles/81543/Finally-Entity-Framework-working-in-fully-disconne?msg=3717432#xx3717432xx

Итак, подведем итог моим вопросам:

  • Допустим ли код ниже?
  • Должно ли это работать нормально для крупномасштабного приложения MVC?
  • Есть ли лучший способ?
  • Будут ли ненужные подключения к SQL оставаться открытыми из EF?(SQL Profiler делает его похожим на то, что он остается открытым некоторое время даже после завершения оператора using)
  • Является ли идея с автономной структурой лучшей, и зачем вам это вообще нужно?Я не верю, что нам нужно отслеживать данные по уровням ...

Примечание: В репозитории реализован IDisposable и есть метод dispose, указанный ниже.Он создает новый экземпляр контекста сущности в конструкторе хранилища.

Пример использования:

Контроллер (вход в систему с использованием пользовательского поставщика членства):

if (MembershipService.ValidateUser(model.UserName, model.Password))
{
    User newUser = new User();                    

    using (AccountRepository repo = new AccountRepository())
    {
         newUser = repo.GetUser(model.UserName);
         ...
    }
}

Поставщик членства ValidateUser:

public override bool ValidateUser(string username, string password)
{
    using (AccountRepository repo = new AccountRepository())
    {
        try
        {
            if (string.IsNullOrEmpty(password.Trim()) || string.IsNullOrEmpty(username.Trim()))
                return false;

            string hash = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Trim(), "md5");

            bool exists = false;

            exists = repo.UserExists(username, hash);

            return exists;
        }catch{
            return false;
        }
    }
}

Методы репозитория учетной записи для GetUser & UserExists:

Получить пользователя:

public User GetUser(string userName)
    {
        try
        {
            return entities.Users.SingleOrDefault(user => user.UserName == userName);
        }
        catch (Exception Ex)
        {
            throw new Exception("An error occurred: " + Ex.Message);
        }           
    }

Пользователь существует:

 public bool UserExists(string userName, string userPassword)
 {
        if (userName == "" || userPassword == "")
            throw new ArgumentException(InvalidUsernamePassword);

        try
        {
            bool exists = (entities.Users.SingleOrDefault(u => u.UserName == userName && u.Password == userPassword) != null);
            return exists;
        }
        catch (Exception Ex)
        {
            throw new Exception("An error occurred: " + Ex.Message);
        } 
    }

Фрагменты репозитория (конструктор, удаление и т. Д.):

    public class AccountRepository : IDisposable
    {
         private DbContext entities;

         public AccountRepository()
         {
            entities = new DbContext();
         }

         ...

         public void Dispose()
         {
            entities.Dispose();
         }
    }

Ответы [ 2 ]

5 голосов
/ 16 февраля 2012

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

Основная причина - минимизация доступа к базе данных.Так, например, посмотрите на свой репозиторий и обратите внимание на метод GetUser().Теперь отступите от кода и подумайте, как будет использоваться ваше приложение.Теперь подумайте о том, как часто вы будете запрашивать данные из пользовательской таблицы без каких-либо дополнительных данных.Ответ почти всегда будет «редко», если вы не создаете базовое приложение для ввода данных.

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

Один из способов - вызывать каждый метод репозитория индивидуально, например так:

var user = userRepository.GetUser("KallDrexx");
var companies = companyRepository.GetCompaniesForUser(user.Id);

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

public class UserRepository
{
    public User GetUser(string userName)
    {
        // GetUser code          
    }

    public User GetUserWithCompanies(string userName)
    {
        // query code here
    }
}

Так что теперь, если вам нужны пользователи и сообщают их контактные данные водин запросТеперь вам нужно добавить еще один метод в свой пользовательский репозиторий.Теперь предположим, что вам нужно выполнить еще один запрос, который также возвращает количество клиентов в каждой компании, поэтому вам нужно добавить еще один метод (или добавить необязательный параметр).Теперь предположим, что вы хотите добавить запрос, который возвращает все компании и пользователей, которых они содержат.Теперь вам нужен новый метод запроса, но возникает вопрос: вы помещаете его в репозиторий User или Company?Как вы отслеживаете, в каком из них он находится, и облегчаете выбор между GetUserWithCompany и GetCompanyWithUsers, когда вам это понадобится позже?

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

Ключом к успешному доступу к данным является хорошее планирование.Вы знаете, как будут выглядеть ваши экраны?Знаете ли вы, как пользователи будут использовать вашу систему?Знаете ли вы все данные, которые на самом деле будут на каждом экране?Если ответ на любой из этих вопросов - «нет», то вам нужно сделать шаг назад и забыть о слое данных, поскольку уровень данных (или должен быть для хорошего приложения) определяется на основе того, как приложение будет на самом деле.пользовательский интерфейс и экраны не должны зависеть от того, как был разработан уровень данных.Если вы не учитываете потребности пользовательского интерфейса и сценарии использования пользователей при разработке доступа к данным, ваше приложение не будет хорошо масштабироваться и не будет работать.Иногда это не проблема, если вы не планируете, чтобы ваш сайт был большим, но никогда не помешало бы помнить об этом.

1 голос
/ 18 февраля 2012

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

public class MyController : Controller
{
    private Entities context = new Entities();

    ...

    public override void Dispose()
    {
        context.Dispose();
    }
}

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

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

repository.Users.GetUser(userName);

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

private UserRepository users;

public UserRepository Users
{
    get
    {
        If (users == null)
        {
            users = new UserRepository(this);
        }
        return users;
    }
}

Затем вы можете выставить свой контекст этим другим загруженным ленивым классам через свойство.

Я не думаю, что это обязательно противоречит шаблону KallDrexx. Его метод просто переворачивает это так вместо

repository.Users.GetUser(userName);

У вас будет что-то вроде

UserQuery query = new UserQuery(repository.Users);

Это становится проблемой синтаксиса. Вы хотите это:

repository.Area.Query(value1, value2, ...);

или это:

AreaQuery query = new AreaQuery { Property1 = value1, ... };

Последнее на самом деле работает лучше с привязкой модели, но, очевидно, более многословно, когда вам действительно нужно ее кодировать.

Лучший совет, который дал KallDrexx, - просто введите свой код и свои действия, а затем выясните это. Если вы делаете простой CRUD, то пусть MVC создает и заполняет вашу модель, тогда все, что вам нужно сделать, это присоединить и сохранить. Если вы обнаружите, что можете повторно использовать код, переместите его туда, где его можно использовать повторно. Если ваше приложение становится слишком сложным, попробуйте некоторые из этих рекомендаций, пока не найдете то, что вам подходит.

...