ASP.net MVC Controller - использование конструктора - PullRequest
34 голосов
/ 19 декабря 2010

Я работаю над приложением ASP.net MVC, и у меня есть вопрос об использовании конструкторов для моих контроллеров.

Я использую Entity Framework и linq to Entities для всех моих транзакций данных.Мне нужно получить доступ к моей модели Entity для почти всех моих действий контроллера.Когда я впервые начал писать приложение, я создавал объект-сущность в начале каждого метода Action, выполняя любую необходимую работу, а затем возвращал свой результат.

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

Я все еще спрашиваю себя, какой путь правильный.Мне интересно А.) Какой метод наиболее подходит?B.) Как долго эти объекты живут в методе конструктора?C.) Есть ли проблемы с производительностью / целостностью метода конструктора?

Спасибо

Ответы [ 2 ]

30 голосов
/ 19 декабря 2010

RCravens имеет некоторые отличные идеи. Я хотел бы показать, как вы можете реализовать его предложения.

Было бы хорошо начать с определения интерфейса для реализуемого класса доступа к данным:

public interface IPostRepository 
{
    IEnumerable<Post> GetMostRecentPosts(int blogId);
}

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

public class PostRepository : IPostRepository
{
    public IEnumerable<Post> GetMostRecentPosts(int blogId)
    {
        // A using statement makes sure the context is disposed quickly.
        using(var context = new BlogContext())
        {
            return context.Posts
                .Where(p => p.UserId == userId)
                .OrderByDescending(p => p.TimeStamp)
                .Take(10)
                // ToList ensures the values are in memory before disposing the context
                .ToList(); 
        }
    }
}

Теперь ваш контроллер может принять одно из этих хранилищ в качестве аргумента конструктора:

public class BlogController : Controller
{
    private IPostRepository _postRepository;
    public BlogController(IPostRepository postRepository)
    {
        _postRepository = postRepository;
    }

    public ActionResult Index(int blogId)
    {
        var posts = _postRepository.GetMostRecentPosts(blogId);
        var model = new PostsModel { Posts = posts };
        if(!posts.Any()) {model.Message = "This blog doesn't have any posts yet";}
        return View("Posts", model);
    }

}

MVC позволяет вам использовать собственную фабрику контроллеров вместо настроек по умолчанию, поэтому вы можете указать, что ваша структура IoC, такая как Ninject, решает, как создаются контроллеры. Вы можете настроить свою инфраструктуру внедрения так, чтобы при запросе IPostRepository он создавал объект PostRepository.

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

var repositoryMock = new Mock<IPostRepository>();
repositoryMock.Setup(r => r.GetMostRecentPosts(1))
    .Returns(Enumerable.Empty<Post>());
var controller = new BlogController(repositoryMock.Object);
var result = (ViewResult)controller.Index(1);
Assert.IsFalse(string.IsNullOrEmpty(result.Model.Message));

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

29 голосов
/ 19 декабря 2010

Вы задаете правильные вопросы.

A.Определенно неуместно создавать эти зависимости внутри каждого метода действия.Одной из главных особенностей MVC является способность разделять проблемы.Загружая свой контроллер с этими зависимостями, вы делаете контроллер толстым.Они должны быть введены в контроллер.Существуют различные варианты внедрения зависимостей (DI).Обычно эти типы объектов могут быть либо вставлены в конструктор, либо в свойство.Я предпочитаю инжектор конструктора.

B.Время жизни этих объектов будет определяться сборщиком мусора.GC не является детерминированным.Поэтому, если у вас есть объекты, которые имеют соединения с ограниченными ресурсами (соединения с базой данных), вам может потребоваться закрыть эти соединения самостоятельно (вместо того, чтобы полагаться на удаление).Во многих случаях проблемы «времени жизни» выделяются в контейнер инверсии управления (IOC).Есть много там.Я предпочитаю Ninject.

C.Затраты на создание экземпляров, вероятно, минимальны.Стоимость транзакций с базой данных - это то, на чем вы, вероятно, хотите сосредоточить свое внимание.Существует концепция под названием «единица работы», которую вы можете рассмотреть.По сути, база данных может обрабатывать транзакции, превышающие одну операцию сохранения / обновления.Увеличение размера транзакции может привести к повышению производительности БД.

Надеюсь, что вы начали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...