views:

28

answers:

1

I want to show a list of game reviews for editing. This is simple to do with VS' built-in scaffolding, but I'm running into issues with handling an entity's associated EntityCollections. My controller method is currently:

public ActionResult ShowReviews()
{
    var reviews = _siteDB.Games.Include("Genre").Include("Platforms").Include("Content").ToList();
    return View(reviews);
}

As you can see, Platforms is plural. Each game can be on a multitude of platforms (PS3, XBox 360, etc.).

I'd like to have the platforms' names as a CSV string in my list. I'm just not sure how to do it elegantly as I first need to drill into the platforms, extract their names, and append them to an empty string. I just feel like I'm going about this the wrong way, and the introduction of result shaping logic in my view strikes me as wrong. Here's my view as it stands now. Please let me know if there's a better way to do this.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<HandiGamer.Models.Game>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
ShowReviews
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>ShowReviews</h2>

    <table>
        <tr>
            <th></th>
            <th>
                Game
            </th>
            <th>
                Review Title
            </th>
            <th>
                Genre
            </th>
            <th>
                Platforms
            </th>
            <th>
                Score
            </th>
            <th>
                Last Modified
            </th>
        </tr>

    <% foreach (var item in Model) { %>
        <% var platformInfo = item.Platforms.ToList();
           string platformNameList = "";

           foreach (var platformName in platformInfo) {
               platformNameList += platformName.Name + " ";
           }
        %>

        <tr>
            <td>
                <%: Html.ActionLink("Edit", "Edit", new { id=item.GameID }) %> |
                <%: Html.ActionLink("Details", "Details", new { id=item.GameID })%>
            </td>
            <td>
                <%: item.GameTitle %>
            </td>
            <td>
                <%: item.Genre.Name %>
            </td>
            <td>
                <%: platformNameList %>
            </td>
            <td>
                <%: item.ReviewScore %>
            </td>
            <td>
                <%: item.Content.LastModified %>
            </td>
        </tr>

    <% } %>

    </table>

    <p>
        <%: Html.ActionLink("Create New", "CreateReview") %>
    </p>

</asp:Content>
+2  A: 

I would recommend you to use a view model and include properties needed by the view:

public class ReviewViewModel
{
    public string Title { get; set; }
    public string Genre { get; set; }
    public string Platforms { get; set; }
    ...
}

and do all the necessary projections in your controller:

public ActionResult ShowReviews()
{
    var reviews = _siteDB.Games.Include("Genre").Include("Platforms").Include("Content").ToList();
    var model = reviews.Select(r => new {
        Title = r.GameTitle,
        Genre = r.Genre.Name,
        Platforms = string.Join(" ", r.Platforms.Select(p => p.Name).ToArray());
    });
    return View(model);
}

And now your view will become much cleaner:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<HandiGamer.Models.ReviewViewModel>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
ShowReviews
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>ShowReviews</h2>

    <table>
        <thead>
            <tr>
                <th>
                    Title
                </th>
                <th>
                    Genre
                </th>
                <th>
                    Genre
                </th>
                <th>
                    Platforms
                </th>
            </tr>
        </thead>
        <tbody>
            <%: Html.DisplayForModel() %>
        </tbody>
    </table>

    <p>
        <%: Html.ActionLink("Create New", "CreateReview") %>
    </p>

</asp:Content>

and a small display template for the review (~/Views/Home/DisplayTemplate/ReviewViewModel.ascx):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<HandiGamer.Models.ReviewViewModel>" %>
<tr>
    <td>
        <%: Model.Title %>
    </td>
    <td>
        <%: Model.Genre %>
    </td>
    <td>
        <%: Model.Platforms %>
    </td>
</tr>

Finally take a look at MVCContrib Grid and you won't regret it.

Darin Dimitrov
Thanks. I wasn't sure if a View Model was the way to go or not. In seeing your code it seems like an obvious choice.
kevinmajor1