This question and community wiki answer has been added to assist in closing out numerous unanswered questions as discussed in this meta post.
I have some code and when it executes, it throws an exception saying:
The model item passed into the dictionary is of type Bar but this dictionary requires a model item of type Foo
What does this mean, and how do I fix it?
The error means that you're navigating to a view whose model is declared as typeof Foo (by using #model Foo), but you actually passed it a model which is typeof Bar (note the term dictionary is used because a model is passed to the view via a ViewDataDictionary).
The error can be caused by
Passing the wrong model from a controller method to a view (or partial view)
Common examples include using a query that creates an anonymous object (or collection of anonymous objects) and passing it to the view
var model = db.Foos.Select(x => new
{
ID = x.ID,
Name = x.Name
};
return View(model); // passes an anonymous object to a view declared with #model Foo
or passing a collection of objects to a view that expect a single object
var model = db.Foos.Where(x => x.ID == id);
return View(model); // passes IEnumerable<Foo> to a view declared with #model Foo
The error can be easily identified at compile time by explicitly declaring the model type in the controller to match the model in the view rather than using var.
Passing the wrong model from a view to a partial view
Given the following model
public class Foo
{
public Bar MyBar { get; set; }
}
and a main view declared with #model Foo and a partial view declared with #model Bar, then
Foo model = db.Foos.Where(x => x.ID == id).Include(x => x.Bar).FirstOrDefault();
return View(model);
will return the correct model to the main view. However the exception will be thrown if the view includes
#Html.Partial("_Bar") // or #{ Html.RenderPartial("_Bar"); }
By default, the model passed to the partial view is the model declared in the main view and you need to use
#Html.Partial("_Bar", Model.MyBar) // or #{ Html.RenderPartial("_Bar", Model.MyBar); }
to pass the instance of Bar to the partial view. Note also that if the value of MyBar is null (has not been initialized), then by default Foo will be passed to the partial, in which case, it needs to be
#Html.Partial("_Bar", new Bar())
Declaring a model in a layout
If a layout file includes a model declaration, then all views that use that layout must declare the same model, or a model that derives from that model.
If you want to include the html for a separate model in a Layout, then in the Layout, use #Html.Action(...) to call a [ChildActionOnly] method initializes that model and returns a partial view for it.
This question already has a great answer, but I ran into the same error, in a different scenario: displaying a List in an EditorTemplate.
I have a model like this:
public class Foo
{
public string FooName { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string BarName { get; set; }
}
And this is my main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.EditorFor(m => m.Bars)
And this is my Bar EditorTemplate (Bar.cshtml)
#model List<Bar>
<div class="some-style">
#foreach (var item in Model)
{
<label>#item.BarName</label>
}
</div>
And I got this error:
The model item passed into the dictionary is of type 'Bar', but this
dictionary requires a model item of type
'System.Collections.Generic.List`1[Bar]
The reason for this error is that EditorFor already iterates the List for you, so if you pass a collection to it, it would display the editor template once for each item in the collection.
This is how I fixed this problem:
Brought the styles outside of the editor template, and into the main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
<div class="some-style">
#Html.EditorFor(m => m.Bars)
</div>
And changed the EditorTemplate (Bar.cshtml) to this:
#model Bar
<label>#Model.BarName</label>
Observe if the view has the model required:
View
#model IEnumerable<WFAccess.Models.ViewModels.SiteViewModel>
<div class="row">
<table class="table table-striped table-hover table-width-custom">
<thead>
<tr>
....
Controller
[HttpGet]
public ActionResult ListItems()
{
SiteStore site = new SiteStore();
site.GetSites();
IEnumerable<SiteViewModel> sites =
site.SitesList.Select(s => new SiteViewModel
{
Id = s.Id,
Type = s.Type
});
return PartialView("_ListItems", sites);
}
In my case I Use a partial view but runs in normal views
Consider the partial map.cshtml at Partials/Map.cshtml. This can be called from the Page where the partial is to be rendered, simply by using the <partial> tag:
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
This is one of the easiest methods I encountered (although I am using razor pages, I am sure same is for MVC too)
First you need to return an IEnumerable version of your model to the list view.
#model IEnumerable<IdentityManager.Models.MerchantDetail>
Second, you need to return a list from the database. I am doing it via SQL Server, so this is code I got working.
public IActionResult Merchant_Boarding_List()
List<MerchantDetail> merchList = new List<MerchantDetail>();
var model = new MerchantDetail();
try
{
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("select * from MerchantDetail md where md.UserGUID = '" + UserGUID + "'", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
model.biz_dbaBusinessName = reader["biz_dbaBusinessName"].ToString();
merchList.Add(model);
}
}
}
}
}
catch (Exception ex)
{
}
return View(merchList);
Passing the model value that is populated from a controller method to a view
public async Task<IActionResult> Index()
{
//Getting Data from Database
var model= await _context.GetData();
//Selecting Populated Data from the Model and passing to view
return View(model.Value);
}
one more thing.
if your view is a partial/sub page and the model for that partial view is null for some reason (e.g no data) you will get this error. Just need to handle the null partial view model
Related
I am having a problem passing to the View a Model that contains the dropdown data and the model.
With this code my page loads, but the dropdownlist contains "System.Web.MVC.SelectList" when selected.
Here's my controller code.
public ActionResult Index(string productNameFilter, string productCategoryFilter, String productTypeFilter )
{
var ddl = new Items();
ddl.CategoryddList = itemsRepository.GetItemDdl("Item Categories").Select(c => new SelectListItem
{
Value = c.DropdownID.ToString(),
Text = c.DropdownText
});
ViewBag.CategoryDD = new SelectList(ddl.CategoryddList, "Value", "Text");
var model = itemsRepository.GetItemByName(productNameFilter);
return View(model);
}
Here's my view
#model Ienumerable<Models.items.items>
#Html.DropDownList("productCategoryFilter",
new SelectList(ViewBag.CategoryDD),
"---Select Category---")
Side note - if you use a ViewModel between the View and the Model instead of binding directly to the model, you can put your SelectList on the ViewModel and use #Html.DropdownFor() instead of #Html.Dropdown(). The ViewBag should really be used sparingly.
However back to your original question:
What is "Items()"? in your line
var ddl = new Items();
I'm not sure what good reason you would have NOT to make it enumerable.
I suspect it is not working because you are making a selectlist from a select list twice --
in your code behind you are defining ViewBag.CategoryDD as a SelectList(), and then in your Razor code you are creating a new SelectList() from the existing selectlist. You shouldn't have to do this.
The way I would do this is create a ProductViewModel class that contains your product category list AND your list of products (your current model), and a property for the selected filter.
public class ProductViewModel
{
public IEnumerable<Model.items.items> ProductList {get;set;}
public IEnumerable<SelectListItem> ProductCategoryList {get;set;} //SelectList is an IEnumerable<SelectListItem>
public string SelectedCategory {get;set;}
}
Then on your view the model would be
#model ProductViewModel
#Html.DisplayFor(model => model.SelectedCategory, "---Select Category---")
#Html.DropdownListFor(model => model.SelectedCategory, Model.ProductCatgoryList)
I have a situation where I am wanting an 'Add' View to accept an int from the controller but return a different type to the HttpPost controller method. Confusing, I know. The 'Add' View is used to create an object typed as a Widget, but I need to pass in the ID of the WidgetCategory. So in my WidgetController, I would have a method something like:
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
return View(id);
}
However, in the view, since in it's intended to return a Widget to be added would start like this:
#using MyProject.Models
#model Widget
#{
ViewBag.Title = "My Title";
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
#using (Html.BeginForm())
{
// Insert markup here ...
}
My question is, how do I pass the WidgetCategoryID into the controller if it's typed to return a Widget? I was hoping to add it as a hidden field inside the form like so:
#Html.Hidden(id)
Any ideas would be greatly appreciated.
Thanks!
Assuming your ViewModel Widget has a WidgetCategoryId property
public class Widget
{
public int ID { set;get;}
public int WidgetCategoryId { set;get;}
//Other properties
}
Send that to the Add View (HttpGet)
public ActionResult Add(int id)
{
Widget objModel=new Widget{ WidgetCategoryId =id} ;
return View(objModel);
}
Now in your Add View, Keep that in a hiddden variable using the HiddenFor HTML helper method.
#using MyProject.Models
#model Widget
#{
ViewBag.Title = "My Title";
}
#using (Html.BeginForm())
{
#Html.HiddenFor(m=>m.WidgetCategoryId);
<input type="submit" value="Save" />
}
Now you will have it in your HTTPPost action method when you submit.
[HttpPost]
public ActionResult Add(Widget model)
{
// check model.WidgetCategoryId and Have fun with it
}
default model binder checks for request parameters by name and attempts to set properties on model according. If you need something more evolute, you can take a look to custom model binding. Btw, you can change your action to:
public ActionResult Add(Widget widget) // Widget class has one property named Id with public set
{
return View(widget);
}
or
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
Widget widget = new Widget();
widget.Id = id;
return View(widget);
}
I slightly prefer the second form for creations, but I guess it's a matter of tastes
Btw, your view's form shall have inputs for each "important" property of the Widget object. (Hidden or text) via:
#Html.HiddenFor (m => m.Id);
Within your view code, you're not only specifying that the view will return a Widget type, but specifying that the entire model for that view is a Widget type. Basically, that data passed both into and out of the View via its #model is of type Widget.
What you can do here is to retain the strong-typing of the View to a Widget, but where you need to pass in simply an ID value (a simple int), you can use either the ViewData or the ViewBag
For example, in the controller:
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
// All properties of a ViewBag are completely dynamic!
ViewBag.WidgetID = id;
// You're still returning a View strongly-typed to a Widget, but not
// actually supplying a Widget instance.
return View();
}
And in the View:
#using MyProject.Models
#model Widget
#{
ViewBag.Title = "My Title";
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
#using (Html.BeginForm())
{
// Retrieve the WidgetID from the ViewBag
var WidgetID = ViewBag.WidgetID;
// Do something with the WidgetID, for example:
#Html.Hidden(WidgetID)
}
Note that ViewData and ViewBag are very similar mechanisms by which "non-model" data can be passed into a view. ViewBag is newer (MVC 3) and is based upon the dynamic features of C#4.0, whereas ViewData is the older method based upon a collection of key/value pairs.
In the Add Action in the Controller you can set a ViewBag property to the id and them in the View do html.Hidden() using the ViewBag property. I.e.,
public ActionResult Add(int id) // 'id' is the WidgetCategoryID
{
Widget widget = new Widget();
widget.Id = id;
ViewBag.categoryId = id;
return View(widget);
}
In the View
#Html.Hidden(#:ViewBag.categoryId)
Can I return List to my View from my Controller, or do I need to wrap my List in another class, just for the sake of returning only a class?
If this is possible, how do I iterate over the list? I'm trying this (Razor), but Visual Studio isn't happy about it:
#model List<QueueItem>
.
.
.
#foreach (item in #Model)
You can do it that way, but I never return my domain objects directly to a view. Normally I have a view model for a view containing only the fields that I need on that view.
Given your scenario I would have this list in a view model:
public class MyViewModel
{
public IEnumerable<QueueItem> QueueItems { get; set; }
}
In your action method you will populate these items from a service/repository call and then this view model would be returned to the view:
public ActionResult List()
{
MyViewModel viewModel = new MyViewModel
{
QueueItems = queueItemRepository.GetAllQueueItems()
};
return View(viewModel);
}
In your view you would loop through the items and do what needs to be done:
#foreach (var item in Model.QueueItems)
{
}
I hope this helps.
I have 2 models:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
public class Order
{
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
I want edit objects of BOTH classes in SINGLE view, so I need something like:
#model _try2models.Models.Person
#model _try2models.Models.Order
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x=>x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
This, of course, don't work: Only one 'model' statement is allowed in a .cshtml file. May be there is some workaround?
Create a parent view model that contains both models.
public class MainPageModel{
public Model1 Model1{get; set;}
public Model2 Model2{get; set;}
}
This way you can add additional models at a later date with very minimum effort.
To use the tuple you need to do the following, in the view change the model to:
#model Tuple<Person,Order>
to use #html methods you need to do the following i.e:
#Html.DisplayNameFor(tuple => tuple.Item1.PersonId)
or
#Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |
Item1 indicates the first parameter passed to the Tuple method and you can use Item2 to access the second model and so on.
in your controller you need to create a variable of type Tuple and then pass it to the view:
public ActionResult Details(int id = 0)
{
Person person = db.Persons.Find(id);
if (person == null)
{
return HttpNotFound();
}
var tuple = new Tuple<Person, Order>(person,new Order());
return View(tuple);
}
Another example : Multiple models in a view
Another option which doesn't have the need to create a custom Model is to use a Tuple<>.
#model Tuple<Person,Order>
It's not as clean as creating a new class which contains both, as per Andi's answer, but it is viable.
If you are a fan of having very flat models, just to support the view, you should create a model specific to this particular view...
public class EditViewModel
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
Many people use AutoMapper to map from their domain objects to their flat views.
The idea of the view model is that it just supports the view - nothing else. You have one per view to ensure that it only contains what is required for that view - not loads of properties that you want for other views.
ok, everyone is making sense and I took all the pieces and put them here to help newbies like myself that need beginning to end explanation.
You make your big class that holds 2 classes, as per #Andrew's answer.
public class teamBoards{
public Boards Boards{get; set;}
public Team Team{get; set;}
}
Then in your controller you fill the 2 models. Sometimes you only need to fill one. Then in the return, you reference the big model and it will take the 2 inside with it to the View.
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
return View(teamBoards);
At the top of the View
#model yourNamespace.Models.teamBoards
Then load your inputs or displays referencing the big Models contents:
#Html.EditorFor(m => Model.Board.yourField)
#Html.ValidationMessageFor(m => Model.Board.yourField, "", new { #class = "text-danger-yellow" })
#Html.EditorFor(m => Model.Team.yourField)
#Html.ValidationMessageFor(m => Model.Team.yourField, "", new { #class = "text-danger-yellow" })
And. . . .back at the ranch, when the Post comes in, reference the Big Class:
public ActionResult ContactNewspaper(teamBoards teamboards)
and make use of what the model(s) returned:
string yourVariable = teamboards.Team.yourField;
Probably have some DataAnnotation Validation stuff in the class and probably put if(ModelState.IsValid) at the top of the save/edit block. . .
In fact there is a way to use two or more models on one view without wrapping them in a class that contains both.
Using Employee as an example model:
#model Employee
Is actually treated like.
#{ var Model = ViewBag.model as Employee; }
So the View(employee) method is setting your model to the ViewBag and then the ViewEngine is casting it.
This means that,
ViewBag.departments = GetListOfDepartments();
return View(employee);
Can be used like,
#model Employee
#{
var DepartmentModel = ViewBag.departments as List<Department>;
}
Essentially, you can use whatever is in your ViewBag as a "Model" because that's how it works anyway. I'm not saying that this is architecturally ideal, but it is possible.
Just create a single view Model with all the needed information in it, normaly what I do is create a model for every view so I can be specific on every view, either that or make a parent model and inherit it. OR make a model which includes both the views.
Personally I would just add them into one model but thats the way I do it:
public class xViewModel
{
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
#model project.Models.Home.xViewModel
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x => x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
You can use the presentation pattern http://martinfowler.com/eaaDev/PresentationModel.html
This presentation "View" model can contain both Person and Order, this new
class can be the model your view references.
Another way that is never talked about is
Create a view in MSSQL with all the data you want to present. Then use LINQ to SQL or whatever to map it. In your controller return it to the view. Done.
you can't declare two model on one view, try to use Html.Action("Person", "[YourController]") & Html.Action("Order", "[YourController]").
Good luck.
Beside of one view model in asp.net you can also make multiple partial views and assign different model view to every view, for example:
#{
Layout = null;
}
#model Person;
<input type="text" asp-for="PersonID" />
<input type="text" asp-for="PersonName" />
then another partial view Model for order model
#{
Layout = null;
}
#model Order;
<input type="text" asp-for="OrderID" />
<input type="text" asp-for="TotalSum" />
then in your main view load both partial view by
<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
I hope you find it helpfull !!
i use ViewBag For Project and Model for task so in this way i am using two model in single view and in controller i defined viewbag's value or data
List<tblproject> Plist = new List<tblproject>();
Plist = ps.getmanagerproject(c, id);
ViewBag.projectList = Plist.Select(x => new SelectListItem
{
Value = x.ProjectId.ToString(),
Text = x.Title
});
and in view tbltask and projectlist are my two diff models
#{
IEnumerable<SelectListItem> plist = ViewBag.projectList;
}
#model List
I wanna pass to my "View" if there's any unreaded interaction within a call.
It's like an e-mail, If you have an unread email within a group of emails, it will be bold displayed. I'm doing something like that.
I have a Repository, so I'm doing this...
foreach(item in callRepo.All())
{
if (item.Interactions.Count(x=>x.Unread==true)>0)
{
}
}
return View(callRepo.All());
A call has many Interactions, and I wanna pass to my View if there is any unread Interaction.
This is how it's working on my View now:
var CSSclass="Readed"
foreach(item in callRepo.All())
{
if (item.Interactions.Count(x=>x.Unread==true)>0){CSSclass="Unreaded"}
<tr class=#CSSclass>}
I do not want to treat this business on my View.
Is there a way to do that on my controller?
.
Is my question clear? Sorry about the bad english.Tks
How many time are you going to call this repository method (I've counted 3 so far)?
I would recommend you to use a view model:
public class MyViewModel
{
public bool IsRead { get; set; }
public IEnumerable<Interaction> Interactions { get; set; }
}
then in your controller:
public ActionResult Index()
{
var items = callRepo.All();
var model = items.Select(item => new MyViewModel
{
Interactions = item.Interactions,
IsRead = item.Interactions.Any(x => x.Unread == true)
});
return View(model);
}
and your view:
#model IEnumerable<MyViewModel>
<table>
#Html.DisplayForModel()
</table>
and in the corresponding display template (~/Views/Shared/DisplayTemplates/MyViewModel.cshtml):
#model MyViewModel
#{
var css = model.IsRead ? "read" : "unread";
}
<tr class="#css">
...
</tr>
A further improvement of this code would be to replace the IEnumerable<Interaction> property in the view model by a specific IEnumerable<InteractionViewModel> and use AutoMapper to externalize the mapping between the model and the view model.