【问题标题】:Fat controllers : How do i make it slim?脂肪控制器:我如何使它苗条?
【发布时间】:2014-12-10 10:11:47
【问题描述】:

我正在使用 EF 6 和 MVC 5 开发博客引擎。

我决定不使用存储库模式或 UoW,因为它已经在框架级别的 EF 6 中实现。

解决方案包含以下层。

DataModels 层:它有简单的 POCO,它们是自动生成的和一个 dbContext。

public partial class Article
    {
        public int Id { get; set; }
        public string Slug { get; set; }
        public string Title { get; set; }
        public string PostBody { get; set; }
        public System.DateTime CreatedOn { get; set; }
        public bool IsPublished { get; set; }
        public string Author { get; set; }
    }

服务层:

public interface IBlogEngine
    {
        List<Article> GetFrontPageBlogPosts();
        void SaveArticle(Article article);
        List<Article> GetArticlesByStatus(string isPublished);
        Article GetBySlug(string slug);
        Article GetById(int id);
        bool Exists(string slugUrl);
        void Delete(int id);
    }

IBlogEngine 实现。为简洁起见,省略了一些方法实现。

public class BlogEngine : IBlogEngine
    {
        private readonly dbContext _context;

        public BlogEngine(DbContext context)
        {
            _context = context;
        }


        public void SaveArticle(Article article)
        {
            if (article.Id == 0)
            {
                _context.Articles.Add(article);
            }
            else
            {
                _context.Entry(article).State = EntityState.Modified;
            }

            _context.SaveChanges();
        }



        public Article GetBySlug(string slug)
        {
            return _context.Articles.SingleOrDefault(x => x.Slug == slug.Trim());
        }


    }

界面层

 public class ArticleController : Controller
    {
        private readonly IBlogEngine _engine;
        public ArticleController(IBlogEngine engine)
        {
            _engine = engine;
        }

        [HttpGet]
        public ActionResult Edit(string slug)
        {
            if (string.IsNullOrWhiteSpace(slug))
            {
                return HttpNotFound();
            }

            var article = _engine.GetBySlug(slug);

            if (article == null)
            {
                return HttpNotFound();
            }

            var model = new EditViewModel { Id = article.Id, Slug = article.Slug, 
            Title = article.Title, PostBody = article.PostBody, IsPublished = true };

            return View("Create", model);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(EditViewModel blogPost)
        {
            if (!ModelState.IsValid)
            {
                return View("Create", blogPost);
            }
            // Get Article by Id
            var article = _engine.GetById(blogPost.Id);

            if (article == null)
            {
                return HttpNotFound();
            }

            // Update it
            article.Id = blogPost.Id;
            article.Title = blogPost.Title.Trim();
            article.Slug = blogPost.Slug.ToUrlSlug();
            article.PostBody = blogPost.PostBody;
            article.CreatedOn = DateTime.UtcNow;
            article.IsPublished = blogPost.IsPublished;
            article.Author = User.Identity.Name;

            // Save it
            _engine.SaveArticle(article);

            return RedirectToAction("Create", "Article");
        }

    }

问题 考虑这样一个场景,用户完成了对旧博客文章/文章的编辑并点击提交按钮来更新他的博客文章/文章。

我的 HTTP POST 编辑操作太胖了吗?我觉得控制器在这里做的事情也很可能。

  1. 从数据库中获取现有文章

  2. 用 ViewModel 值更新它

  3. 从服务层调用SaveArticle方法。

我怎样才能让这个控制器节食?

Service Layer 方法 SaveArticle 不应该完成从 Db 检索文章并使用新值更新它并调用 SaveChanges 方法的工作吗?

如果上述陈述为真,我如何将 ViewModel 传递给 ServiceLayer 方法?允许 ViewModel 泄漏到 Service 层不是一个糟糕的决定吗?

我该如何处理?我很困惑,需要一些帮助。

【问题讨论】:

  • 理论上您应该使用专用模型回发到您的控制器,而不是回发整个 ViewModel。使用专用模型,将该模型提供给存储库/服务层以执行其操作确实是正确的。
  • 我将整个 ViewModel 发布到控制器操作(HTTP POST),因为它是必需的,因为我不知道用户更改了哪些字段。你的意思是我应该在 HTTP POST 操作方法中使用 Article 而不是 EditViewModel 吗?
  • 对我来说看起来不错,并且正在做控制器 POST 方法应该做的事情。接受视图模型,验证它,映射到域模型,保存并重定向。
  • @StephenMuecke ServiceLayaer 方法可以返回 DTO 而不是 Domain 对象吗?因为,在某些方法中,我返回整个域对象,但在控制器级别,我只需要很少的属性。
  • 如果您使用视图模型仅显示/编辑您需要的数据,这不会有任何问题

标签: asp.net-mvc entity-framework design-patterns service-layer


【解决方案1】:

坦率地说,这让我感到困惑,有时也让我感到困惑,控制器应该做什么和做多少来服务请求。

在我的大部分实现中,我会执行以下操作:

  1. 在 Post 方法中接受 viewModel 输入对象(输入值在客户端验证)。
  2. 检查 ModelState。
  3. 将视图模型对象转换为域模型对象。我用AutoMapper
  4. 将其交给服务方法。它会执行操作需要执行的操作。
  5. 根据运算返回适当的结果。

我是你,我会写:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(EditViewModel blogPost)
    {
        if (!ModelState.IsValid)
        {
            return View("Create", blogPost);
        }

        // Use AutoMapper for ViewModel to DomainModel conversion
        var blogPostDomainModel = Mapper.Map<EditViewModel, BlogPost>(blogPost);

        // Save it - Update the object in persistent store. It may throw
        // exception if something wrong while updating the object. Having
        // validated input from UI that should only happen due to server
        // error.
        _engine.SaveArticle(blogPostDomainModel);

        return RedirectToAction("List", "Article");
    }

【讨论】:

  • 所以如果我不使用自动映射器,我所做的与您发布的代码类似。我只需要创建域对象的新实例并从 ViewModel 映射其属性并最终将其传递给服务层方法。另一个问题。可以将域模型(博客帖子)引用到控制器中吗?如果我的域模型发生变化,它不会破坏控制器吗?
  • 是的,你是对的。 AutoMapper 使我们免于做这项管道工作。好吧,我认为它可以,因为我将所有域模型/实体存储在单独的程序集中。我得到了另一个程序集,其中我保留了 EF 特定的所有内容。 Web 程序集没有对 EF 的引用。我同意对模型的更改会破坏控制器。但无论如何它都会。不是吗?
猜你喜欢
  • 2012-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2020-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多