I have a BookingView class in Model:
public class BookingView
{
[Required]
[Display(Name = "Attraction")]
public int Attraction { get; set; }
[Required]
[Display(Name = "Date")]
public string Date { get; set; }
[Required]
[Display(Name = "Username")]
public string Username { get; set; }
}
Table against this model in DB is Tickets.
I need to write a function in another class in model named BookingManager to get all Ticket Records.
public IEnumerable<BookingView> GetAllBookings()
{
var a = from o in dre.Tickets select o;
return a.ToList();
}
I want to display these records in view named ViewAllBookings:
#model IEnumerable<VirtualTickets.Models.ViewModel.BookingView>
#{
ViewBag.Title = "ViewAllBookings";
}
<h2>ViewAllBookings</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Attraction
</th>
<th>
Date
</th>
<th>
Username
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Attraction)
</td>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
The function gives complie time error in function GetAllBookings on return statement. If I change return type to Ticket then I get runtime error as expected because in view ViewAllBookings it is expecting IEnumerable List of records having type BookingView.
Please provide a solution to this situation. I am really confused how to deal with this.
Thanks
It looks like your setup requires converting the entity class Ticket into the view model BookingView. I guess you need something like this:
return a.AsEnumerable().Select(ticket => new BookingView
{
Attraction = ticket.SomeProperty,
// etc
})
.ToList();
You have to write method that maps Ticket to BookingView and use it like this:
public IEnumerable<BookingView> GetAllBookings()
{
var a = from o in dre.Tickets select o;
return a.AsEnumerable().Select(Map).ToList();
}
private BookingView Map(Ticket ticket)
{
var bookingView = new BookingView();
//mapping code goes here
return bookingView;
}
I am not sure if your Tickets table actually has the same columns as the BookingView class in your code, but if theere's a mapping between the table and your class, your solution would be:
var a = from o in dre.Tickets
select new BookingView { Attraction= o.Attraction,
Date=o.Date,
Username = o.UserName } ;
return a.ToList();
Yes it is obvious that you are getting this error because you are returning a Ticket object and your view needs a list of BookingView objects. The 2 don't match, i.e. they are not the same.
Your BookingManager should return your tickets to the controller. Here is an alternative way of how I would have done it.
You could change your view model to look like this:
public class BookingViewModel
{
public List<Ticket> Tickets { get; set; }
}
Your BookingManager can contain the following method to return your tickets:
public class BookingManager : IBookingManager
{
// References to your database context
public IEnumerable<Ticket> GetAllTickets()
{
return databaseContext.Tickets;
}
}
In your booking controller you can inject an instance of IBookingManager with a dependency injection framework like AutoFac:
public class BookingController : Controller
{
private readonly IBookingManager bookingManager;
public BookingController(IBookingManager bookingManager)
{
this.bookingManager = bookingManager;
}
public ActionResult Index()
{
BookingViewModel viewModel = new BookingViewModel
{
Tickets = bookingManager.GetAllTickets().ToList()
};
return View(viewModel);
}
}
And then on your view:
#model YourProject.ViewModels.Tickets.BookingViewModel
#foreach (var ticket in Model.Tickets)
{
// Display ticket details here
}
I hope this helps.
Related
Im experiencing an error on trying to fetch data from a db using a list scaffolding. The error and the code is as follow.
My Model:
namespace ShimbaSchool.Models
{
[Table("tblStaff")]
public class Staff
{
[Key]
public int StaffId { get; set; }
[Required]
[DisplayName("Upload Image")]
public string ImagePath { get; set; }
[Required,MinLength(2),DisplayName("Staff Name")]
public string StaffName { get; set; }
[Required,MaxLength(250)]
[DisplayName("Teacher's Subject")]
public string StaffSpecialty { get; set; }
[NotMapped]
public HttpPostedFileBase ImageFile { get; set; }
}
}
My Controller:
namespace ShimbaSchool.Controllers
{
public class StaffController : Controller
{
EventMessageDepartmentContext db = new EventMessageDepartmentContext();
public ActionResult Index()
{
return View(db.StaffTable.ToList());
}
}
}
The View:
#model IEnumerable<ShimbaSchool.Models.Staff>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ImagePath)
</th>
<th>
#Html.DisplayNameFor(model => model.StaffName)
</th>
<th>
#Html.DisplayNameFor(model => model.StaffSpecialty)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ImagePath)
</td>
<td>
#Html.DisplayFor(modelItem => item.StaffName)
</td>
<td>
#Html.DisplayFor(modelItem => item.StaffSpecialty)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.StaffId }) |
#Html.ActionLink("Details", "Details", new { id=item.StaffId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.StaffId })
</td>
</tr>
}
</table>
On executing i get the error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List 1[ShimbaSchool.Models.Staff]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable 1[ShimbaSchool.Models.EventMessageDepartment]'.
Please help me fix this situation and understand the logic so that there is no next time. To add on i used the same model with another controller and its working fine though the view were rendered on different layouts of the same project.
Actually the list you are passing from controller method to view is a List of EventMessageDepartment, not List of Staff. Please check it properly and pass List of Staff from controller method to the view.
If you are ensured that the data you are passing to the view is List<ShimbaSchool.Models.Staff> then please check your Layout.cshtml page. May be there is #model IEnumerable<EventMessageDepartment> is referenced.
Did you try the following?
public ActionResult Index()
{
return View(db.StaffTable.AsEnumerable());
}
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);
I couldn't find a solution for my issue, I have tried many alternatives but I could not resolve it.
I generate my database with the model first, afterwards I have used Scaffolding to generate the Views (Index, Create, Edit, Delete..). The only view (Index) with the model use IEnumerable.
The Index View was :
#model IEnumerable<CAD_CMDBv2.Models.Location>
#{
ViewBag.Title = "Location's Management";
}
<h2>All Locations</h2>
<p>
#Html.ActionLink("Create Location", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Location.site_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.country_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.region_name)
</th>
<th></th>
</tr>
#foreach(var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Location.site_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.country_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.region_name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Location.location_id }) |
#Html.ActionLink("Details", "Details", new { id = item.Location.location_id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Location.location_id })
</td>
</tr>
}
</table>
I want to insert an asynchronous form for the datasearch, so that becomes:
#model IEnumerable<CAD_CMDBv2.Models.RechercheLocationViewModel>
#{
ViewBag.Title = "Location's Management";
}
<h2>All Locations</h2>
<p>
#Html.ActionLink("Create Location", "Create")
</p>
#using (Html.BeginForm("Search", "Restaurant", FormMethod.Get))
{
#Html.TextBoxFor(r => r.Recherche)
<input type="submit" value="Rechercher" />
<p>Search Results </p>
if (Model.ListeLocations.Count == 0)
{
<p> No Results but you can create it !</p>
}
else
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Location.site_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.country_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.region_name)
</th>
<th></th>
</tr>
#foreach(var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Location.site_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.country_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.region_name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Location.location_id }) |
#Html.ActionLink("Details", "Details", new { id = item.Location.location_id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Location.location_id })
</td>
</tr>
}
</table>
}
}
I have modified the model in adding a View Model class to allow in IndexView to take as model the View Model by taking over the parameters Locations and use the Search parameter:
//------------------------------------------------------------------------------
// <auto-generated>
// Ce code a été généré à partir d'un modèle.
//
// Des modifications manuelles apportées à ce fichier peuvent conduire à un comportement inattendu de votre application.
// Les modifications manuelles apportées à ce fichier sont remplacées si le code est régénéré.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CAD_CMDBv2.Models
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class Location
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Location()
{
this.User = new HashSet<User>();
this.host = new HashSet<Host>();
this.client_catia = new HashSet<Client_catia>();
this.client_smartam = new HashSet<Client_smarteam>();
}
public int location_id { get; set; }
[Display(Name = "Site's Name")]
public string site_name { get; set; }
[Display(Name = "Country's Name")]
public string country_name { get; set; }
[Display(Name = "Region's Name")]
public string region_name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<User> User { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Host> host { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Client_catia> client_catia { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Client_smarteam> client_smartam { get; set; }
}
public class RechercheLocationViewModel : IEnumerable<Location> {
public string Recherche {get; set;}
public Location Location { get; set; }
public List<Location> ListeLocations;
public IEnumerator<Location> GetEnumerator()
{
return ListeLocations.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ListeLocations.GetEnumerator();
}
}
}
The current Controller
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using CAD_CMDBv2.Models;
namespace CAD_CMDBv2.Areas.Locations.Controllers
{
public class LocationsController : Controller
{
private ModeleDonneesCMDBContext db = new ModeleDonneesCMDBContext();
// GET: Locations/Locations
public ActionResult Index()
{
var liste = db.Locations.ToList();
var listeTriee = liste.OrderBy(t => t.site_name);
return View(listeTriee);
}
...
But that generates two errors of the same type about IEnumerable in the Index View on the lines:
#Html.TextBoxFor(r => r.Recherche)
And
if (Model.ListeLocations.Count == 0)
I got this error:
CS1061 Error 'IEnumerable' does not contain
a definition for 'ListeLocations' and no extension method
'ListeLocations' accepting a first argument of type
'IEnumerable' could be found (are
you missing a using directive or an assembly reference ?)
What does that mean? How can I resolve this? I still have some difficulty with understanding the IEnumerable interface.
Your Location class contains the properties Recherche and ListeLocation, but an IEnumerable of that class does not have those properties.
You are using the IEnumerable of the class as an instance of that class, that can't work.
You should think about what you need your model to be, because in one part of the view you use Model as if it were a Location, and in another part (#foreach(var item in Model) {) you use it as an IEnumerable
When you use the IEnumerable interface as a model, you are telling the View you have some kind of list, collection, or something you can 'Enumerate' as a model. A list of your Location objects, so to speak, not a single one.
Edit in response to your comments:
Change the #model IEnumerable<CAD_CMDBv2.Models.RechercheLocationViewModel> to CAD_CMDBv2.Models.RechercheLocationViewModel
Then you need yo change the controller Action:
Instead of :
var liste = db.Locations.ToList();
var listeTriee = liste.OrderBy(t => t.site_name);
return View(listeTriee);
use:
var model = new RechercheLocationViewModel();
model.AddRange(db.Locations.OrderBy(t => t.site_name));
return View(model);
But that won't make it 'work':
Your search query cannot take place in the view, you will have to go back to the server for that, so the architecture of your model is not quite right; you don't need all your locations in there, an what the single Location is there for I don't understand as well. If you want to do an async search in the view, you need an AJAX call back to the server that's going to return the search result. Your form is now just going to post something back to a Search action on your controller, and I don't know what that action does.
I can only advice you to study a bit more on creating search forms with AJAX in ASP.NET MVC
This is where your error is:
var listeTriee = liste.OrderBy(t => t.site_name);
return View(listeTriee);
Instead of passing a single model to your View, you are passing a collection (IEnumerable) which indeed doesn't have the property ListeLocations.
You should create a viewmodel and put the collection in there:
public class ListeTrieeViewModel
{
...
public IEnumerable<Locations> ListeLocations {get; set;}
}
Then you can pass that model in your controller:
public ActionResult Index()
{
var liste = db.Locations.ToList();
var listeTriee = liste.OrderBy(t => t.site_name);
var viewModel = new ListeTrieeViewModel { ListeLocations = listeTriee; }
return View(viewModel);
}
Now your check in the view will work:
if (Model.ListeLocations.Count() == 0)
Not sure why I got down voted but I'm going to re-write my question after doing some research and testing. This is a side project that I'm using to learn MVC/EF/Repository/Bootstrap etc. I only get a couple hours here a few nights a week to work on it.
Basic original question:
I know I should really be using a List<> in a ViewModel to pass the data to the View but I'm not sure how or if it will meet my requirement.
What I am trying to do is get a list of users to display in a table which would have a checkbox in each row. Above that table I want to have a list of Groups to that they could be assigned to. You select the section from the DropDownList (DDL) and then check who you want to assign it to. It's the groups/sections that I want want to assign as a list and pass to the View.
So, I've got a ViewModel with a list and I'm using a repository to populate the VM. What I don't know how to do exactly is where/when to populate that list with each VM object and even if I do and there are say 50 users I wouldn't want to make 50 trips to the DB to pull the same information.That is why I'm thinking that for this scenario using the ViewBag to pass that Group list to the View might be justifiable. On the flip side I would like to learn how to populate that list properly in the VM for future coding.
Updated question/code:
So, after more research and following some suggestions I've now got the following code. I'm still not sure how I will properly populate my Patrols in my ViewModel in order to populate the DDL in my View.
At the moment I've got the View displaying the table with the checkboxes. Now I'm back to working on getting the values to populate the DDL and then I'll have to work on posting to the controller, looping to find the check rows, and updating the database. In my case each member record is defaulted to a PatrolId=0 and this page should allow me to update the PatrolId to a value from the DDL.
The Patrols property in the PatrolMemberViewModel should be a list of about 5 records that I would pull from a DB table instead of hard coding in the DDL.
ViewModel:
public class PatrolViewModel
{
public int PatrolId { get; set; }
public string PatrolName { get; set; }
}
public class PatrolMemberViewModel
{
[Key]
public int MemberId { get; set; }
public int PatrolId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Phone")]
public string PhonePrimary { get; set; }
[Display(Name = "Email")]
public string EmailPrimary { get; set; }
public bool IsSelected { get; set; }
public PatrolViewModel Patrols { get; set; }
}
Controller:
public ViewResult Unassigned()
{
try
{
IEnumerable<PatrolMemberViewModel> model = repository.SelectAllUnassigned();
return View(model);
}
catch (Exception)
{
ModelState.AddModelError(string.Empty, "Error retrieving the record.");
return View();
}
}
Repository:
public IEnumerable<PatrolMemberViewModel> SelectAllUnassigned()
{
using (DataContext db = new DataContext())
{
var results = (from p in db.Person
where p.IsActive == true
&& p.IsScout == true
&& p.PatrolId == 0
select new PatrolMemberViewModel
{
MemberId = p.PID,
FirstName = p.FirstName ?? string.Empty,
LastName = p.LastName ?? string.Empty,
EmailPrimary = p.EmailPrimary ?? string.Empty,
PhonePrimary = p.PhonePrimary ?? string.Empty,
PatrolId = p.PatrolId,
IsSelected = false
}
).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return results;
}
}
View:
#model IList<ProjectName.ViewModels.PatrolMemberViewModel>
#{
ViewBag.Title = "Unassigned";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Patrols</h2>
#using (Html.BeginForm("Update", "Patrol", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "alert alert-danger" })
<table class="table table-bordered table-striped table-hover table-condensed tbackground">
<tr>
<th class="text-center">
</th>
<th class="text-center">
First Name
</th>
<th class="text-center">
Last Name
</th>
<th class="text-center">
Email
</th>
<th class="text-center">
Phone
</th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td class="text-center">
#Html.CheckBoxFor(m => m[i].IsSelected)
</td>
<td>
#Html.DisplayFor(m => m[i].FirstName)
</td>
<td>
#Html.DisplayFor(m => m[i].LastName)
</td>
<td>
#Model[i].EmailPrimary
</td>
<td class="text-center">
#Html.DisplayFor(m => m[i].PhonePrimary)
</td>
</tr>
}
</table>
<div class="control-wrapper">
<input type="submit" id="btnSubmit" value="Assign" class="btn btn-success" />
</div>
}
<p> </p>
Start by creating the view models to represent what you want to display/edit in the view. Your PatrolMemberViewModel can be used but remove the [Key] attribute and the int PatrolId and PatrolViewModel Patrols properties.
Then create the parent view model
public class AssignPatrolViewModel
{
[Display(Name = "Patrol")]
[Required(ErrorMessage = "Please select a patrol")]
public int? SelectedPatrol { get; set; }
public IEnumerable<SelectListItem> PatrolList { get; set; }
public List<PatrolMemberViewModel> Members { get; set; }
}
and you GET method would be
public ViewResult Unassigned()
{
var model = new AssignPatrolViewModel
{
PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName"), // modify to suit
Members = repository.SelectAllUnassigned().ToList()
};
return View(model);
}
and in the view
#model AssignPatrolViewModel
....
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.SelectedPatrol)
#Html.DropDownListFor(m => m.SelectedPatrol, Model.PatrolList, "Please select")
#Html.ValidationMessageFor(m => m.SelectedPatrol)
<table>
.... // thead elements
<tbody>
#for(int i = 0; i < Model.Members.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => m.Members[i].IsSelected)
#Html.HiddenFor(m => m.Members[i].MemberId)
// add other hidden inputs for properties you want to post
</td>
<td>#Html.DisplayFor(m => m.Members[i].FirstName)</td>
....
</tr>
}
</tbody>
</table>
<input type="submit" value="Assign" class="btn btn-success" />
}
Then in the POST method
[HttpPost]
public ViewResult Unassigned(AssignPatrolViewModel model)
{
if (!ModelState.IsValid)
{
model.PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName");
return View(model);
}
// Get selected ID's
IEnumerable<int> selected = model.Members.Where(m => m.IsSelected).Select(m => m.MemberId);
// Get matching data models
var members = db.Person.Where(p => selected.Contains(p.PID)); // modify to suit
// loop each each member, update its PatrolId to the value of model.SelectedPatrol
// save and redirect
}
You could create a new class for your view model, with the list of users and the list of sections as properties within it. Some people seem to like that approach.
But I think your use of ViewBag for passing the list of sections is perfectly valid. I do that all the time for DDLs like this.
I have a page to display every log (message, time, type, customerId, Name) in a html table. Since the log is huge I am using a IPagedList in the Razor MVC and this works perfectly. I currently have 2 search boxes (for admins) and 1 for members. Where you can search by the message and customer ID.
Now the problem is that I don't want the users to just have a textbox where you only can put in a number (for example you put in customer ID 2 and get the customer T) - but instead I want a dropdown with all the current customer names connected to the customer IDs.
I have this functionality in another page I use but it only works because I return the model on the other page and because the log page returns a "IPagedListModel" instead of a "Model" I can't use this solution. How would I make this solution work for my log page as well?
HTML code
#:<p>#Html.DropDownListFor(m => m.SelectedCustomer, Model.CustomerList)</p>
Model
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Collections.Generic;
using PagedList;
using System.Web.Mvc;
namespace ASDMVC.Models
{
[Table("Log")]
public class LogModel
{
[Key]
public long id { get; set; }
public string message { get; set; }
public DateTime timeStamp { get; set; }
public string level { get; set; }
public int customerId { get; set; }
[NotMapped]
public string Name { get; set; }
}
public class LogModelVM
{
public int SelectedCustomer { get; set; }
public IEnumerable<SelectListItem> CustomerList { get; set; }
public string Message { get; set; }
public IPagedList<LogModel> Logs { get; set; }
}
public class LogDBContext:DbContext
{
public LogDBContext() : base("MySqlConnection")
{
}
public DbSet <LogModel> Log { get; set; }
public IQueryable<LogModel> GetLogs()
{
return from log in Log
select log;
}
}
}
Controller
public class DbCustomerIds
{
public List<DbCustomer> GetCustomerIds()
{
List<DbCustomer> Customers = new List<DbCustomer>();
string queryString = "SELECT * FROM dbo.customers";
SqlDataAdapter adapter = new SqlDataAdapter(queryString, System.Configuration.ConfigurationManager.ConnectionStrings["MySqlConnection"].ConnectionString);
DataSet customers = new DataSet();
adapter.Fill(customers, "Customers");
foreach (DataRow item in customers.Tables[0].Rows)
{
DbCustomer cid = new DbCustomer();
cid.FakeId = Convert.ToInt32(item["Id"]);
cid.FakeName = Convert.ToString(item["Name"]);
Customers.Add(cid);
}
return Customers;
}
}
private IEnumerable<SelectListItem> GetCustomerIds()
{
var DbCustomerIds = new DbCustomerIds();
var customers = DbCustomerIds
.GetCustomerIds()
.Select(x =>
new SelectListItem
{
Value = x.FakeId.ToString(),
Text = x.FakeName
});
return new SelectList(customers, "Value", "Text");
}
}
[HttpPost]
public PartialViewResult LogPartialView(string searchString, string searchString2, string currentFilter, string currentFilterz, int? page, string sortOrder)
{
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
Customer = GetCustomerIds();
message = db.GetLogs();
int pageSize = 10;
int pageNumber = (page ?? 1);
var logs = message.OrderByDescending(i => i.timeStamp).ToPagedList(pageNumber, pageSize);
foreach (var log in logs)
log.Name = Customer.Where(a => a.Value == log.customerId.ToString()).FirstOrDefault().Text;
LogModelVM LMVM = new LogModelVM();
LMVM.Logs = logs;
LMVM.CustomerList = Customer;
return PartialView("_LogPartialLayout", LMVM);
}
LogModelVM LMVM = new LogModelVM();
LMVM.Logs = logs;
LMVM.CustomerList = Customer;
return PartialView("_LogPartialLayout", LMVM);
}
_LogPartialLayout
#model ASDMVC.Models.LogModelVM
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
<table class="table">
<tr>
<th id="tableth">
message
</th>
<th id="tableth">
timestamp
</th>
<th id="tableth">
level
</th>
<th id="tableth">
customerId
</th>
<th id="tableth">
customerName
</th>
</tr>
#foreach (var item in Model.Logs)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.message)
</td>
<td>
#Html.DisplayFor(modelItem => item.timeStamp)
</td>
<td>
#Html.DisplayFor(modelItem => item.level)
</td>
<td>
#Html.DisplayFor(modelItem => item.customerId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
}
#if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Member"))
{
<table class="table">
<tr>
<th id="tableth">
message
</th>
<th id="tableth">
timestamp
</th>
<th id="tableth">
level
</th>
</tr>
#foreach (var item in Model.Logs)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.message)
</td>
<td>
#Html.DisplayFor(modelItem => item.timeStamp)
</td>
<td>
#Html.DisplayFor(modelItem => item.level)
</td>
</tr>
}
</table>
}
Page #(Model.Logs.PageCount < Model.Logs.PageNumber ? 0 : Model.Logs.PageNumber) of #Model.Logs.PageCount
#Html.PagedListPager(Model.Logs, page => Url.Action("LogPartialView",
new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter, currentFilterz = ViewBag.CurrentFilterz }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(PagedListRenderOptions.ClassicPlusFirstAndLast,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "divLogs"
}))
Any help would be apprechiated, sorry for the long question - I just wanted to get all the information which seems relevant to the situation.
Thanks in advance.
Current error when running:
[InvalidOperationException: The model item passed into the dictionary is of type 'PagedList.PagedList`1[NowasteWebPortalMVC.Models.LogModel]', but this dictionary requires a model item of type 'NowasteWebPortalMVC.Models.LogModelVM'.]
Create a view model with the properties you need in the view
public class LogModelVM
{
public int SelectedCustomer { get; set; }
public IEnumerable<SelectListItem> CustomerList { get; set; } // suggested name change
public string Message { get; set; } // for the message search box?
public IPagedList<NowasteWebPortalMVC.Models.LogModel> Logs { get; set; }
.... // add properties for sortorder etc
}
Then in the controller method, initiaize a new LogModelVM and assign the values (e.g. model.Logs = logs;), and return the view model so that in the view you can use
#model yourAssembly.LogModelVM
....
#Html.DropDownListFor(m => m.SelectedCustomer, Model.CustomerList) // why change the id attribute?
....
#Html.PagedListPager(Model.Logs, page => Url.Action(...`
You should also consider adding the other properties such as sortOrder and currentfilter rather than using ViewBag
Side note: Ensure that all associated views, including the main view also use #model yourAssembly.LogModelVM
I am trying to return a list of schedule in education system. I have a schedule model in my Project with this property:
public partial class Schedule
{
public Schedule()
{
this.ClassTimes = new HashSet<ClassTime>();
this.Scores = new HashSet<Score>();
}
public int Id { get; set; }
public int TeacherId { get; set; }
public int LessonId { get; set; }
public int ClassId { get; set; }
public int DegreeId { get; set; }
public int FacultyId { get; set; }
public int SemesterId { get; set; }
public int MajorId { get; set; }
public System.DateTime DateOfExame { get; set; }
public string Capacity { get; set; }
public string locationOfExame { get; set; }
public virtual Class Class { get; set; }
public virtual ICollection<ClassTime> ClassTimes { get; set; }
public virtual Degree Degree { get; set; }
public virtual Faculty Faculty { get; set; }
public virtual Lesson Lesson { get; set; }
public virtual Major Major { get; set; }
public virtual ICollection<Score> Scores { get; set; }
public virtual Semester Semester { get; set; }
public virtual Teacher Teacher { get; set; }
}
So in this model I save the id of my entity for example major, teacher, lesson, etc. So I need to return a list of my schedules. So I have to convert the id of my entity to name of that entity. So I design a schedule controller in my MVC project like this:
private readonly ScheduleRepositor obj = new ScheduleRepositor();
public ActionResult Index()
{
var list=new List<SchedulePresentation>();
ClassRepository objclassrep=new ClassRepository();
DegreeRepositor objdegreerep=new DegreeRepositor();
FacultyRepositor objfactulyrep=new FacultyRepositor();
LessonRepository objLessonRep=new LessonRepository();
MajorRepository objmajorrep=new MajorRepository();
SemesterRepositor objsemesterrep=new SemesterRepositor();
TeacherRepositor objteacherrep=new TeacherRepositor();
DateConverter objdateconverte = new DateConverter();
List<Schedule> model = obj.GetLessonlist();
foreach (var t in model)
{
SchedulePresentation objpres=new SchedulePresentation();
objpres.Capacity = t.Capacity;
objpres.DateOfExam = objdateconverte.ConvertToPersianToShow(t.DateOfExame);
objpres.className = objclassrep.FindClassById(t.ClassId).ClassName;
objpres.degreeName = objdegreerep.FindDegreeById(t.DegreeId).DegreeName;
objpres.examLocation = t.locationOfExame;
objpres.facultyName = objfactulyrep.FindFacultyById(t.FacultyId).FacultyName;
objpres.lessonName = objLessonRep.FindLessonById(t.LessonId).LessonName;
objpres.majorName = objmajorrep.FindMajorById(t.MajorId).MajorName;
objpres.semesterName = objsemesterrep.FindSemesterById(t.SemesterId).SemesterName;
objpres.teacherName = objteacherrep.FindTeacherById(t.TeacherId).Name + " " +
objteacherrep.FindTeacherById(t.TeacherId).LastName;
list.Add(objpres);
}
return View(list);
}
So I just create a repository for each entity to return the name of my entity by id.
And I create a representation class for my schedule to convert the id to name of my entities like this:
public class SchedulePresentation
{
public string teacherName { set; get; }
public string lessonName { set; get; }
public string className { set; get; }
public string degreeName { set; get; }
public string facultyName { set; get; }
public string semesterName { set; get; }
public string majorName { set; get; }
public string DateOfExam { set; get; }
public string Capacity { set; get; }
public string examLocation { set; get; }
}
So I have two problems. I have 4 projects in my solution:
DomainClass
Model
Repository
MVC project
So
Is it a good method that I convert these Id to their name in the MVC layer, or it is better to make a Repository or create a model for this list of schedules?
In the last line when I want to return my list I got this error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[EducationMVC.PresentationClass.SchedulePresentation]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[DomainClasses.Schedule]'.
The view code of MVC :
#model IEnumerable<DomainClasses.Schedule>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.TeacherId)
</th>
<th>
#Html.DisplayNameFor(model => model.LessonId)
</th>
<th>
#Html.DisplayNameFor(model => model.ClassId)
</th>
<th>
#Html.DisplayNameFor(model => model.DegreeId)
</th>
<th>
#Html.DisplayNameFor(model => model.FacultyId)
</th>
<th>
#Html.DisplayNameFor(model => model.SemesterId)
</th>
<th>
#Html.DisplayNameFor(model => model.MajorId)
</th>
<th>
#Html.DisplayNameFor(model => model.DateOfExame)
</th>
<th>
#Html.DisplayNameFor(model => model.Capacity)
</th>
<th>
#Html.DisplayNameFor(model => model.locationOfExame)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.TeacherId)
</td>
<td>
#Html.DisplayFor(modelItem => item.LessonId)
</td>
<td>
#Html.DisplayFor(modelItem => item.ClassId)
</td>
<td>
#Html.DisplayFor(modelItem => item.DegreeId)
</td>
<td>
#Html.DisplayFor(modelItem => item.FacultyId)
</td>
<td>
#Html.DisplayFor(modelItem => item.SemesterId)
</td>
<td>
#Html.DisplayFor(modelItem => item.MajorId)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateOfExame)
</td>
<td>
#Html.DisplayFor(modelItem => item.Capacity)
</td>
<td>
#Html.DisplayFor(modelItem => item.locationOfExame)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
Your view probably has a strongly typed model like this
#model IEnumerable<DomainClasses.Schedule>
but you are returning a List<SchedulePresentation>.
One solution would be to change the model line to: #model IEnumerable<SchedulePresentation>.
The answer to your question lies in the error message:
The model item passed into the dictionary is of type
'System.Collections.Generic.List1[EducationMVC.PresentationClass.SchedulePresentation]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable1[DomainClasses.Schedule]'.
It seems like the view corresponding to this method (by default the Index view of your controller) is expecting a model of type IEnumerable<Schedule>.
If you wish to send a List<SchedulePresentation>, change the model type in the view to IEnumerable<SchedulePresentation>.
Hint: In your view, one of the first lines should be of the form:
#model IEnumerable<DomainClasses.Schedule>
which you could change like this:
#model IEnumerable<EducationMVC.PresentationClass.SchedulePresentation>
The answer lies in the error reported by the compiler. You're passing to a view a model object of a certain type (SchedulePresentation), when the view expects a model object of type Schedule.
In particular, in the lines:
List<Schedule> model = obj.GetLessonlist();
foreach (var t in model)
{
SchedulePresentation objpres=new SchedulePresentation();
objpres.Capacity = t.Capacity;
objpres.DateOfExam = objdateconverte.ConvertToPersianToShow(t.DateOfExame);
objpres.className = objclassrep.FindClassById(t.ClassId).ClassName;
objpres.degreeName = objdegreerep.FindDegreeById(t.DegreeId).DegreeName;
objpres.examLocation = t.locationOfExame;
objpres.facultyName = objfactulyrep.FindFacultyById(t.FacultyId).FacultyName;
objpres.lessonName = objLessonRep.FindLessonById(t.LessonId).LessonName;
objpres.majorName = objmajorrep.FindMajorById(t.MajorId).MajorName;
objpres.semesterName = objsemesterrep.FindSemesterById(t.SemesterId).SemesterName;
objpres.teacherName = objteacherrep.FindTeacherById(t.TeacherId).Name + " " +
objteacherrep.FindTeacherById(t.TeacherId).LastName;
list.Add(objpres);
}
you're creating a List<Schedule> but you add to it objects of type SchedulePresentation, when the model specified in your view expects the type IEnumerable<DomainClasses.Schedule>. For this reason, you have the error:
The model item passed into the dictionary is of type
'System.Collections.Generic.List1[EducationMVC.PresentationClass.SchedulePresentation]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable1[DomainClasses.Schedule]'.
To solve the problem, change your model line to #model IEnumerable<SchedulePresentation>.
Regarding your first question, I'd say the MVC controller is the right place to extract the information from what the repository returns, and put it in a view model (your SchedulePresentation class). However, the code you posted will result in an aweful lot of database queries - for each item in your schedule, you'll end up with eight extra queries to get the related items. In a schedule with 20 items, that's an extra 160 database queries to get stuff you could probably have included.
You've tagged this [entity-framework], so I'm going to assume that you're using it to get the Schedule items from your database. Instead of using the id's of the items, use the actual properties:
for (var t in model) {
SchedulePresentation objpres=new SchedulePresentation();
objpres.Capacity = t.Capacity;
objpres.DateOfExam = objdateconverte.ConvertToPersianToShow(t.DateOfExame);
objpres.className = t.Class.ClassName; // <- this works!
}
Now, doing just that will not save you any db queries - it will just clean up your code a little. Due to lazy loading, EF will still retrieve the relevant Class object from the db when you ask to look at it, rather than at the top. However, if you in your repository add an Include statement to your EF query, you'll start saving - then EF will include the related items in the original query, and they will already be fetched from the db. This will look something like the following:
var ctx = getYourEFContextSomehow(); // pseudocode, obviously...
var scheduleItems = ctx.Schedules.Include(s => s.Class);
// Add more Include for other objects
There are other ways to turn off lazy loading for specific properties or entire objects as well - check out http://msdn.microsoft.com/en-us/data/jj574232