Как сохранить контроллеры в ASP.NET MVC?

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

Таким образом, логика в контроллерах хранится как минимум, но у меня все еще есть очень тонкие фрагменты кода, которые выполняют некоторые задачи, такие как

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

некоторые более длинные случаи делают разные вещи в зависимости от "статуса", возвращаемого службой.

вот несколько примеров:

[HttpPost]
[AjaxOnly]
[Authorize]
public JsonResult Preview(string input)
{
    LinkResult parsed = linkService.ParseUserInput(input);
    if (parsed.Result == LinkParseResult.Used)
    {
        long? postId = parsed.Link.PostId;
        if (postId.HasValue)
        {
            Post post = postService.GetById(postId.Value, false);
            return Json(new
            {
                faulted = "used",
                link = DetailsRoute(post),
                id = postId
            });
        }
        else
        {
            return Json(new { faulted = "invalid" });
        }
    }
    else if (parsed.Result == LinkParseResult.Invalid)
    {
        return Json(new { faulted = "invalid" });
    }
    else
    {
        Link link = parsed.Link;
        if (link.Description != null && link.Description.Length > 200)
        {
            link.Description = link.Description.Substring(0, 200);
        }
        return AjaxView(link);
    }
}

и (Post поступает из домена, PostModel - модель представления)

private PostModel PostModelConverter(Post post)
{
    Link link = post.Link;
    if (link == null)
    {
        throw new ArgumentException("post.Link can't be null");
    }
    if (link.Type == LinkType.Html)
    {
        return new PostedLinkModel
        {
            Description = link.Description,
            PictureUrl = link.Picture,
            PostId = post.Id,
            PostSlug = postService.GetTitleSlug(post),
            Timestamp = post.Created,
            Title = link.Title,
            UserMessage = post.UserMessage,
            UserDisplayName = post.User.DisplayName
        };
    }
    else if (link.Type == LinkType.Image)
    {
        return new PostedImageModel
        {
            PictureUrl = link.Picture,
            PostId = post.Id,
            PostSlug = postService.GetTitleSlug(post),
            Timestamp = post.Created,
            UserMessage = post.UserMessage,
            UserDisplayName = post.User.DisplayName
        };
    }
    return null;
}

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

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

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

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

Ответ 1

Это задано несколько раз:
Бизнес-логика в контроллере
Где я должен поставить свою логическую логику контроллера в MVC3
Держать контроллеры тонкими

Также как и в других местах:
ASP MVC Best Practices - Skinny Controllers
Keep Controllers Thin

Сообщество, похоже, хорошо констатирует, что такая логика принадлежит вне контроллеров. Обычно в модели (или ViewModel), но где-то в бизнес-слое.

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

Ответ 2

private PostModel PostModelConverter(Post post)
{
    Link link = post.Link;
    if (link == null)
    {
        throw new ArgumentException("post.Link can't be null");
    }
    if (link.Type == LinkType.Html)
    {
        var model = AutoMapper.Map<PostedLinkModel>(post);
        model.PostSlug = postService.GetTitleSlug(post);
        return model;
    }
    else if (link.Type == LinkType.Image)
    {
        var model = AutoMapper.Map<PostedImageModel>(post);
        model.PostSlug = postService.GetTitleSlug(post);
        return model;
    }
    return null;
}

http://www.viddler.com/v/b568679c

Ответ 3

Контроллер не будет содержать логики домена

Контроллер должен отвечать только за:

Проверка ввода

Вызов модели для подготовки представления

Вернуть представление или перенаправить на другое действие

Если вы делаете какую-либо другую вещь, вы делаете ее в неправильном месте, это скорее ответственность Модели, которую вы выполняете в контроллере.

Если вы выполните это правило, ваш метод действия не будет содержать более 20 - 25 строк кода. У Яна Купера отличный пост Skinny Controller Fat Model, прочитайте его.