views:

793

answers:

5

Into some view data i have put the result of an anonymous type:

            var projectData = from p in db.Projects
                          orderby p.title
                          select new
                          {
                              Title = p.title,
                              DevURL = p.devURL ?? "N/A",
                              QAURL = p.qaURL ?? "N/A",
                              LiveURL = p.liveURL ?? "N/A",
                              Users = p.GetUsers().MakeUserList()
                          };

        ViewData["ProjectSummary"] = projectData;

How do I iterate through this view data in the MVC view on the front end to say, make a table of results?

A: 

The problem is not that it's an anonymous type. The problem is that it's merely a lazy-evaluated IQueryable<> object, and it hasn't been evaluated yet.

I can't answer for ASP.NET MVC, but in the very similar Monorail, you would have to convert it from an iterator into an actual collection:

        var projectData = (from p in db.Projects
                      orderby p.title
                      select new
                      {
                          Title = p.title,
                          DevURL = p.devURL ?? "N/A",
                          QAURL = p.qaURL ?? "N/A",
                          LiveURL = p.liveURL ?? "N/A",
                          Users = p.GetUsers().MakeUserList()
                      }).ToList();
James Curran
A: 

I have tried that, it doesn't work. Also I have definitely sent IEnunerables as ViewData before and it has handled it fine.

my problem is i have no idea what even the syntax would be...

<%foreach(what_the_frick_is_the_type t in ViewData["ProjectSummary"])%>
qui
There is a way to cast anonymous types using another anonymous type (ie. you declare a dummy anonymous object with the same structure and cast), but I don't know how to extend that to an IEnumerable. It should be possible though (I don't know enough about generics/anonymous types).
eyston
+2  A: 

In your case it would be much simpler to create a model to hold your data rather than using an anonymous type.

The issue you're having is that your anonymous type is cast to an object when its stored within the ViewData. On the UI side, when you get that object out, the only way to access its properties is to use reflection. You do NOT want to do this in your UI. It will be highly ugly. Instead, just add the following class to your Models:

public class Project{

public string Title {get;set;}
public string DevUrl {get;set;}
public string QAUrl {get;set;}
public string LiveUrl {get;set;}
public IEnumerable<User> Users {get;set;}

public static IEnumerable<Project> RetrieveAllProjects()
{
  return from p in db.Projects
           orderby p.title
           select new Project
             {
                Title = p.title,
                DevURL = p.devURL ?? "N/A",
                QAURL = p.qaURL ?? "N/A",
                LiveURL = p.liveURL ?? "N/A",
                Users = p.GetUsers().MakeUserList()
             };
}

In your controller do this:

public ActionResult Index()
{
  return View("Index", Project.RetrieveAllProjects());
}

and in your view's codebehind, strongly type it thusly:

//snip
public partial class Index : ViewPage<IEnumerable<Project>>
{
//snip

You might think its a bit wasteful to have all these models laying around, but its much easier to understand, and makes your UI code much slimmer, if you use your models wisely.

Also, a model is a great place (and, in fact, should be where you do it) to place the logic for loading your data and constructing the models themselves. Think ActiveRecord. And, while you're coding all this, realize that projects like SubSonic create your models for you without any muss or fuss.

Will
Thanks for your reply Will. I can see your point, I was kind of hoping I would be able to use anon types in my front end code somehow, but I guess not without it being too clean. I detest making display classes :)
qui
Your display classes should encapsulate the loading logic for those classes. You shouldn't be loading data in your controllers; that's not in their job description. So you don't have to consider them as trash...
Will
A: 

Not tried this with an anonymous type but this is how i do it by passing a List<T> object to ViewData

<% foreach (Project p in (IEnumerable<Project>)ViewData["ProjectSummary"]) { %>
     <%= Html.Encode(p.Title) %>
<% } %>

Hope this is what your looking for.

Mark

A: 

I side with Mark and Will. Using actual model classes to encapsulate the model data and business logic makes the code more legible and easier to maintain for the very minor cost of creating the class (a necessity anyways with a domain driven design).

charliedigital