胖控制器:我如何让它变得苗条?

时间:2014-12-10 10:11:47

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

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

我决定不使用Repository模式或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. 从DB

  2. 获取现有文章
  3. 使用ViewModel值

  4. 更新它
  5. 从服务层调用SaveArticle方法。

  6. 我怎样才能把这个控制器放在饮食上?

    服务层方法SaveArticle是否应该从Db检索文章并使用新值更新它并调用SaveChanges方法?

    如果以上陈述为真,我如何将ViewModel传递给ServiceLayer方法?允许ViewModels泄漏到服务层是不是一个错误的决定?

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

1 个答案:

答案 0 :(得分:2)

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

在我的大多数实现中,我都遵循:

  1. 接受Post方法中的viewModel输入对象(输入值在客户端验证)。
  2. 检查ModelState。
  3. 将viewmodel对象转换为域模型对象。我使用AutoMapper
  4. 将其提供给服务方法。它确实需要为操作做些什么。
  5. 根据操作返回适当的结果。
  6. 我是你,我会写:

        [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");
        }