How to pass complex object collections to the controller in mvc - c#

I'm new to MVC and sorry for this beginners question. I have following Model classes:
public class ReturnBookHedModel
{
public int RefferenceID { get; set; }
public int BorrowedRefNo { get; set; }
public int MemberId { get; set; }
public DateTime ReturnDate { get; set; }
public bool IsNeedToPayFine { get; set; }
public DateTime CurrentDate { get; set; }
public virtual List<ReturnBookDetModel> RetunBooks { get; set; }
public virtual MemberModel member { get; set; }
}
public class ReturnBookDetModel
{
public int BookID { get; set; }
public int RefferenceID { get; set; }
public bool IsReturned { get; set; }
public virtual ReturnBookHedModel ReturnBookHed { get; set; }
public virtual BookModel book { get; set; }
}
I have following controller methods:
public ActionResult SaveReturnBook(int refNo)
{
ReturnBookHedModel model = ReturnBookFacade.GetReturnBookBasedOnRefference(refNo);
return View(model);
}
//
// POST: /ReturnBook/Create
[HttpPost]
public ActionResult SaveReturnBook(ReturnBookHedModel model)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
in my model i define as follows:
<div class="control-label">
#Html.LabelFor(model => model.BorrowedRefNo)
#Html.TextBoxFor(model => model.BorrowedRefNo, new { #class = "form-control" ,#readonly = "readonly" })
#Html.ValidationMessageFor(model => model.BorrowedRefNo)
</div>
// rest of the header details are here
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.RetunBooks.FirstOrDefault().IsReturned)
</th>
<th>
#Html.DisplayNameFor(model => model.RetunBooks.FirstOrDefault().BookID)
</th>
<th>
#Html.DisplayNameFor(model => model.RetunBooks.FirstOrDefault().book.BookName)
</th>
<th></th>
</tr>
#foreach (var item in Model.RetunBooks)
{
<tr >
<td>
#Html.CheckBoxFor(modelItem => item.IsReturned)
</td>
<td>
#Html.HiddenFor(modelItem => item.BookID);
#Html.DisplayFor(modelItem => item.BookID)
</td>
<td>
#Html.DisplayFor(modelItem => item.book.BookName)
</td>
</tr>
}
</table>
this is working fine.. but these table details (complex objects) are not in the controller's post method. when i searched i found that i can use this detail data as follows: but i cant use it as follows.
#for (var i = 0; i < Model.RetunBooks.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(x => x.RetunBooks.)
</td>
</tr>
}
how can i send these information to controller

In order for the collection to be posted back you need to index them in the following way for the model binder to pick them up.
This should do the trick:
#for (var i = 0; i < Model.RetunBooks.Count; i++)
{
...
#Html.CheckBoxFor(model => Model.RetunBooks[i].IsReturned)
...
}
Complex objects require the indexing in the above manner.
For more info on it see here:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

Related

What properties do i have to give my controller index method?

I am building a project for school. I am almost there but there is one error I can't fix.
I am making a bike reservation asp.net website.
The error says I pass a list to the index page of reservations and it expects an IEnumerable.
I already tried making the expected a list, but that also gives an error.
I have seen other fixes of this problem, but I can't get it to work.
Here is my original index method.
// GET: Reservations
public ActionResult Index()
{
var reservations = db.Reservations.Include(r => r.Bike).Include(r => r.Customer).Include(r => r.DropoffStore).Include(r => r.PickupStore);
return View(reservations.ToList());
}
Here is my new Controller index method that i got from someone else, but i dont know why i wont work:
// GET: Reservations
public ActionResult Index()
{
var reservations = db.Reservations.Include(r => r.Bike).Include(r => r.Customer).Include(r => r.DropoffStore).Include(r => r.PickupStore);
IEnumerable<ReservationViewModel> reservationViewModels = new List<ReservationViewModel>();
foreach (var reservation in reservations)
{
var reservationViewModel = new ReservationViewModel
{
(PROPERTY)
};
reservationViewModels.ToList().Add(reservationViewModel);
}
return View(reservationViewModels);
}
Here is my index page:
#model IEnumerable<ASP.NET_Framwork_for_real_bitch.ViewModels.ReservationViewModel>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Reservation.Customer.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.Customer.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.Customer.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.Customer.Gender)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.DropoffStore_Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.PickupStore_Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.StartDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Reservation.EndDate)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Reservation.Customer.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.Customer.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.Customer.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.Customer.Gender)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.DropoffStore.StoreName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.PickupStore.StoreName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.StartDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Reservation.EndDate)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Reservation.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Reservation.Id })
</td>
</tr>
}
</table>
This is the exact error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[ASP.NET_Framwork_for_real_bitch.Models.Reservation]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[ASP.NET_Framwork_for_real_bitch.ViewModels.ReservationViewModel]'.
my Whole ReservationViewModel:
using ASP.NET_Framwork_for_real_bitch.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ASP.NET_Framwork_for_real_bitch.ViewModels
{
public class ReservationViewModel
{
private BikeShoppaModel db = new BikeShoppaModel();
public Reservation Reservation { get; set; }
public SelectList DropoffStore_Id { get; private set; }
public SelectList PickupStore_Id { get; private set; }
public SelectList Bikes { get; private set; }
public SelectList CustomerGender { get; set; }
public int TotalDays { get; set; }
public double TotalPrice { get; set; }
public ReservationViewModel()
{
DropoffStore_Id = new SelectList(db.Stores, "Id", "StoreName");
PickupStore_Id = new SelectList(db.Stores, "Id", "StoreName");
Bikes = new SelectList(db.Bikes, "Id", "Brand");
CustomerGender = new SelectList(db.Customers, "Id", "Gender");
}
public ReservationViewModel(int id) : this()
{
Reservation = db.Reservations.Find(id);
TotalDays = (Reservation.EndDate.Date - Reservation.StartDate.Date).Days + 1;
}
public void Save()
{
if(Reservation.Id > 0)
{
db.Entry(Reservation).State = EntityState.Modified;
}
else
{
db.Reservations.Add(Reservation);
}
db.SaveChanges();
}
}
}
my Reservations model:
public class Reservation
{
public int Id { get; set; }
[ForeignKey("Customer")]
[Display(Name = "Customer")]
public int Customer_Id { get; set; }
public virtual Customer Customer { get; set; }
[ForeignKey("Bike")]
public int Bike_Id { get; set; }
public Bike Bike { get; set; }
[DataType(DataType.Date)]
[Column(TypeName = "Date")]
[Display(Name = "Start date")]
public DateTime StartDate { get; set; }
[DataType(DataType.Date)]
[Column(TypeName = "Date")]
[Display(Name = "End date")]
public DateTime EndDate { get; set; }
[ForeignKey("PickupStore")]
[Display(Name = "Pickup store")]
public int PickupStore_Id { get; set; }
public Store PickupStore { get; set; }
[ForeignKey("DropoffStore")]
[Display(Name = "Dropoff store")]
public int DropoffStore_Id { get; set; }
public Store DropoffStore { get; set; }
}

How can I post changes to a list of players to database?

I want to post data to database but my viewmodel is empty when posting it to the controller, i tried different approaches, but everytime my viewmodel is null.
These are my classes :
public class Player
{
[Key]
public int Id { get; set; }
public string Name{ get; set; }
public string PreName{ get; set; }
}
public class Activitity
{
[Key]
public int Id { get; set; }
public string WhichActivity { get; set; }
public List<Player> Players { get; set; }
}
public class Aanwezigheid
{
[Key]
public int Id { get; set; }
public ReasonEnum Reason{ get; set; }
public int PlayerId{ get; set; }
public Player Player{ get; set; }
public List<Player> Players{ get; set; }
public int ActivityId { get; set; }
}
My View Model :
public class PresenceVM
{
public int PlayerId{ get; set; }
public int ActivityId { get; set; }
public string Name{ get; set; }
public string PreName { get; set; }
public ReasonEnum Reason { get; set; }
}
My HTTPGET for a list of players and I want to put the absence reason with the player in the database.
[HttpGet]
public ActionResult Presence(int id)
{
var sp = _context.Players.ToList();
foreach(Players s in sp)
{
var van = new PresenceVM
{
PlayerId = s.Id,
Name = s.Name,
PreName = s.PreName,
ActivityId = id
};
list.Add(van);
}
return View(list);
}
My HttpPost
[HttpPost]
public ActionResult Presence(List<PresenceVM> list)
{
var sp = _context.Players.ToList();
var list = new List<Presence>();
foreach (Players s in sp)
{
var van = new Aanwezigheid
{
PlayerId = s.Id,
ActivityId = vm.ActivityId,
Reason = vm.Reason
};
list.Add(van);
_context.Presence.Add(van);
//_context.SaveChanges();
}
return RedirectToAction("Index", "Presence");
}
The problem is that my PresenceVm (viewmodel) does not get any data in true my controller. I don't understand why? Is it because of a list? With one item it's easy to post it to database. Maybe multiple items?
Edit 1:
The viewmodel for the Get & Post
#model IEnumerable<....ViewModels.PresenceVM>
#{
ViewBag.Title = "Presence";
}
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PreName)
</th>
<th>
#Html.DisplayName("Reason")
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.PreName)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item.Reason, "Present", new { #class = "form-control" })
</td>
</tr>
}
</table>
<form action="/Presences/Presence" method="post">
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
You're putting the form scope in wrong place (only include submit button). The correct way should include all properties you want to submit with indexes (since you're using IEnumerable<PresenceVM>, like this example:
<form action="/Presences/Presence" method="post">
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PreName)
</th>
<th>
#Html.DisplayName("Reason")
</th>
</tr>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.EditorFor(modelItem => item[i].Name)
</td>
<td>
#Html.EditorFor(modelItem => item[i].PreName)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item[i].Reason, "Present", new { #class = "form-control" })
</td>
</tr>
}
</table>
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
Note that if you want to allow user input, you need to change all DisplayFor into EditorFor.
Your Presence method is returning an object that does not exist (list).
It must return the view model if you want to use it in the post method.
[HttpGet]
public ActionResult Presence(int id)
{
List<PresenceVM> model = context.Players.Select(u => new PresenceVM
{
PlayerId = s.Id,
Name = s.Name,
PreName = s.PreName,
ActivityId = id
}).ToList();
return View(model);
}
Seems that you don't use the Form tag
VIEW
#model MyViewModel
#using (Html.BeginForm(("Presence"))
{
#Html.AntiForgeryToken()
//divs
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
And it's better to pass a Model than a list of Model
VIEWMODEL
public class MyViewModel{
public IList<PresenceVM> MyList {get;set;}
}
CONTROLLER
public ActionResult Presence(MyViewModel xxx)
{
//whatever
}

ASP.NET MVC how to display associative data based on id's

I'm trying to display data on my index view from from my models that are associated with each other based on id's. I.e. display client name, asset name that belongs to this client, and address of this client, etc...
Here's my model:
Client model:
public class Client : Person {
public ICollection<OccupancyHistoryRecord> OccupancyRecords { get; set; }
public ICollection<RentHistoryRecord> RentRecords { get; set; }
}
Asset model:
public class Asset {
public int Id { get; set; }
[Display(Name = "Asset Name")]
public string Name { get; set; }
[Display(Name = "Asset Type")]
public string Type { get; set; }
public FullAddress Address { get; set; }
[Display(Name = "Asking Rent")]
public string AskingRent { get; set; }
public ICollection<OccupancyHistoryRecord> OccupancyRecords;
public ICollection<RentHistoryRecord> RentRecords;
}
Occupancy Record:
public class OccupancyHistoryRecord {
public int Id { get; set; }
public int AssetId { get; set; }
public int ClientId { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
Client Controller:
public ActionResult Index()
{
var clients = db.Clients.Include(c => c.OccupancyRecords) // how to get the asset name instead of the id)
.Include(c => c.HomeAddress)
.Include(c => c.WorkAddress);
return View(clients.ToList());
}
Index View:
#model IEnumerable<RentalManagement.Models.Client>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.OccupancyRecords)
</th>
<th>
#Html.DisplayNameFor(model => model.HomeAddress)
</th>
<th>
#Html.DisplayNameFor(model => model.WorkAddress)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.OccupancyRecords)
</td>
<td>
#Html.DisplayFor(modelItem => item.HomeAddress.StreetAddress)
</td>
<td>
#Html.DisplayFor(modelItem => item.WorkAddress.StreetAddress)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }, null) |
#Html.ActionLink("Assets", "Details", "Assets", new { id = item.Id}, null) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
Right now it's displaying the occupancy record's Id. What I want is to display the asset name based on the occupancy's AssetId.
Thanks.
You need to change your :
public ActionResult Index()
{
var clients = db.Clients.Include(c => c.OccupancyRecords) // how to get the asset name instead of the id)
.Include(c => c.HomeAddress)
.Include(c => c.WorkAddress);
return View(clients.ToList());
}
code as below:
public ActionResult Index()
{
var clients = db.Clients.Include(c => c.OccupancyRecords.Select(s => new { AssetId = s.AssetId, AssetName = /* Find AssetName By Id here */ }))
.Include(c => c.HomeAddress)
.Include(c => c.WorkAddress);
return View(clients.ToList());
}

Get and average from user input to a database?

I am totally new to programming so my question maybe confusing but I will try to explain as best as I can.
So I build a app that take in ratings between 1-5. The result is stored in the data base. But I do not understand how to put some logic in my model class where it can retrieve that number and out output the number the users input and the overall average of the rating it was given.
This is my Model class:
public class MovieReview : IValidatableObject
{
public int Id { get; set; }
[Range(1,10)]
[Required]
public double Rating { get; set; }
[Required]
[StringLength(1024)]
public string Comment { get; set; }
[Display(Name="User Name")]
[DisplayFormat(NullDisplayText="anonymous")]
//[MaxWord(5)]
public string ReviewerName { get; set; }
public int MovieId { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(Rating < 2 && ReviewerName.ToLower().StartsWith("keith"))
{
yield return new ValidationResult("Scorry, Keith uou can't do this");
}
//throw new NotImplementedException();
}
}
public partial class MovieReview : IValidatableObject
{
public ICollection<AverageRating>
public int Id { get; set; }
public int AverageRating { get _AverageRating(c => c.AverageRating; set; }
}
}
This is my controller:
namespace YayOrNay.Controllers
{
public class ReviewsController : Controller
{
YayOrNayDb _db = new YayOrNayDb();
// GET: Reviews
public ActionResult Index([Bind(Prefix = "id")]int movieId)
{
var movie = _db.Movies.Find(movieId);
if(movie !=null)
{
return View(movie);
}
return HttpNotFound();
}
[HttpGet]
public ActionResult Create (int movieId)
{
return View();
}
[HttpPost]
public ActionResult Create(MovieReview review)
{
if(ModelState.IsValid)
{
_db.Reviews.Add(review);
_db.SaveChanges();
return RedirectToAction("Index", new { id = review.MovieId });
}
return View(review);
}
[HttpGet]
public ActionResult Edit (int id)
{
var model = _db.Reviews.Find(id);
return View(model);
}
//[Bind(Exclude = "ReviewerName")]
[HttpPost]
public ActionResult Edit (MovieReview review)
{
if (ModelState.IsValid)
{
_db.Entry(review).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index", new { id = review.MovieId });
}
return View(review);
}
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}
}
And this is my view:
#model IEnumerable<YayOrNay.Models.MovieReview>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Rating)
</th>
<th>
#Html.DisplayNameFor(model => model.Comment)
</th>
<th>
#Html.DisplayNameFor(model => model.ReviewerName)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Rating)
</td>
<td>
#Html.DisplayFor(modelItem => item.Comment)
</td>
<td>
#Html.DisplayFor(modelItem => item.ReviewerName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
</td>
</tr>
}
</table>
I hope this is enough information for you to understand what I am trying to do.

Model binding generic list is null in asp.net mvc

I am binding objects in a razor foreach in the index.html:
VIEW
#using (Ajax.BeginForm("Save", "Unit", new AjaxOptions { OnSuccess = "onSuccess" }))
{
<button type="submit" class="btn btn-default" id="saveUnits"><i class="fa fa-save"></i></button>
<table>
<tbody>
#foreach (var item in Model)
{
<tr>
#Html.HiddenFor(modelItem => item.UnitId)
<td>
#Html.EditorFor(modelItem => item.Name)
</td>
<td>
#Html.EditorFor(modelItem => item.ErrorText)
</td>
</tr>
}
</tbody>
</table>
}
I have grabbed the data sent to my action parameter with fiddler and got this:
item.UnitId=5&
item.Name=111111111111&
item.ErrorText=fsdddddddddddddddd+&
item.UnitId=5&
item.Name=+&
item.ErrorText=dddddd+&
ACTION
public ActionResult Save(List<Unit> units )
{
return new EmptyResult();
}
VIEWMODEL
public class Unit
{
[HiddenInput(DisplayValue = false)]
public int UnitId { get; set; }
[DataType(DataType.MultilineText)]
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string ErrorText { get; set;
}
Why is my units instance null? The properties match so they should be bound!
Did I overlook something?
You need to use a for loop not a foreach loop. Also, it would be better to make your Model class have a property which is a collection.
Your model could be something like:
public class UnitsViewModel
{
public List<Unit> Units { get; set; }
public class Unit
{
[HiddenInput(DisplayValue = false)]
public int UnitId { get; set; }
[DataType(DataType.MultilineText)]
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string ErrorText { get; set; }
}
}
And you could do the following in your cshtml:
#for (int i = 0; i < Model.Count; i++)
{
<tr>
#Html.HiddenFor(m => m.Units[i].UnitId)
<td>
#Html.EditorFor(m => m.Units[i].Name)
</td>
<td>
#Html.EditorFor(m => m.Units[i].ErrorText)
</td>
</tr>
}

Categories