How to use multiple models in one view for ASP.NET MVC?
I've read similar questions on SO but can't seem to figure out this issue, which is specific to DBContext objects (I think). Here's some dummy code to illustrate.
I have the following code in my Index() action:
public ActionResult Index()
{
AnimalDBContext db = new AnimalDBContext();
return View(db.Dogs);
}
I have the following code for my models:
public class Dog
{
public int ID { get; set; }
public string name { get; set; }
public string breed { get; set; }
}
public class AnimalDBContext : DbContext
{
public DbSet<Dog> Dogs { get; set; }
}
In my view I have the following:
@model IEnumerable<AnimalProject.Models.Dog>
@foreach (var d in Model)
{
<h3>@d.name</h3>
<h2>@d.breed</h2>
}
Everything works great, the view will loop through every dog in my database. However, I have anot开发者_运维问答her set of DBContext data from another table that I want in the same view. I want to be able to enumerate each item in the database for that table as well.
This is what I want, if you catch my drift:
@model IEnumerable<AnimalProject.Models.Dog>
@model IEnumerable<AnimalProject.Models.Cat>
@foreach (var d in Dog)
{
<h3>@d.name</h3>
<h2>@d.breed</h2>
}
@foreach (var c in Cat)
{
<h3>@c.name</h3>
<h2>@c.breed</h2>
}
I have tried grouping the classes together and using a partial view, but apparently you can't have a different model in a partial view, because I always get the error message:
"The model item passed into the dictionary is of type 'System.Data.Entity.DbSet
1[AnimalProject.Models.Dog]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable
1[AnimalProject.Models.Cat]'."
So, how can I use multiple models in my view that both get the data I want from separate tables in the database?
What about creating custom view model class:
public AnimalModel
{
public IEnumerable<Dog> Dogs { get; set; }
public IEnumerable<Cat> Cats { get; set; }
}
Fill this model in Index and pass it to the view which will expect AnimalModel
instead of enumerables.
Edit:
Filling the model:
public ActionResult Index()
{
using (var db = new AnimalDBContext())
{
var model = new AnimalModel
{
Dogs = db.Dogs.ToList(),
Cats = db.Cats.ToList()
};
return View(model);
}
}
View (I have never used Razor so I hope this is correct):
@model AnimalProject.Models.AnimalModel
@foreach (var d in Model.Dogs)
{
<h3>@d.name</h3>
<h2>@d.breed</h2>
}
@foreach (var c in Model.Cats)
{
<h3>@c.name</h3>
<h2>@c.breed</h2>
}
A model is just an object like any other object in C# (or VB)
So you can pass any object you want to the view, even complex ones. If you need to present a lot of things, then create a view model (a model just for the view's needs) which contains all the things you want to present and pass this one to the view. Then, from within the view, you access the model's individual properties on the different parts of the view, to present them. An example of this approach is the Animal Model Ladislav Mrnka talked about. You put in there the dogs and cats enumerables and then pass it to the view. You tell the view that your model now is this AnimalModel type etc etc...
So, since a model is just an object, and if you are lazy enough to not want to create another custom view model, then you can pass your whole dbcontext as a model to the view like this.
Controller
public ActionResult Index()
{
return View(new AnimalDBContext()); // We pass a new instance of the dbcontext to the view
}
View
@model AnimalProject.AnimalDBContext // we declare that our model is of type AnimalDBContext
@foreach (var d in Model.Dogs) // We reference the dbcontext's sets directly
{
<h3>@d.name</h3>
<h2>@d.breed</h2>
}
@foreach (var c in Model.Cats) // We reference the dbcontext's sets directly
{
<h3>@c.name</h3>
<h2>@c.breed</h2>
}
Now you have declared to your view that your "Model" object will be an AnimalDBContext So you can access all the AnimalDBContext's properties, sets etc directly from within your view. It may not be the best abstraction for the data access, but look how simple your controller has become! :) You just throw the whole world into the view and let it decide which parts to pick and present... Of course this is for simple scenarios. If you wanted more complex stuff you would have to fall back to custom view models eventually.
The only solution to this problem I've found so far is to chuck both models in the ViewBag
, and then render partial views for parts that use only one model. Of course, it only works if the partial views are separable - as is in your case. This allows strongly typed partial views compared to accessing the contents of the ViewBag
directly.
The solution is similar to Ufuk's answer here. First put data in ViewBag
from controller:
ViewBag.Dogs = db.Dogs;
ViewBag.Cats = db.Cats;
Then render the partial View from the view:
@Html.Partial("_ListDogs", ViewBag.Dogs)
@Html.Partial("_ListCats", ViewBag.Cats)
And each partial view would be along the lines of the following (example is _ListDogs.cshtml
):
@model IEnumerable<AnimalProject.Models.Dog>
@foreach (var d in Model)
{
<h3>@d.name</h3>
<h2>@d.breed</h2>
}
精彩评论