Подтвердить что ты не робот

ASP.net MVC Controller - использование конструктора

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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 позволяет вам использовать собственный контроллер Factory вместо значения по умолчанию, поэтому вы можете указать, что ваша инфраструктура 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));

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