Fat Controllers: How Can I Make It Thin?

I am developing a blog engine using EF 6 and MVC 5.

I chose not to use the repository pattern or UoW ​​as it is already implemented in EF 6 at the infrastructure level.

The solution contains the following layers.

DataModels Level: It has simple POCOs that are auto-generated and 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; }
    }

      

Service level:

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

... Some methods have been omitted for brevity.

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());
        }


    }

      

User interface level

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

    }

      

Problem Consider a scenario where a user has finished editing their old blog post / article and clicked the submit button to update the blog post / article.

Is my HTTP POST Change Action Too Fat? I feel like the controller is doing something here too.

  • Get an existing article from the database

  • Update it with ViewModel values

  • Call the SaveArticle

    method from the service layer.

How can I put this controller on a diet?

Shouldn't the Service Layer method SaveArticle

perform the task of fetching the article from the Db and updating it with new values ​​and calling the SaveChanges method?

If the above statement is true, how do you pass the ViewModel method to the ServiceLayer method? Isn't it a bad solution to allow passing ViewModels into the service layer?

How can I handle this? I am confused and need some help.

+3


source to share


1 answer


To be honest, this also confuses me, and sometimes what and how the controller should do to submit the request.

In most of my implementations, I do the following:

  • Accept input object viewModel in Post method (input values ​​are validated on client side).
  • Check ModelState.
  • Convert the viewmodel object to a domain model object. I am using AutoMapper .
  • Give it to the service method. He does what needs to be done for the operation.
  • Returns the appropriate result based on the operation.


I was you, I would write:

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

      

+2


source







All Articles