I am new to C#, MVC, LINQ, Entity Framework, and everything else I'm doing. I have picked the stackoverflow brain-trust and other sites on this issue and do not see how to resolve it.
I am trying to implement the Repository pattern for a new application we are building and having problems returning the query results. The first and most pressing issue is that I am getting the error below, and the second is more a matter of figuring out how to handle empty results from the query.
For this issue, I am trying to get a list of requests from the database to be presented in the dashboard view. We have a working prototype that uses SQL queries, and I'm trying to replace it with repositories, which may be overkill, but it is something we feel we want to do.
Here is the view:
#using RAM.DAL.Models
#model RequestViewModel
#{
ViewBag.Title = "Dashboard";
}
<h1>Dashboard</h1>
<div class="col-md-4">
<h2>Resource Requests</h2>
<div class="panel">
<table class="table table-hover">
<thead>
<tr class="bg-primary">
<th>Number</th>
<th>Mission Title</th>
<th>Resource Requested</th>
<th>Person</th>
</tr>
</thead>
<tbody>
#if (Model.isEmpty)
{
<tr>
<td colspan="4">No requests pending</td>
</tr>
}
else
{
<tr onclick="location.href= '#Url.Action("Assignment", "Mission", new { id = Model.ID })'">
<td>#Model.ID</td>
<td>#Model.title</td>
<td>#Model.resourceTitle</td>
<td>#Model.userName</td>
</tr>
}
</tbody>
</table>
</div>
<!--<p><a class="btn btn-default" href="#">Content 1 Btn ยป</a></p>-->
</div>
Here is the ViewModel:
using System;
namespace RAM.DAL.Models
{
public class RequestViewModel
{
public int? ID { get; set; }
public string title { get; set; }
public string requestText { get; set; }
public string user_ID { get; set; } //The userID of the user being requested.
public string userName { get; set; } //Full name of the user being requested
public int? fromResourceID { get; set; } //The resource where the request was generated from
public int? toResourceID { get; set; } //The resource where the reassigned worker is requested to go to
public string resourceTitle { get; set; } //Title of the resource where the reassigned worker is requested to go to
public DateTime? requestDate { get; set; }//The date the request was made
public bool? isEmpty { get; set; }
}
}
And here is the repository up to the GetRequests method I'm having problems with (the rest is not implemented yet):
using RAM.DAL.Models;
using System;
using System.Linq;
using System.Data;
using System.Collections.Generic;
namespace RAM.DAL
{
public class RequestRepository : IRequestRepository<RequestViewModel>
{
private RAMcontext context;
public RequestRepository(RAMcontext context)
{
this.context = context;
}
public IEnumerable<RequestViewModel> GetRequests()
{
var requests = from r in context.RAM_Requests
join u in context.Users on r.user_ID equals u.User_ID
join res in context.RAM_Resources on r.toResourceID equals res.ID
where r.resolved == false
select new RequestViewModel()
{
title = r.title,
ID = r.ID,
fromResourceID = r.fromResourceID,
toResourceID = r.toResourceID,
user_ID = r.user_ID,
userName = u.First_Name + " " + u.Last_Name,
resourceTitle = res.title,
requestText = r.requestText,
requestDate = r.requestDate
};
/* }
catch
{
RequestViewModel empty = new RequestViewModel
{
isEmpty = true
};
return empty;
}*/
return requests.ToList().AsEnumerable();
}
The error I'm getting is:
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[RAM.DAL.Models.RequestViewModel]',
but this dictionary requires a model item of type
'RAM.DAL.Models.RequestViewModel'.
From the error message, I guess your action method is sending a collection of RequestViewModel to the view. But your view is strongly typed to a single instance of RequestViewModel , not a collection. That is the reason you are getting this error.
Since you want to show a collection of requests, you should change the view to be strongly typed to a collection. You can use the LINQ Any() method to determine whether you have more than one item in the collection passed to the view and show/hide a message/ table to display the data.
#model IEnumerable<RequestViewModel>
<h1>Dashboard</h1>
#if(!Model.Any())
{
<p>No records found </p>
}
else
{
<table>
<tr>
<th>Title</th>
<th>User name </th>
</tr>
#foreach(var req in Model)
{
<tr>
<td>#req.title</td>
<td>#req.userName</td>
</tr>
}
</table>
}
Related
Edit My view is using the Employer model. Employer and JobPosting have a 1:M relationship. I will share more of the view for context.
Context: In my application, I want to show the Employer the number of applicants who applied for their JobPosting. The code that I currently have written is not returning any value. It's not throwing any errors- but it's not working either. I'm pretty sure the issue is in my controller, but I'll provide the Model and View as well.
Controller:
public ActionResult AppCount()
{
foreach (var app in db.JobPostings.ToList())
{
int id = app.JobPostingID;
int count= db.Applications.Where(a => a.JobPostingID == id).Count();
app.AppCount = count;
ViewBag.AppCount = count;
}
return View();
}
View:
#model InTurn_Model.Employer
.
.
.
<h2>My Job Postings</h2>
<p>
#Html.ActionLink("Create New", "Create", "JobPostings", null, null)
</p>
<div id="employeeContainer"></div>
<table class="table table-striped">
<tr>
<th>Position</th>
<th>Job Type</th>
<th>Number of Applicatiosn</th>
<th></th>
</tr>
#foreach (var item in Model.JobPostings)
{
if (item.EmployerID == Model.EmployerID)
{
<tr>
<td>
#Html.DisplayFor(model => item.Position)
</td>
<td>
#Html.DisplayFor(model => item.JobType)
</td>
<td>#ViewBag.AppCount</td>
<td>#Html.ActionLink("Details", "Details", "JobPostings", new { id = item.JobPostingID }, null) </td>
</tr>
}
}
</table>
Model:
[MetadataType(typeof(JobPostingMetaData))]
public partial class JobPosting
{
public int AppCount { get; set; }
private sealed class JobPostingMetaData
{
[Display(Name = "Job Posting ID")]
public int JobPostingID { get; set; }
[Display(Name = "Employer ID")]
public int EmployerID { get; set; }
[Display(Name = "Description")]
public string Desc { get; set; }
[Display(Name = "Job Type")]
public JobType JobType { get; set; }
[Display(Name = "Employment Type")]
public TimeType TimeType { get; set; }
[DataType(DataType.Currency)]
public decimal Wage { get; set; }
}
}
There are two problems that I see.
First, you are not passing Model from controller to view. However, you are iterating through Model.JobPostings. It is empty.
Second, you assign ViewBag.AppCount in the loop. So, all values, except for the last one are lost. But if you fix the first problem (use Model instead of ViewBag) - the second might go away by itself.
You need to specify the model in the view with #model:
#model YourNameSpace.JobPosting
Then return that model to the view:
public ActionResult AppCount()
{
foreach (var app in db.JobPostings.ToList())
{
int id = app.JobPostingID;
int count= db.Applications.Where(a => a.JobPostingID == id).Count();
app.AppCount = count;
ViewBag.AppCount = count;
}
return View(app);
}
This will make the values in the model available to the view. There is no need to use ViewBag, as AppCount is part of the model.
I was overthinking this. I just needed to set this up from the JobPosting model, and then the rest worked, I didn't need to iterate through the Controller at all.
public int AppCount => Applications.Count;
I have a question. I created a system where admin can add, edit or delete items (members of society in this case). I just need a sorting by year (top-bottom: from old to new) automatically without choices or buttons. I tried to run sorting with help of JavaScript, but it didn't work, unfortunately. It is sucessfully worked with data which was written with HTML, but, probably it doesn't work with data loaded from database which is my case. I read Microsoft tutorial, but didn't understand, honestly. Can you help me with automatic sorting by year? Thank You. All code is which downloads data from the database and shows it to the user is laid out below.
TeamController:
namespace Blog.Controllers {
public class TeamController : Controller {
private IRepository _repo;
public TeamController(IRepository repo)
{ _repo = repo; }
public IActionResult Index() {
var team = _repo.GetAllPlayers();
return View(team);
}
}
}
Player.cs in Model
namespace Blog.Models
{
public class Player {
public int Id { get; set; }
public string NickName { get; set; } = "";
public string Position { get; set; } = "";
public string MemberSince { get; set; } = "";
public string Category { get; set; } = "";
}
}
index.cshtml (Team)
#model IEnumerable<Player>
<div>
<table id="table">
<thead>
<tr>
<th scope="col">NickName</th>
<th scope="col">Position</th>
<th scope="col">Member since</th>
</tr>
</thead>
<tbody>
#foreach (var player in Model) {
#if (#player.Category == "Owner") {
<tr asp-controller="Team" asp-action="Player" asp-route-id="#player.Id">
<td data-label="NickName" id="nickname"> #player.NickName</td>
<td data-label="Position">#player.Position</td>
<td data-label="MemberSince">#player.MemberSince</td>
</tr>
}
}
</tbody>
</table>
</div>
Repository.cs
public Player GetPlayer(int id)
{ return _ctx.Team.FirstOrDefault(p => p.Id == id); }
public List<Player> GetAllPlayers()
{ return _ctx.Team.ToList(); }
IRepository.cs
Player GetPlayer(int id);
List<Player> GetAllPlayers();
Use orderby along with DateTime parsing.
public List<Player> GetAllPlayers()
{ return _ctx.Team.OrderBy(member => DateTime.Parse(member.MemberSince).Year).ToList(); }
You can use OrderByDesc if you want latest first.
I'm trying to build a teacher recommendation web app using sessions for lab, and have gotten to a particular point where I need to view the recommendations that a particular teacher has.
app
When I click on the number of recommendations, it should take me to a view that lists all the recommendations that particular person has, but instead I get an error page saying
'Lab3Models.Models.Person' does not contain a definition for 'Rating'
Here's some of my code, hopefully someone can point me in the right direction.
Recommendation Controller
using Lab3Models.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab3Models.Controllers
{
public class RecommendationController : Controller
{
private static IDictionary<string, Person> _people = null;
public ActionResult Add(string id)
{
if (Session["people"] != null)
{
_people = (Dictionary<string, Person>)Session["people"];
}
else
{
_people = new Dictionary<string, Person>();
Session["people"] = _people;
}
return View(_people[id]);
}
[HttpPost]
public ActionResult Create(string personId, Recommendation recommendation)
{
if (personId == null)
{
return HttpNotFound("Error, ID not found");
}
else
{ _people[personId].Recommendations.Add(recommendation);
return RedirectToAction("Index", "Home");
}
}
public ActionResult Show(string id)
{
if (Session["people"] != null)
{
_people = (Dictionary<string, Person>)Session["people"];
}
else
{
_people = new Dictionary<string, Person>();
Session["people"] = _people;
}
return View(_people);
}
}
}
Person & Recommendation Models
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public ICollection<Recommendation> Recommendations { get; set; }
public Person()
{
Recommendations = new List<Recommendation>();
}
public int NumberOfRecommendations
{
get
{
return Recommendations.Count;
}
}
public class Recommendation
{
public string Id { get; set; }
public int Rating { get; set; }
public string Narrative { get; set; }
public string RecommenderName { get; set; }
public Person ThePerson { get; set; }
}
}
When I put #model IDictionary<string, Lab3Models.Models.Person> in the top of my Show I get the error message 'Person' does not contain a definition for 'Rating' and no extension method 'Rating' accepting a first argument of type 'Person' could be found
If I put #model IDictionary<string, Lab3Models.Models.Recommendation> in the top of my view I get the error message ERROR
If anyone could help me out, it'd be greatly appreciated.
EDIT
#model IDictionary<string, Lab3Models.Models.Recommendation>
#{
ViewBag.Title = "Show";
}
<h2>Show</h2>
<table class="table">
<tr>
<th>
...
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.Value.Id
</td>
<td>
#item.Value.Rating
</td>
<td>
#item.Value.Narrative
</td>
<td>
#item.Value.RecommenderName
</td>
<td>
Delete |
</td>
</tr>
}
</table>
EDIT 2
I have #model IDictionary<string, Lab3Models.Models.Recommendation> at the top of my view and have changed the code in my view to look like this:
#foreach (var item in Model)
{
foreach (var rec in item.Recommendations)
{
var rating = rec.Rating;
var narr = rec.Narrative;
...
<tr>
<td>#rating</td>
<td>#narr</td>
<td>#recName</td>
<td>
Delete
</td>
</tr>
}
}
But I'm getting errors in my code specifically on Model in this statement #foreach (var item in Model) and on Value in the delete link. #item.Value.Id When I load the view, I get an error saying
'KeyValuePair' does not contain a definition for 'Recommendations' and no extension method 'Recommendations' accepting a first argument of type 'KeyValuePair'
Did I goof up somewhere logically?
You do want to use #model IDictionary, as that's the type you are using. The issue is that you are getting a type Person out of the dictionary, and attempting to display rating directly from that type. Without seeing your front-end code I can't pinpoint exactly how the issue is presenting, but can tell you what your issue is. Essentially, you are attempting to get the Rating property from the person object, but the Rating property is part of the Person object's Recommendation Collection.
I'm assuming here that you are iterating through each Person in the dictionary to build out the display. You also need to iterate through each Recommendation for each person if you want to access the Rating.
roughly
foreach(var person in #model) {
//person specific display things
foreach(var recommendation in person.Recommendations) {
var rating = recommendation.Rating;
// display rating things
}
}
I'm still working on my asp.net app. I have a page where orders table is displayed and i would like to include "details"column, so someone can choose order and then look up it's details.
Here is my View code:
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Seat</th>
<th>Movie</th>
<th>Date</th>
<th>Details</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.OrderList.results)
{
<tr>
<td>#item.objectId</td>
<td>#item.Seat</td>
<td>LOTR</td>
<td>#item.createdAt</td>
<td>Details</td>
</tr>
}
</tbody>
</table>
Ofcourse Url.Action is just for testing. Here is my controller method:
public ActionResult Details(OrderModel model)
{
return View(model);
}
Results is a List of OrderModel objects. I'd like to pass one OrderModel object that corresponds with chosen table row. The whole point is to display OrderModel object contents on Details page. Can someone explain me how to do that?
EDIT: My model:
OrderModel
public class OrderModel
{
/*class representing Order data*/
public string Seat { get; set; }
public string objectId { get; set; }
public DateTime? createdAt { get; set; }
public DateTime? updatedAt { get; set; }
}
My root for ordermodel(needed for json deserialization to list of objects)
public class OrderRootModel
{
public List<OrderModel> results { get; set; }
}
My baseviewmodel orderlist line(viewmodel is shared along all sites - i'm using shared layout):
public OrderRootModel OrderList { get; set; }
EDIT2:
Alright so after looking at my code i modified it, so Details page recieves BaseViewModel instead of ordermodel(i'm using shared layout).
BaseViewModel:
public class BaseViewModel
{
public OrderModel Order { get; set; }
public OrderRootModel OrderList { get; set; }
}
OrdersController:
public ActionResult Details(OrderModel order)
{
BaseViewModel model = new BaseViewModel();
model.Order = order;
return View(model);
}
Alright guys. I think i didn't get enough sleep so my thought process was a little bit off. As #user1672994 suggested i can just pass order id to details view(if i remember right passing whole object between view and controller is not recommended).
So if anyone is interested here is a solution:
View:
#foreach (var item in Model.OrderList.results)
{
<tr>
<td>#item.objectId</td>
<td>#item.Seat</td>
<td>LOTR</td>
<td>#item.createdAt</td>
<td>Details</td>
</tr>
}
Controller:
public ActionResult Details(string id)
{
ApiModel data = new ApiModel();
BaseViewModel model = new BaseViewModel();
model.Order = data.GetOrderData(id);
return View(model);
}
Model:
public OrderModel GetOrderData(string id)
{
OrderModel model = new OrderModel();
string url = "https://api.parse.com/1/classes/Orders" + "/" + id;
model = JsonConvert.DeserializeObject<OrderModel>(getParseIdData(url));
return model;
}
It works perfect. Thank you guys.
So I am working on a web application and have encountered a new challenge that I have been stuck on for a few weeks. I will provide background information regarding my application and my data models, as well as the desired end results.
Basic App Info & Context of the Problem:
My app is intended to serve as a tool which landscape contractors can use to help with managing their business operations. My app will provide a place where they can create an account, then enter all of their customer information ("Client" data model), as well as the information about jobs they are doing for each of their clients ("Job" data model). There is a one-to-many relationship between clients and jobs (one client can have many jobs, but only one client for any given job).
Background:
I have two simple data models, "Client" and "Job". My app is being built in the ASP.net MVC3 framework. Using the Entity Framework scaffolding mechanism, I have created the basic CRUD views for each of these data models (create, read, update, delete). This is great for the most part (I can create new clients and jobs, and edit existing ones easy enough).
Business Problem:
I have the need to permit batch creating for new jobs in my application. I want my users (landscape contractors) to be able to enter all the mowing jobs they completed for the day. As such, I would like to have my view for this process populate a table with all active clients - each with a checkbox beside the row. Then I would like the user to be able to check boxes for each client they did a new job for (mowed their lawn), and submit the form (enter jobs done), where the result would be new jobs created for each of these clients.
Technical Problem:
My best guess is that I need to create a custom ViewModel in the controller and send it to the view, where the ViewModel would be a list of new jobs created based on currently active clients. Then in the view, the checkboxes could have the Client_ID (unique identifier for clients) as their value (this would be part of the ViewModel. When the user submits the form, the view would pass the ViewModel back to the controller. Then the controller could go through the list of ViewModel jobs and create a new job for each ViewModel job that has the checkbox checked.
So, my question is - how can I use the controller to do the following:
1.) Build a list of ViewModel jobs at runtime based on data from the Client list ("Client" data model)?
2.) How can I then pass that to the view?
3.) Once it comes back to the controller, how can I iterate through the list and modify my other data model accordingly (create new "Job" items)?
I created the custom ViewModel, which contains attributes from clients and jobs that I need to construct the new job entries (client name, client address, client id, job notes, crew, crew size, time on job, checkbox to indicate completion, etc.). Let's say the user has 50 clients that he mows lawns for which are active clients. I would want to construct a ViewModel with 50 rows (to represent each client that could possibly have their lawn mowed). Then I want to send that to the view and display it with checkboxes, which indicate whether the lawn was cut or not. When the model comes back to the view, the controller would then take the rows with checks in the checkboxes and create new "Job" rows in that table.
Thanks in advance for any assistance you can provide, I know this is probably an easy one for many of you. I am new to C# and MVC3.
UPDATE:
Here is my code -
Job Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace LawnTracker.Models
{
public class Job
{
[Key]
public int Job_ID { get; set; }
public int Client_ID { get; set; }
public int Account_ID { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string Notes { get; set; }
public string SvcRoute { get; set; }
public string Service { get; set; }
public string Date { get; set; }
public double SvcPriceOverride { get; set; }
public float SvcQty { get; set; }
public string UofM { get; set; }
public bool Invoiced { get; set; }
public string Crew { get; set; }
public int TechCount { get; set; }
public string TimeStart { get; set; }
public string TimeFinish { get; set; }
public double TimeSpent { get; set; }
public string Image1 { get; set; }
public string Image2 { get; set; }
public string Image3 { get; set; }
public double MulchUsed { get; set; }
public double FertUsed { get; set; }
public double HerbUsed { get; set; }
public string NextDue { get; set; }
}
}
My MowingJobViewModel model -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace LawnTracker.Models
{
public class MowingJobViewModel
{
public int Client_ID { get; set; }
public bool Completed { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public double SvcPriceOverride { get; set; }
public string UofM { get; set; }
public int SvcQty { get; set; }
public string Notes { get; set; }
public string Date { get; set; }
public string Crew { get; set; }
public int TechCount { get; set; }
public string SvcRoute { get; set; }
public string Schedule { get; set; }
}
}
And my JobController -
// GET: /Job/CreateMowing
public ActionResult CreateMowing(string route = "", string sched = "")
{
List<SelectListItem> listItems = new List<SelectListItem>();
listItems.Add(new SelectListItem()
{
Value = "Lump Sum",
Text = "Lump Sum"
});
listItems.Add(new SelectListItem()
{
Value = "Hours",
Text = "Hours"
});
ViewBag.Units = new SelectList(listItems, "Value", "Text");
ViewBag.Routes = db.Clients.Select(r => r.SvcRoute).Distinct();
ViewBag.Sched = db.Clients.Select(r => r.MowSched).Distinct();
var model = from r in db.Clients
orderby r.SvcRoute
where (r.Mowing == true) &&
(r.Status == "Active") &&
(r.SvcRoute == route || (route == "")) &&
(r.MowSched == sched || (sched == ""))
select r;
if (model.Count() > 0)
{
ViewBag.total = model.Select(r => r.MowPrice).Sum();
}
else
{
ViewBag.total = 0.00;
}
/* Build a list of MowingJobViewModel objects based on the above defined list of clients
* who are subscribed to mowing and active. This will enable batch entry for new jobs done.
* This list of MowingJobViewModel objects will be sent to the client after a HTTP GET
* request for the CreateMowing view. The user will be able to check boxes associated
* with each client in the client list. When the form is submitted, the controller
* receives the model back with the updated information (completed, notes, etc.) about
* each job. Then the controller must update the jobs table, adding the new jobs based on
* the view model returned from the view / client.
*
*/
//Create a new list of MowingJobViewModel objects
IEnumerable<MowingJobViewModel> mjList = new List<MowingJobViewModel>();
//iterate through the list of clients built from earlier (in model)...
foreach (var item in model)
{
//create new MowingJobViewModel object MJ and add it to the list
mjList.Add(new MowingJobViewModel()
{
Client_ID = item.Client_ID,
Completed = false,
Name = (item.FirstName + " " + item.LastName),
Street = item.Address1,
City = item.City,
SvcPriceOverride = item.MowPrice,
UofM = "Lump Sum",
SvcQty = 1,
Notes = "",
Date = "",
Crew = "",
TechCount = 2,
SvcRoute = item.SvcRoute,
Schedule = item.MowSched,
});
}
return View(mjList);
}
**I don't have my view ("CreateMowing.cshtml") worked out correctly, but here is what I have-**
#model IEnumerable<LawnTracker.Models.MowingJobViewModel>
#{
ViewBag.Title = "Enter Mowing Jobs";
}
<h2>Enter Mowing Jobs</h2>
<div style="float: left; clear:both; width: 100%;">
<b>Total Jobs: #Html.Encode(Model.Count())</b><br />
<b>Total Revenue: $#Html.Encode(ViewBag.total)</b><br /><br />
</div>
<p></p>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<div style="float: right; clear:both; width: 100%;">
#using (Html.BeginForm("CreateMowing", "Job", FormMethod.Get))
{
<table>
<tr>
<th></th>
<th>
Route
</th>
<th>Schedule</th>
<th></th>
</tr>
<tr>
<td>
Show:
</td>
<td>
#Html.DropDownList("route", new SelectList(ViewBag.Routes), "--ALL--")
</td>
<td>
#Html.DropDownList("sched", new SelectList(ViewBag.Sched), "--ALL--")
</td>
<td>
<input type="submit" value="Filter" />
</td>
</tr>
</table><br /><br />
}
</div>
<table>
<tr>
<th>
Completed
</th>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Street
</th>
<th>
City
</th>
<th>
Service Route
</th>
<th>
Price
</th>
<th>
Units
</th>
<th>
Qty
</th>
<th>
Notes
</th>
<th>
Schedule
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
<input type="checkbox" name="invoiced" value="#item.Client_ID" >
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address1)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.SvcRoute)
</td>
<td>
#Html.DisplayFor(modelItem => item.MowPrice)
</td>
<td>
</td>
<td>
</td>
<td>
</td>
<td>
#Html.DisplayFor(modelItem => item.MowSched)
</td>
</tr>
}
</table>
<div>
<br />
#Html.ActionLink("Back to List", "Index")
</div>
You've created a view-model that contains the display attributes / data entry fields for a particular client when creating a job. Since you didn't give it a name, I'll call it NewJobEntry. As you guessed, you now need a view-model that has a property of type List<NewJobEntry> to represent a variable number of new jobs that can be created at once.
Your controller will populate the list, adding a NewJobEntry instance for each client.
Your view, when provided the model containing the list, can enumerate the list and generate a row for each NewJobEntry.
Once the data is posted back to your controller, the default ASP.NET MVC binder can handle lists but there are some quirks. The default binder will automatically hydrate objects in a list so long as the field names have sequential indices. To ensure these indices are correctly generated, you will want to bind using lambda expressions in your view that have a sequential index:
Html.CheckboxFor(model => model.JobList[i].IsSelected)
You might consider using a for loop instead of a foreach loop when enumerating your list.
You also have to be careful with checkboxes, because an unchecked checkbox does not actually appear in the post data sent to the controller. This will break binding, because the indices sent in the post data may no longer be sequential (there will be gaps wherever there are unchecked checkboxes). Therefore, I would recommend you include a hidden field for the client Id. This way, the post data will always contain at least one field with the appropriate sequential index for each list entry.
Html.HiddenFor(model => model.JobList[i].ClientId)
Now your post data will be something along the lines of:
JobList_0_IsSelected=true
JobList_0_ClientId=12345
JobList_1_ClientId=12346
JobList_2_IsSelected=true
JobList_2_ClientId=12347
The default binder will handle this and rebuild your list. Note that without the hidden field for client Id, index 1 would be missing, and the default binder would not correctly rebuild the list.
Hope that helps!