Missing namespace or assembly in Razor syntax when using ViewBag, but fine with Model
I'm working on an ASP.NET MVC 3 application that uses the Entity Framework and has a Class Library project with methods that return ObjectSet<T>
instances.
I've got a Controller that has code like this:
public ActionResult Index()
{
var students = context.GetStudents();
return View(students);
}
Here, GetStudents returns a type of System.Data.Objects.ObjectSet<Student>
.
In my view I have the code @Model.Count()
, which correctly displays the number of items in the model. This works wonderfully.
What's odd is that if I change the action so that it puts students
in ViewBag
then I start getting errors. Specifically, if I do:
public ActionResult Index()
{
var students = context.GetStudents();
ViewBag.Students = students;
return View(students);
}
And then add @ViewBag.Students.Count()
in my view I get a Yellow Screen of Death with the following message: "'System.Data.Objects.ObjectSet' does not contain a definition for 'Count'"
Why does it work (as expecte开发者_Python百科d) with the model but not with ViewBag
?
ViewBag uses new .NET 4.0 dynamic feature. Your code doesn't work for the exact same reason for which the following code doesn't work:
IEnumerable<int> array = new int[] { 1, 2, 3 };
dynamic foo = array;
Console.WriteLine(foo.Count());
Extension methods cannot be resolved on dynamic objects. You can make it work by the following monstrosity:
@(((System.Data.Objects.ObjectSet<Student>)Model).Count())
But please promise me you will never do anything like this.
The fact that your code doesn't work is actually a good thing. Because IMHO what you are trying to do is wrong in many aspects:
- You are passing domain models to your views instead of using view models.
- You are passing data access specific objects to your view such as
ObjectSet<T>
instead of using plain CLR objects. - You are using a weakly typed ViewBag => you are loosing Intellisense, you will need to perform some casts in your views in order to invoke certain overloads of some helper methods (which would lead into spaghetti code), you get some cryptic errors because of the dynamic nature of ViewBag, you name it, ...
So the correct way to do this would be to define a view model which will contain only the properties that your view cares about. For example:
public class StudentViewModel
{
public string Name { get; set; }
}
and your controller action would query the repository and fetch a model which will be mapped to a view model passed to the corresponding strongly typed view:
public ActionResult Index()
{
var model = context.GetStudents();
var viewModel = model.Select(x => new StudentViewModel
{
Name = string.Format("{0} {1}", x.FirstName, x.LastName)
}).ToArray();
return View(viewModel);
}
and finally your strongly typed view would only depend on the specific view model (a view shouldn't care about data access specific stuff, it should only be passed whatever it needs to show):
@model StudentViewModel[]
<div>We have @Model.Length students</div>
精彩评论