Add a new view, Details.cshtml
, within the Views\Player
subfolder:
@model RetrogamesWeb.Data.Entities.Player @{ ViewBag.Title = "Player's details"; } <h2>Player's details</h2> <fieldset> <legend>Player</legend> <div class="display-label"> @Html.DisplayNameFor(model => model.Name) </div> <div class="display-field"> @Html.DisplayFor(model => model.Name) </div> <div class="display-label"> @Html.DisplayNameFor(model => model.Gender) </div> <div class="display-field"> @Html.DisplayFor(model => model.Gender) </div> </fieldset> <table> <tr> <th> Game </th> <th> Date & Time </th> <th> Score </th> </tr> @foreach (var score in Model.Scores) { <tr> <td> @Html.DisplayFor(modelItem => score.GameName) </td> <td> @Html.DisplayFor(modelItem => score.ScoreDateTime) </td> <td> @Html.DisplayFor(modelItem => score.ScoreValue) </td> </tr> } </table> <p> @Html.ActionLink("Play Games!", "PlayGames", new { id = Model.Id }) </p> <p> @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) @Html.ActionLink("Back to List", "Index") </p>
Add a new view, Edit.cshtml
, within the Views\Player
subfolder:
@using RetrogamesWeb.Data.Entities @model RetrogamesWeb.Data.Entities.Player @{ ViewBag.Title = "Edit player"; } <h2>Edit player</h2> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Player</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.Gender) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.Gender, new SelectList(Enum.GetValues(typeof(Gender)))) @Html.ValidationMessageFor(model => model.Gender) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Add a new view, Index.cshtml
, within the Views\Player
subfolder:
@model IEnumerable<RetrogamesWeb.Data.Entities.Player> @{ ViewBag.Title = "Players"; } <h2>Players</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> @Html.DisplayNameFor(model => model.Name) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.Id.ToString() }) | @Html.ActionLink("Details", "Details", new { id = item.Id.ToString() }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id.ToString() }) </td> </tr> } </table>
Add a new view, PlayGames.cshtml
, within the Views\Player
subfolder:
@model RetrogamesWeb.Models.PlayerGames @{ ViewBag.Title = "Play games"; } <h2>Play games</h2> <fieldset> <legend>Player</legend> <div class="display-label"> @Html.DisplayNameFor(model => model.Player.Name) </div> <div class="display-field"> @Html.DisplayFor(model => model.Player.Name) </div> </fieldset> <h3>Click on the desired game and register your score</h3> @foreach (var game in Model.AvailableGames) { <p> @Html.ActionLink(game.Name, "AddScore", new { playerId = Model.Player.Id, gameId = game.Id, gameName = game.Name }) </p> } <p> @Html.ActionLink("Back to List", "Index") </p>
Working with the ASP.NET MVC Web Application
If you start your website and go to /Game/Create (http://localhost:16820/Game/Create, in my case), you can enter a name, date, select one or more categories, click Create
(see Figure 6), and MongoDB will perform the following actions:
- Create the new
retrogamesweb
database (the previous database created wasretrogames
). - Create the
games
collection in the new database. - Insert the new game document in the
games
collection.
As explained in the previous article, you will definitely want to create indexes based on the most frequent queries. Indexes are as important in MongoDB, as they are in any other database.
Figure 6: Creating a new game.
After you insert many game documents, you will see the list sorted by release date in descending order in /Game (see Figure 7). The time shouldn't appear in both the /Game/Create and /Game/Index views, but I didn't want to add annotations in the code for either UI hints or formatting. The data layer is the most important in this case.
Figure 7: Listing a maximum of 100 games.
Similarly, go to /Player/Create, enter a name, select a gender from the dropdown list, click Create
(see Figure 8) and MongoDB will perform the following actions:
- Create the
players
collection in theretrogamesweb
database. - Insert the new player document in the
players
collection.
Figure 8: Creating a new player.
You will see the new player in the list. Click the Details
link on the right-hand side of the player name and the /Player/Details view will display an empty scores list. Click the Play Games!
link, and you will go to the Player/PlayGames view (see Figure 9); then click on the desired game to generate a random score.
Figure 9: The Player/PlayGames view.
Click on the link for any game and the application will push a new random score to the player document. The browser will display the Player/Details view with the scores registered so far (see Figure 10). You can use MongoVUE or the MongoDB shell to see the changes in the database collections (see Figure 11).
Figure 10: The Player/Details view with many scores listed.
Figure 11: The player document with five score documents in the Scores array.
You can use AJAX to retrieve and display the available games and the scores for a player. You can also add paging and search capabilities learning just a few additional things about MongoDB queries. Note that the embedded scores can become a big problem for this application, and it might be a better idea to create a specific collection for the scores to have more flexibility to query scores. However, the idea here is to provide an example with embedded documents, so that you know specific operations to work with them and their most common problems. For example, the generic GetById
method has a problem with embedded documents because you retrieve all of them each time you call this method. Thus, you must be careful when deciding what to save in each document.
In this example, the score was important and the write operations required the acknowledged
option. However, when you want the best possible performance for applications that require logging huge amounts of data and you can lose some data because it doesn't add too much value, you should work with the unacknowledged
option.
For example, if you want to do sampling, you can define a capped array for the scores to make sure the number of saved scores won't be excessive, and then discard scores when you reach the limit.
I've provided many examples of MongoDB working with the C# driver in a console application and in an ASP.NET MVC Web application. However, MongoDB has an important number of additional features and advantages that I haven't covered in this series. I also haven't covered database-administration related topics, such as the different scalability options. But with the foothold I've provided, you can dive deeper on more advanced features, such as the use of MapReduce and more complex queries. You won't regret investing time in learning MongoDB features.
Gaston Hillar is an expert in Windows-based programming who writes frequently for Dr. Dobb's.
Related Articles