Simple MVC Comment moderation

asp.net-mvc, c#, entity-framework, optimization

Solution

The model seems to hold off so far, so I'll answer the question this way:

Is there any holes in this implementation, such as can a knowing user override the moderated value in the postback?

Yes. I don't since any FORM code based on what you sent, but I'll assume you're creating a Comment via post value and directly saving to the database. This can be bad. You would be better getting only the values that you need from the user and fill the rest in the controller:

public ActionResult AddComment(Comment comment, int id)
{
    if (ModelState.IsValid)
    {
        // NEW
        comment.Moderated = false;

        comment.PostId = id;
        db.Comments.Add(comment);
        db.SaveChanges();
        return RedirectToAction("Details", "Blog", new { id = id });
    }
    return RedirectToAction("Details", "Blog", new { id = id });
}

Is there a simpler, or perhaps more elegant solution to follow? Again I don't want to use anything pre-built. The point of this project is to learn stuff.

As said earlier, this looks good so far. However, for reusability and testing purpose I would probably get the database operation in a different class, such as a Service or a Repository.

So, the code would look like this:

public ActionResult AddComment(Comment comment, int id)
{
    if (ModelState.IsValid)
    {
        CommentService.Save(comment);
        return RedirectToAction("Details", "Blog", new { id = id });
    }
    return RedirectToAction("Details", "Blog", new { id = id });
}

This ain't a big change, but it's huge in the sens of it's going to get you more flexibility later if you want to reuse this code.

As for your PostCommentViewModel class, I woundn't do ANY operation in ViewModels, especialy not in the constructor. The way you should use ViewModels would be to bind data onto it instead of having the ViewModel doing the job. You could be getting the data from anyway, ViewModels represent only a structure of what need to be displayed. So, get the code out of there, and put it in a service (i.e: CommentService).

Problem

I have a simple (and possibly crude) way of moderating comments on a blog I am building. This is a learning/fun project, so I am rolling everything I can on my own to get more familiar with some different technologies. I am wondering if there are any holes in my logic, or perhaps a better implementation for what I am doing. I am going to allow anonymous comments on the site, but I want to moderate them for anything i find inappropriate. Here is how I have done it: My Model is using EF Code first approach: ``` public class Comment { public int Id { get; set; } public bool Moderated { get; set; } public string DisplayName { get; set; } public string Email { get; set; } public DateTime DateCreated { get; set; } public string Content { get; set; } public int PostId { get; set; } public Post Post { get; set; } } ``` Standard stuff here. Then I created a ViewModel to display the details on a blog post and all the comments associated with it on a page like so: ``` public class PostCommentViewModel { public Post Post { get; set; } public List<Comment> Comment { get; set; } public PostCommentViewModel(int postId) { var db = new BlogContext(); Post = db.Posts.First(x => x.Id == postId); var query = from x in db.Comments where x.PostId == postId && x.Moderated == true select x; Comment = query.ToList(); } } ``` For the comments this just grabs the ones that are related to the PostId and that are Moderated (i.e. I have been able to review them) For the View that is display this I just using a base scaffolding template: ``` public ActionResult Details(int id = 0) { var viewModel = new PostCommentViewModel(id); return View(viewModel); } ``` The cshtml: ``` @model CodeFirstBlog.ViewModels.PostCommentViewModel <fieldset> <legend>PostCommentViewModel</legend> @Html.DisplayFor(x => x.Post.Title) <br /> @Html.DisplayFor(x => x.Post.Content) <br /> @Html.DisplayFor(x => x.Post.CreatedDate) <hr /> @foreach(var comment in Model.Comment) { @Html.DisplayFor(x => comment.Content) <br /> @Html.DisplayFor(x => comment.DateCreated) <br /> @Html.DisplayFor(x => comment.DisplayName) <br /> @Html.DisplayFor(x => comment.Email) <br /> <hr /> } </fieldset> @Html.ActionLink("Add Comment", "AddComment", new { id = Model.Post.Id} ) ``` Here is the AddComment in the Controller ``` public ActionResult AddComment(int id = 0) { return View(); } [HttpPost] public ActionResult AddComment(Comment comment, int id) { if (ModelState.IsValid) { comment.PostId = id; db.Comments.Add(comment); db.SaveChanges(); return RedirectToAction("Details", "Blog", new { id = id }); } return RedirectToAction("Details", "Blog", new { id = id }); } ``` So when I comment is added, Moderated is defaulting to false so the comment will not show up right away. Now if the admin logs in he can go to the ViewModeration view which just returns a list of all comments awaiting approval: ``` public ActionResult ViewModeration() { var comments = from x in db.Comments where x.Moderated == false select x; return View(comments); } ``` If he click the approve button it executes this in the controller: ``` public ActionResult ApproveComment(int id) { Comment c = (from x in db.Comments where x.Id == id select x).First(); c.Moderated = true; db.SaveChanges(); return RedirectToAction("ViewModeration"); } ``` What I really want to know is this: - Are there any holes in this implementation, such as can a knowing user override the moderated value in the postback? - Is there a simpler, or perhaps more elegant solution to follow? Again I don't want to use anything pre-built. The point of this project is to learn stuff.

Original source