views:

147

answers:

2

I'm working through the ASP.NET MVC article at http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx. (Note: Some of the constructs in this post were deprecated in MVC 1 RTM, so I've changed the code accordingly. Perhaps that's my problem.)

In my LINQ to SQL .dbml (in MyDB.designer.cs) the Category class (compiler generated) is defined as follows:

namespace MVC_Basics_1.Models
{
    ...
    [Table(Name="dbo.Categories")]
    public partial class Category : INotifyPropertyChanging, INotifyPropertyChanged
    {
        ...
    }

In my controller class I define a Categories action method as follows:

public ActionResult Categories()  // /Products/Categories/ maps here
{
    List<Category> categories = northwind.GetCategories();
    return View("Categories", categories);
}

I then create a Categories.aspx which is strongly typed as "MVC_Basics_1.Models.Category" which places the

<%@ ... Inherits="System.Web.Mvc.ViewPage<MVC_Basics_1.Models.Category>" %>

line at the top of the file.

Finally, in the .aspx I have:

<% foreach (var category in ViewData) { %>
    <li>
        <%= Html.ActionLink(category.CategoryName, new { action="List", category=category.CategoryName }) %>
    </li>
<% } %>

Two questions:

First, when I browse to /Products/Categories/ I get the error:

Compiler Error Message: CS1061: 'System.Collections.Generic.KeyValuePair' does not contain a definition for 'CategoryName' and no extension method 'CategoryName' accepting a first argument of type 'System.Collections.Generic.KeyValuePair' could be found (are you missing a using directive or an assembly reference?)

and the category view data object isn't recognizing any of the properties of the Category class.

What am I missing?

Second, the controller's Category() action method is returning a list of categories (typed as "List categories") as the viewdata for the view, but the view's ActionLink helper method references a "category.CategoryName". This doesn't make sense to me since I'm passing in an instance of categories (not Category) as the type. Since the controller's action method is returning a List doesn't this suggest the view needs to be typed as Categories?

UPDATE:

I realized a flaw in my question and in my approach to this. I was more focused on the error and comparing my results to the article I read than on my actual goal - kind of like not seeing the forest through the trees. When I thought about what I was actually trying to accomplish - passing a model data to the view - I stopped thing about the syntax error and started thinking about (a) the particular object I wanted to create in the controller and (b) which piece(s) of that data I wanted to pass to the view. Once I saw the forest through the trees, the answer became obvious to me. @Ufuk and @ryk helped me realize this.

+3  A: 

You should strongly type the View to the List, not the model only.

<%@ ... Inherits="System.Web.Mvc.ViewPage<IEnumerable<MVC_Basics_1.Models.Category>>" %>

This should solve both of your problems I guess. First you couldn't browse it with a foreach becase your model was not IEnumerable. Second List<Category> instances are lists, you have to refer to a member if you want to use Category class' properties.

Update

There is a problem in your View too. You are sending the list in Model, not in ViewData.Change your View like this:

<% foreach (var category in Model) { %>
    <li>
        <%= Html.ActionLink(category.CategoryName, new { action="List", category=category.CategoryName }) %>
    </li>
<% } %>
Ufuk Hacıoğulları
@Ufuk - I am getting the exact same error. Curious, when I create the view and select to strongly type it, I select "MVC_Basics_1.Models.Category". Why isn't the listbox giving me the option to pick the List rather than the category?
Howiecamp
It's odd - I'm entering the code as-is from the article, with the exception of changes like RenderView(...) being deprecated in favor of return View(...). Now that I've specified the <IEnumerable<MVC_Basics_1.Models.Category>> as you suggested, what else do I need to change?
Howiecamp
I realised another error in View. Please see the update. For the other question: I don't know for sure. Intellisense offer classes in Models normally, but you have to specify the place if it's nested.
Ufuk Hacıoğulları
@Ufuk - I realized a flaw in my question. I'm updating it, please check it out. Thanks.
Howiecamp
A: 
<%@ ... Inherits="System.Web.Mvc.ViewPage<MVC_Basics_1.Models.Category>" %>

needs to be

<%@ ... Inherits="System.Web.Mvc.ViewPage<List<MVC_Basics_1.Models.Category>>" %>

You need to be using 'model' in place of ViewData.

<% foreach (var category in model) { %>
    <li>
        <%= Html.ActionLink(category.CategoryName, "List", new {category= category.CategoryName }) %>
    </li>
<% } %>

ViewData is a hash table, using Key-Value pairs, so iterating through ViewData will give you each object in the hash table, not your Model that you're declaring and passing to the View.

Benjamin Anderson
@Benjamin: An interface such as IEnumerable is a better choice.
Esteban Araya
Howiecamp
@Howiecamp: I've corrected the ActionLink parameters. It should work now
Benjamin Anderson
@Benjamin - I'd love to accept both your answers but apparently not possible. I'm going to accept the other answer because I think both are equally good but he answered first. I needed some criteria!
Howiecamp