Asp.net two models in one view - c#

I'm trying to get 2 models to show in 1 view but it is not working. I have tried lots of different ideas from Google but none have worked so far.
There is no error in the Error List. But when I start it I get this error.
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Namespace.Models.Class1]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Namespace.Models.ParentClass]'.
I have a parent class containing the to child classes. If i use the #model IEnumerable<> directly at the child class it works, but not when pointed at the parent.
What am I doing wrong?
EDIT
Ok so these are my files.
Model1.cs
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
Model2.cs
public int AnotherProperty1 { get; set; }
public int AnotherProperty2 { get; set; }
ViewModel.cs
public IEnumerable<Model1> Model1 { get; set; }
public IEnumerable<Model2> Model2 { get; set; }
HomeController.cs
private ConnectContext db = new ConnectContext();
public ActionResult Index()
{
var model = from m in db.model select m;
model = db.model.OrderByDescending(m => m.ID);
return View(db.model.ToList());
}
Index.chstml
#model IEnumerable<Namespace.Models.ViewModel>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Model1.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Model2.Title)
</td>
</tr>
}
Now with the files like this my error message is
CS1061: 'System.Collections.Generic.IEnumerable<Namespace.Models.Model1>' does not contain a definition for 'Cover' and no extension method 'Cover' accepting a first argument of type 'System.Collections.Generic.IEnumerable<Namespace.Models.Model1>' could be found (are you missing a using directive or an assembly reference?)

If you have two and only two classes you want to pass in, have you considered using a tuple?
For example:
On the controller end,
var model = new Tuple<ModelType1, ModelType2>(yourModel1, yourModel2);
return View(model);
On the view end, you'll want this at the top, along with any using statements you may need:
#model Tuple<ModelType1, ModelType2>
To access each part in the view, #Model.Item1 will be your ModelType1 and #Model.Item2 will be your ModelType2.
If you wind up with more than two classes, it might be a good idea for you to make a ViewModel class with properties for the various types you want to include. (You can also cop out and add properties to the ViewBag.)

What about just making a model class with properties that makes up the two classes you need for your view?
E.g.
public class FirstModel
{
public int Id { get; set; }
public string SomeProperty { get; set; }
}
public class SecondModel
{
public int Id { get; set; }
public string SomeOtherProperty { get; set; }
}
public class ViewModel
{
public FirstModel MyFirstModel { get; set; }
public SecondModel MySecondModel { get; set; }
}
Then in your view you use a model of ViewModel.

Try this:
public class FirstModel
{
public int Id { get; set; }
public string SomeProperty { get; set; }
}
public class SecondModel
{
public int Id { get; set; }
public string SomeOtherProperty { get; set; }
}
public class ViewModel
{
public FirstModel MyFirstModel { get; set; }
public SecondModel MySecondModel { get; set; }
}
In your Controller:
private ConnectContext db = new ConnectContext();
public ActionResult Index()
{
FirstModel firstModel = //Set FirstModel Value;
SecondModel secondModel = //Set SecondModel Value;
ViewModel viewModel = new ViewModel(){
FirstModel = firstModel,
SecondModel = secondModel
}
return View(viewModel);
}

Finally I got this working. I had to change my ViewModel, Controller and View.
ViewModel.cs (From IEnumerable to List)
public List<Model1> Model1 { get; set; }
public List<Model2> Model2 { get; set; }
HomeController.cs
private ConnectContext db = new ConnectContext();
public ActionResult Index()
{
ViewModel vm = new ViewModel();
vm.Model1 = (from m in db.Model1 select m).OrderByDescending(x => x.ID).Take(3).ToList();
vm.Model2 = (from t in db.Model2 select t).OrderByDescending(x => x.ID).Take(3).ToList();
return View(vm);
}
Index.cshtml
(So here I removed the IEnumerable and then each Foreach connects to each Model)
#model Namespace.Models.ViewModel
#foreach (var item in Model.Model1) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
</tr>
}
#foreach (var item in Model.Model2) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
</tr>
}

Related

How to display value by selecting from model in the controller ASP MVC

I want to display a name in the razor page by select it from a model and pass it through Viewbag in the controller.
Controller
public IActionResult sheet()
{
var tp1 = _db.Topic.Where(t => t.sheet_id == 1).ToList();
var tp1name = tp1.Select(t => t.topic1name);
ViewBag.tp1name = tp1name;
return View();
}
Model
public class Topic
{
[Key]
public int topic_id { get; set; }
[Required]
public int sheet_id { get; set; }
[Required]
public string topic1name { get; set; }
[Required]
public string topic2name { get; set; }
}
public class Transaction
{
[Key]
public int id { get; set; }
[Required]
public int sheet_id { get; set; }
[Required]
public string topic1score { get; set; }
}
View page
#model transaction
<table class="table">
<tr>
<td rowspan="2">1</td>
<td rowspan="2">#ViewBag.tp1name</td>
<td rowspan="2">30</td>
<td>Below average</td>
<td>Average</td>
<td>Above average</td>
</tr>
It returns
System.Linq.Enumerable+SelectListIterator`2[UserLoginApp.Models.Topic,System.String] in the view page instead of topic1name
tp1 is a list of topics.
so when you do a select it creates a new Enumerable en for each item in tp1 it selects the value of topic1name.
Thus creating an Enumerable+SelectListIterator
I think you want the value of one item:
var tp1 = _db.Topic.FirstOrDefault(t => t.sheet_id == 1)
if(tp1 != null)
ViewBag.tp1name = tp1.topic1name;

ASP.NET MVC Viewmodel returns nothing

Trying to make a simple application but my view returns nothing when trying to use a viewmodel. I assume the "db.[TableName].ToList();", which works when applied on a domain model, is not enough and the selection should happen in a different way when using a viewmodel, but I have no idea how to do it. Please help. Thank you.
Town.cs
using System.Collections.Generic;
namespace City.Models
{
public class Town
{
public Town()
{
Streets = new List<Street>();
}
public int TownId { get; set; }
public string TownName { get; set; }
public virtual ICollection<Street> Streets { get; set; }
}
}
Street.cs
using System.Collections.Generic;
namespace City.Models
{
public class Street
{
public Street()
{
Houses = new List<House>();
}
public int StreetId { get; set; }
public string StreetName { get; set; }
public virtual ICollection<House> Houses { get; set; }
}
}
House.cs
namespace City.Models
{
public class House
{
public int HouseId { get; set; }
public string HoueseName { get; set; }
public int StreetId { get; set; }
public virtual Street Street { get; set; }
}
}
Floor.cs
namespace City.Models
{
public class Floor
{
public int FloorId { get; set; }
public int FloorNumber { get; set; }
public int FireExtinguisherId { get; set; }
}
}
FireExtinguisher.cs
namespace City.Models
{
public class FireExtinguisher
{
public int FireExtinguisherId { get; set; }
public string FireExtinguisherName { get; set; }
public int FloorId { get; set; }
}
}
MyViewModel.cs
namespace City.Models
{
public class MyViewModel
{
public MyViewModel()
{
Town = new Town();
Street = new Street();
House = new House();
Floor = new Floor();
FireExtinguisher = new FireExtinguisher();
}
public int MyViewModelId { get; set; }
public Town Town { get; set; }
public Street Street { get; set; }
public House House { get; set; }
public Floor Floor { get; set; }
public FireExtinguisher FireExtinguisher { get; set; }
}
}
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Town> Towns { get; set; }
public DbSet<Street> Streets { get; set; }
public DbSet<House> Houses { get; set; }
public DbSet<Floor> Floors { get; set; }
public DbSet<FireExtinguisher> FireExtinguishers { get; set; }
public DbSet<MyViewModel> MyViewModels { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
HomeController.cs (I think the problem lies here)
using System.Linq;
using System.Web.Mvc;
using City.Models;
namespace City.Controllers
{
public class HomeController : Controller
{
private ApplicationDbContext db;
public HomeController()
{
db = new ApplicationDbContext();
}
public ActionResult Index()
{
return View(db.MyViewModels.ToList());
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
Index.cshtml
#model IEnumerable<City.Models.MyViewModel>
<h2>Map information</h2>
<div class="container">
<table class="table">
<thead>
<tr>
<th>Town</th>
<th>Street</th>
<th>House</th>
<th>Floor</th>
<th>FireExtinguisher</th>
</tr>
</thead>
#foreach (var item in Model)
{
<tbody>
<tr>
<td>#(item.Town.TownName)</td>
<td>#(item.Street.StreetName)</td>
<td>#(item.House.HoueseName)</td>
<td>#(item.Floor.FloorNumber)</td>
<td>#(item.FireExtinguisher.FireExtinguisherName)</td>
</tr>
</tbody>
}
</table>
</div>
Even though I have test data in the db, this is all what I see when I run it:
Image is here
Please tell me what should I fix, how to get data retrieved. Thanks
EDIT #CrowdCoder
new picture here
I think your understanding about view model is incorrect.
View model is a class to transfer data between your view and your action method. View model is specific to the view. So if your view needs to display only 2 properties (Name and Age), your view model will have only those 2 properties. No need to bring all the properties from your entity model to the view model class.
I see that you added a new collection to the your db context,
public DbSet<MyViewModel> MyViewModels { get; set; }
This does not makes any sense. As i mentioned earlier, view models are UI concerns. It should not be in your data access code. Also do not mix the entities created by your ORM layer in your view model.
Also view models are simple POCOs. It should be lean-flat classes with properties. It is your responsibility to load the property values. You can do that in your action method or another method called from your action method.
Let's say you want to display a list of houses with it's street name, you will create a view model like this
public class HouseViewModel
{
public int HouseId { set; get;}
public string HoueseName { set;get;}
public string StreetName { set;get; }
}
Now in your view, you simply access these properties
#model IEnumerable<HouseViewModel>
<table>
#foreach(var item in Model)
{
<tr>
<td>#item.HouseId </td>
<td>#item.HoueseName </td>
<td>#item.StreetName </td>
</tr>
}
</table>
Ofcourse, for this code to work, you need to make sure you will be creating a list of HouseViewModel and send it to the view from your action method.
public ActionResult Index()
{
var list= new List<HouseViewModel>{
new HouseViewModel { HouseId =1,HoueseName ="Abc", StreetName ="Detroit"},
new HouseViewModel { HouseId =2,HoueseName ="Xyz", StreetName ="Novi"}
};
return View(list);
}
Now you can see that how we are using view model to transfer data from the action method to the view. Here we just hard coded the property values for the items in the list we are sending. We can update that to read from your EF db context as needed.
Let's read all the Houses, use LINQ projection to create a HouseViewModel object for each item in that collection and assign the property values.
public ActionResult Index()
{
var houses = db.Houses
.Select(a=>new HouseViewModel
{ HouseId =a.HouseId,
HoueseName =a.HouseName,
StreetName = a.Street.StreetName
})
.ToList();
return View(houses);
}

2 models in a single view with if condition

I have problem with how to and where to add if condition in view which has two models in a single view.
This is view
#foreach (service_provider SP in ViewBag.service_provider) {
<tr>
<td>#SP.Sp_email</td>
<td>#SP.Sp_name</td>
<td>#SP.city.Cityname</td>
</tr>}#foreach (picture img in ViewBag.pictures){
<tr>
<td><img src="data:image/png;base64,#Convert.ToBase64String(img.pic,0,img.pic.Length)" width="100" />
</td>
</tr>
}
This is picture model
public int PIC_ID { get; set; }
public string pic_name { get; set; }
public Nullable<int> belong_id { get; set; }
public byte[] pic { get; set; }
This is service_provider model
public int SPID { get; set; }
public string Sp_email { get; set; }
public string Sp_password { get; set; }
public string Sp_name { get; set; }
This is my controller
public ActionResult Index(){
ViewBag.service_provider = dc.service_provider;
ViewBag.pictures = dc.pictures;
return View();
}
The purpose of the above view is to display details of the service_provider with it's picture by where belong_id in picture equals SPID in service_provider. but I couldn't understand where do I add if condition.I'm using Database first approach
It would be much easier if you used a strongly typed model instead of the ViewBag. You can then add logic to your model or your controller. I personally it easier read and test. It also prevents syntax errors.
Models.cs
public class Picture {
public int PIC_ID { get; set; }
public string pic_name { get; set; }
public Nullable<int> belong_id { get; set; }
public byte[] pic { get; set; }
// added - your FK pointing back to the corresponding service_provider
public int SPID { get; set; }
// added - the corresponding service_provider
public service_provider ServiceProvider { get; set; }
}
public class service_provider {
public int SPID { get; set; }
public string Sp_email { get; set; }
public string Sp_password { get; set; }
public string Sp_name { get; set; }
// added
public ICollection<Picture> Pictures {get;set;}
}
Controller.cs
public ActionResult Index(){
var service_provider = dc.service_provider.Inculde(x => x.Pictures);
// var pictures = dc.pictures;
// code to add the pictures to the correct service_provider instance
// ideally this should already be reflected in your model.
// If you are using EF you should be modeling this using proper data relations
// that would allow you to execute an .Include statement on the retrieval
// and pass the model directly into the View without the need for an additional call to pictures as the service_provider would then already contain the relations
return View(service_provider);
}
View.cshtml
#model IEnumerable<service_provider>
#*This following line indicates that it is a list of service_provider hense the IEnumerable*#
#foreach (service_provider SP in Model){
<tr>
<td>#SP.Sp_email</td>
<td>#SP.Sp_name</td>
<td>#SP.city.Cityname</td>
</tr>
#*Iterate over the pictures on each model.
You can still split this if you want all service providers and then all pictures.*#
#foreach (picture img in SP.Pictures){ #*changed to still look at the model*#
<tr><td><img src="data:image/png;base64,#Convert.ToBase64String(img.pic,0,img.pic.Length)" width="100" /></td></tr>
}
}
Try:
#foreach (picture img in ViewBag.pictures)
{
if(img.PIC_ID == ViewBag.service_provider.SPID)
{
<tr><td><img src="data:image/png;base64,#Convert.ToBase64String(img.pic,0,img.pic.Length)" width="100" /></td></tr>
}
}
BTW - Maybe better way - is check in server side and return ONLY pics that you need??

How to show other model column in Razor view?

Currently i'm able to show all column in razer view from userRole model.
Just curious if i like to show the SiteName column in UserRole Razor view, instead of showing SiteID, is it possible? I know it can be done via custom view model, but is it a must? Please correct me if i'm wrong!
UserRole Model:
public int UserID { get; set; }
public string UserName { get; set; }
public int SiteID { get; set; }
*No SiteName column here....so i only can show SiteID in razor..
Site Model :
public int SiteID { get; set; }
public string SiteName { get; set; } <<-- the column i want..
Controller:
public ActionResult Index()
{
//need join table here perhaps?
return View(db.User_Role.ToList());
}
Razor View:
#model IEnumerable<FAB_Portal_POC.Models.UserRole>
<table class="table table-striped table-hover">
<tr>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.Role)
</th>
<th>
#Html.DisplayNameFor(model => model.SiteID)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Role)
</td>
<td>
#Html.DisplayFor(modelItem => item.SiteID) <<-- i want site name.
</td>
</tr>
}
</table>
The first problem is that your UserRole entity model doesn't seem to have a navigation property.
You don't need to, but then querying becomes awkward:
var rolesWithSite = from r in db.User_Role
join s in db.Sites on s.ID equals r.Site_ID
select new
{
Role = r,
Site = s
}
.ToList();
While when you add a navigation property (perhaps even instead of the foreign key property):
public class User_Role
{
public int UserID { get; set; }
public string Role { get; set; }
public string UserName { get; set; }
public virtual Site Site { get; set; }
}
Querying will become a whole lot easier:
var rolesWithSite = db.User_Role.Include(r => r.Site).ToList();
You can then introduce a viewmodel:
public class UserRoleViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string Role { get; set; }
public string Site { get; set; }
}
And map the query results to that:
var viewModels = rolesWithSite.Select(r => new UserRoleViewModel
{
UserID = r.UserID,
UserName = r.UserName,
Role = r.Role,
Site = r.Site.SiteName,
}).ToList();
return View(viewModels);
Entities and Models are very different.
You have to create a specific model for your page.
something like this :
PageModel
public string UserName { get; set; }
public string SiteName { get; set; }
In your controller your have to prepare all the data you want to send to the page. It s here that you will "associate" your entities to your pagemodel
public ActionResult Index()
{
var roles = db.User_Role.ToList();
var sites = db.Sites.ToList(); // i don t know how you get this information in your exemple.
//define here every property of the PageModel you want to use there
var model = new PageModel();
model.UserName = roles.UserName;
model.SiteName = sites.Select(...).SiteName;
return View(model);
}
then in razor view
#model IEnumerable<FAB_Portal_POC.Models.PageModel>
Hope it will help
You have multiple ways to do that the simple one is you have to create new class name UserRoleViewData and mention these fields and pass the class object to view. just like that.
New Class UserRoleViewData Look like this
public int UserID { get; set; }
public string UserName { get; set; }
public int SiteID { get; set; }
public string SiteName { get; set; }
and your Controller
public ActionResult Index()
{
List<className> dto = (from a in _db.User_Role
join b in db.Site on a.SiteID equals b.SiteID
select (new classname { UserID =a.UserID , UserName =a.UserName , SiteID =a.SiteID , SiteName =b.SiteName })).ToList();
return View(dto);
}
In View You have to change #model IEnumerable<FAB_Portal_POC.folderName.ClassName>
and map other property on view like others.I hope u'll understand and this will help you.
You can store the sites list in the ViewBag and show the site name by extracting it from the ViewBag:
public ActionResult Index()
{
ViewBag.Sites = db.Sites.ToList();
return View(db.User_Role.ToList());
}
And the View:
<td>
#Html.DisplayFor(modelItem => ViewBag.Sites.Single(s => s.SiteID == item.SiteID).SiteName)
</td>
Another, and to me a better approach, would be to create a ViewModel for your view which contains all of the data that you need. And return that view model to your view.
public class UserSiteViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string Role { get; set; }
public int SiteID { get; set; }
public string SiteName { get; set; }
}
No need to use ViewBag , join between 2 tables and store result in another viewModel it's vary bad idea. Please check out reference link, It will really help you to do Code in Very good direction. Click Here

How to display create form in an existing MVC 4 view and C#

I have view to display Restaurant. In this view at the bottom of the page, I want to display Comments form to add comments about that Restaurant.
Can someone please help me to do this using MVC 4 & C#.
My Models has the followign two tables:
/Classifieds TABLE
public class Classifieds
{
[Key]
public string C_Unique_Id { get; set; }
public string AdType { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
//ClassifiedsComments TABLE
public class ClassifiedsComments
{
[Key]
public string CCommentsUniqueId { get; set; }
public string CommentAuthor { get; set; }
public string Comment { get; set; }
[ForeignKey("Classifieds")]
public string C_Unique_Id { get; set; } //this is the foreign key of Classified record
public virtual Classifieds Classifieds { get; set; }
}
Classifieds Details view:
#model SomeIndianShit.Models.Classifieds
#{
ViewBag.Title = "Details";
}
<table class="recordDetailsDisplayTableStype">
<tr>
<td colspan="2" align="left">
#Html.DisplayFor(model => model.Description)
<br /><br />
</td>
</tr>
<tr>
<td>
Ad Type
</td>
<td align="left"> :
#Html.DisplayFor(model => model.AdType)
</td>
</tr>
SOME OTHER FIELDS DISPLAY HERE
</table>
//Here I want to display "ClassifiedsComments" form to add comments to above Classified.
//HOW can I display the ClassifiedsComments create.cshtml code here??
Try this:
With View:
#using (Html.BeginForm("Classifieds", "ClassifiedsDetails", FormMethod.Post))
and
#using (Html.BeginForm("ClassifiedsComments", "ClassifiedsDetails", FormMethod.Post))
And use 1 Model for this:
public class ClassifiedsDetails
{
public Classifieds Model1{ get; set; }
public ClassifiedsComments Model2{ get; set; }
}
Update:
public class ClassifiedsDetails
{
public ClassifiedsDetails()
{
Model1 = new Classifieds();
Model2 = new ClassifiedsComments();
}
public Classifieds Model1{ get; set; }
public ClassifiedsComments Model2{ get; set; }
}
public class Classifieds
{
public Classifieds()
{
C_Unique_Id = String.Emty;
AdType = String.Emty;
//---- Add default setting here------
}
[Key]
public string C_Unique_Id { get; set; }
public string AdType { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
Or display list of comments in View with Model:
public class ClassifiedsDetails
{
public ClassifiedsDetails()
{
Model1 = new Classifieds();
Model2 = new List<ClassifiedsComments>();
}
public Classifieds Model1{ get; set; }
public List<ClassifiedsComments> Model2{ get; set; }
}
View:
#model ClassifiedsDetails
#Html.LabelFor(model => model.Model1.Title)
#foreach (var items in Model.Model2)
{
#item. //fields
}
To display data in view create view model, but to post comment, dont use model:
public class ClassifiedsViewModel
{
public ClassifiedsViewModel()
{
Comments = new List<ClassifiedsComments>();
}
public Classifieds Classifieds { get; set; }
public List<ClassifiedsComments> Comments { get; set; }
}
Fill this view model, use in view to display details and comments like above you write.
if(Model != null && Model.Classifieds != null)
{
<table> ...display details.. </table>
}
if(Model != null && Model.Comments != null)
{
<table> ...display comments with foreach loop.. </table>
}
And at bottom, create comment post form
#using (Html.BeginForm("SaveComment", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
//set to which Classifieds will this comment posted
#Html.Hidden("C_Unique_Id", Model.Classifieds.C_Unique_Id)
<fieldset>
#Html.TextBox("Comment")
//other fields ....
<input type="submit" value="Save" />
</fieldset>
}
Edit2: View model creating:
public ActionResult Details(int id)
{
//add breakpoint here and follow any step.
ClassifiedsViewModel viewModel = new ClassifiedsViewModel();
viewModel.Classifieds = db.Classifieds.Find(id);
viewModel.Comments = db.LoadCommentsByClassifiedsId(id); //create db method
// or instead of this line use:
viewModel.Comments = db.ClassifiedsComments.Where(e => e.C_Unique_Id == id).ToList();
return(viewModel);
}
You can then add a ViewModel like this
public class ClassifiedsDetailViewModel
{
public ClassifiedsDetailViewModel()
{
ClassifiedsComments = new ClassifiedsComments();
}
public Classifieds Classifieds { get; set; }
public ClassifiedsComments ClassifiedsComments { get; set; }
}
Pass this view model to your view and then add #Html.Partial("_CreateClassifiedsCommentsFormPartial", Model.ClassifiedComments) below the table. And in your partial view, you can use Html.BeginForm or the Ajax.BeginForm.
See sample here

Categories

Resources