mirhagk
mirhagk

Reputation: 1285

How can I get ViewBag value in ASP.NET MVC ?

I'm using ASP.NET MVC Razor view engine, and I'm trying to use Viewbag. The following code will not work for me. In the controller I have

ViewBag.courses = new List<dynamic>();
ViewBag.courses.Add(new { Name = "Math" });

and then in the view I have

@foreach(dynamic course in ViewBag.courses)
{
    <li>@course.Name</li>
}

But it gives me an error saying the object course does not have a definition for Name. The debugger gives me the value, and shows everything correctly. Is there any way to get this to work? (I have a workaround already, I just would rather use this).

Thanks in advance.

Upvotes: 3

Views: 12110

Answers (2)

Brad Christie
Brad Christie

Reputation: 101604

Personally, I'd solve it using the following:

public class Course
{
    public String Name { get; set; }
}

public class CoursesViewModel
{
    private IList<Course> courses;

    public IList<Course> Courses{
      get { return this.courses ?? (this.courses = new List<Course>()); }
      set { this.courses = value; }
    }
}

Controller:

public ActionResult Index()
{
  CoursesViewModel model = new CoursesViewModel();
  model.Courses.Add(new Course { Name = "Math" });

  return View(model: model);
}

And your view:

@model CoursesViewModel
@* ... *@
<ul>
@foreach (Course course in Model.Courses)
{
  <li>@course.Name</li>
}
</ul>
@* ... *@

Use models, that's what the whole point of MVC is. It makes life so much easier, not to mention the attributes you can apply to the model to further customize how it's displayed (templating, etc.). [Ab]Using dynamic the way you are is just going to lend your site to future problems, not to mentioned a bunch of cat-and-mouse going back and forth ("what did I name that property?", "what value type was 'foo' again?", ...), ignoring if there is anyone else that needs to work on the code you're generating.

Upvotes: 7

Michael Buen
Michael Buen

Reputation: 39393

Brad Christie's ViewModel approach is more sound, compiler can catch errors in your View earlier, instead of when it is more expensive to fix errors, i.e. during runtime; whereas if you used dynamic, you are basically on your own. And doing dynamic on anonymous type won't work either, having said that I once tried persisting anonymous types to dynamic, it's not an ugly hack; but I still want my Views errors be caught earlier instead, can be done via ViewModel.

If you still want to use dynamic, you just need a good serializer to persist anonymous values from Controller to View. You'll do this on your Controller:

 return View((object)r.JsSerialize());

And on your View:

 @{ dynamic r = ((string)Model).JsDeserialize(); }


 @foreach (var item in r) {
 <tr>
  <td>
   @item.Person.Lastname
  </td>
  <td>
   @item.Person.Firstname
  </td>
  <td>
   @item.Person.FavoriteNumber
  </td>
  <td>
   <input type="checkbox" disabled="disabled" @(item.IsQualified ? "checked" : "") />
  </td>
 </tr>
}

In what I tried, Json Serializer is a good candidate for serialization needs, it can mimic the whole object graph

The Json Serializer component I used: http://www.drowningintechnicaldebt.com/ShawnWeisfeld/archive/2010/08/22/using-c-4.0-and-dynamic-to-parse-json.aspx

Upvotes: 2

Related Questions