ASP.NET MVC – Handling Blog Tags in MVC

asp.net-mvc5c

I'm writing my own blog engine as a learning exercise. The blog is fairly functional right now but I'm trying to add a 'tag' feature to it and I'm confused as to what the best way to handle saving a list of tags back to my controller.

My Question:

Should I be passing my list of comma separated tags back to the controller as a single string and converting it into a IList<Tag> there? Or should I be handling this out in the view and then pass an IList<Tag> to the controller?

A sudo-version of my Post.cs model:

public class Post : IEntity
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public IList<Tag> Tags { get; set; }
}

and my Tag.cs model:

public class Tag : IEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<Post> Posts { get; set; }
}

update: Sharing some gist files.

PostController.cs

Create.cshtml

Best Answer

When tags are submitted by the author of a blog, they are under a form of a simple string (with either comma-separated values or, like on Stack Exchange, simple spaces). This is the only moment in the workflow when all tags are appearing as a single string.

The controller then splits those tags and trims them. At this stage, those tags become a Collection<string> (or Collection<Tag> if there is specific data or business logic associated with a tag.) Depending on your specific needs, you may need to use other generic collection classes, such as a HashSet<T> if you need to quickly search through the sequence of tags.

The controller may then persist those tags together with the article, the title, the author info, etc. Depending on the persistence layer (i.e. the type of the database) you use, the tags may take different forms, but usually they will still be considered as some sort of array and stored one by one, not as a single value.

Finally, when it comes to displaying the tags, the controller loads them from the persistence layer (the tags are then appearing in a form of a Collection<string>/Collection<Tag>), and stores the collection (eventually as IEnumerable<string>) in the model, passing the model down to the view.

The view enumerates those tags and displays the corresponding HTML.


Looking at your code, I'm not sure what are you trying to do. I guess that Tags is a sequence in your model, but you're displaying only the first element in your view (line 54 and 55). Why?

This is also why I asked in my comments how are tags displayed in the form. Here's one way to bind an array to a sequence of check boxes; I'm not sure this is what you want or need. Probably what you actually need is something similar to Stack Exchange tagging: a single text field where a user can enter one or more words (or comma-separated values in your case). For instance:

data-center, rack, power-management, wiring

will be entered in a single text box and result in four tags: , , and .

This leads to the difference between:

  • The view which shows a form to an author, letting him modifying the article and its tags, and:

  • The view which shows the article to the end users.

The first view can deal with all tags represented as a simple string. It already does when it comes to submitting a form: the value of a text box—a simple text, is translated to a string which can then be parsed like you did in your code on line 79 with a Split(). Although disturbing,¹ you can also use a string to populate the form when the author wants to modify an article (this makes it possible to use the same model in both cases.) While the model will contain a string containing comma-separated tags, try to use Collection<Tag> as much as possible within the controller itself. The only locations where you should see a string are the boundaries: when the controller receives the input and when it creates a model.

On the other hand, the view which shows the article—and the tags—to the end user should rely completely on IEnumerable<Tag> representation which should be pushed by the controller through the model down to the view. It's the view which will use the sequence to enumerate through the values with a foreach, generating the corresponding HTML.

A final note: when you'll get your code finished, compiling and working, make sure you post a few parts to CodeReview. There are a lot of things to tell about the code in its current state.


¹ The approach is disturbing because a concatenated string is a wrong data structure for tags. The right one remains a sequence (such as Collection<T>). The problem, in your case, is that you're relying on the same model both for generating the form and getting the submitted values. Since you're forced to use a string in the second case, using a sequence in the first one will require to have two different models, which will make your code more complex than it should be.