In this recipe, we will take a look at how to iterate over a collection of data using for
and foreach
. In this recipe, we will use NBuilder to create a list of Product
classes for us to work with. Then we will build three views that will iterate over the collection in different ways. We will show how to iterate over a collection of products pulled from ViewData
using foreach
. Then we will see how we can iterate over the Model
property of a strongly typed view. And finally, we will create another strongly typed view and use a for
statement to iterate over the collection.
Models
folder of our application called Product
. This class will be used for creating a collection of data for our iteration examples.Models/Product.cs:
public class Product { public int ProductId { get; set; } public string Name { get; set; } public double Price { get; set; } public string Description { get; set;} public DateTime CreateDate { get; set; } }
ProductRepository
class in the Models
folder. This class will use NBuilder to do the generation work for us.Models/ProductRepository.cs:
public class ProductRepository { public List<Product> GetProducts() { List<Product> result = Builder<Product> .CreateListOfSize(30) .WhereAll() .Have(x => x.Description = @"...long lorem ipsum string...") .Build() .ToList(); return result; } }
foreach
loop. Start by opening the home controller. In the existing Index view, we will add a line of code passing a collection of Products
into the ViewData
dictionary.Controllers/HomeController.cs:
ViewData["Products"] = new ProductRepository().GetProducts();
Index
action we just added the code to. Then remove all of the fluff that the template adds for us. Now we will add a line of code to grab the list of products out of the ViewData
collection.Views/Home/Index.aspx:
<% List<Product> products = ViewData["Products"] as List<Product>; %>
Views/Home/Index.aspx:
<% foreach (Product p in products) { %> ... <% } %>
There is a new shorthand syntax in MVC 2 that allows us to perform an Html.Encode
. There is the previous syntax <%=
that allows us to write data as if we had used Response.Write
. Then there is the new <%:
syntax. This <%:
variant encodes the data prior to spitting it out, allowing us to remove all the <% Html.Encode
statements that we would have to use. But as Visual Studio's code generator still uses <%= Html.Encode
, we will leave it as it is.
Views/Home/Index.aspx:
<div class="display-label">ProductId</div> <div class="display-field"><%= Html.Encode(p.ProductId) %></div> <div class="display-label">Name</div> <div class="display-field"><%= Html.Encode(p.Name) %></div> <div class="display-label">Price</div> <div class="display-field"> <%= Html.Encode(String.Format("{0:c}", p.Price)) %></div> <div class="display-label">Description</div> <div class="display-field"><%= Html.Encode(p.Description) %></div> <div class="display-label">CreateDate</div> <div class="display-field"> <%= Html.Encode(String.Format("{0:g}", p.CreateDate)) %></div> <hr />
ViewData
completed, we can now take a look at how things differ when using a strongly typed view instead. To start this portion of the recipe, open the home controller. Then we will add a new ActionResult
called ForEach
. In this method, we will again get a list of products from our repository and return that collection to our view. This time though, we will not use the ViewData
dictionary to pass out the data. Instead, we will pass it directly to the view.Controllers/HomeController.cs:
public ActionResult ForEach() { List<Product> products = new ProductRepository().GetProducts(); return View(products); }
MvcApplication1.Models.Product
in this case). In the View content: drop-down list, there are all sorts of good options...we will choose to generate a Details view. Then you can choose to use a master page for your view. Click on Add.Inherits="System.Web.Mvc.ViewPage <MvcApplication1.Models.Product>"
to
Inherits="System.Web.Mvc.ViewPage<List <MvcApplication1.Models.Product>>".
foreach
statement wrapped around our generated details view.<% foreach (Product p in Model) { %> ... <% } %>
p
reference in place of the Model
object. Change:<%= Html.Encode(Model.ProductId) %>
to
<%= Html.Encode(p.ProductId) %>
(The same for all other Model
references.)
ViewData
and strongly typed view Model
iterations using the foreach
statement, we can now look at a strongly typed view Model
example using the for
statement. To start this example, we need to create another new action called For
. This method will also return a list of products from our ProductRepository
.Controllers/HomeController.cs:
public ActionResult For() { List<Product> products = new ProductRepository().GetProducts(); return View(products); }
Inherits="System.Web.Mvc.ViewPage <MvcApplication1.Models.Product>"
to
Inherits="System.Web.Mvc.ViewPage<List <MvcApplication1.Models.Product>>".
for
statements.Views/Home/For.aspx:
<% for (int i = 0; i < Model.Count; i++) { %> ... <% } %>
for
declaration in place, we then need to update the generated details view to reference an index in the collection of products, rather than referencing properties of a single product model. We will have to change all references ofto
<%= Html.Encode(Model[i].ProductId) %>
The iteration over a collection is pretty standard to all languages. The syntax of each iteration style was the focus of this recipe. More importantly, how to reference the collection is what varies the most. For a quick recap:
ViewData
dictionary and cast it out as the type we expect.List<Product> products = ViewData["Products"] as List<Product>; foreach (Product p in products) p.ProductId
Model
rather than in the ViewData
.foreach (Product p in Model) p.ProductId
for
syntax is also different.for (int i = 0; i < Model.Count; i++) Model[i].ProductId
Now there is the question of which method of iteration you use, for
, or foreach?
This is not the place to address that. Understand that the for
syntax is the most efficient, but the foreach
syntax is considerably more readable! Take a look at Jon Skeet's post on the performance implications between the two (http://msmvps.com/blogs/jon_skeet/archive/2009/01/29/for-vs-foreach-on-arrays-and-lists.aspx).