One-To-Many relationship between ApplicationUser and an other object - asp.net-mvc

I am struggling trying to implement à create action and an index for my controller.
Basically, I want each user to have multiple pizzas.
I want the connected user to create his own pizzas.
And in the index of my controller I want to show, only the pizzas created by the current connected user.
Here are my models :
1/Pizzas :
public class PizzaModel
{
[Key]
public int PizzaID { get; set; }
[Display(Name = "Nom")]
public string nom { get; set; }
[Display(Name = "Prix(€)")]
public float prix { get; set; }
[Display(Name = "Végétarienne")]
public bool vegetarienne { get; set; }
[Display(Name = "Ingrédients")]
public string ingredients { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
}
2/ ApplicationUser :
public class ApplicationUser : IdentityUser
{
public ICollection<PizzaModel> Pizzas { get; set; }
}
3/ This is my Context :
public class AuthDbContext : IdentityDbContext<ApplicationUser>
{
public AuthDbContext(DbContextOptions<AuthDbContext> options) : base(options)
{
}
public DbSet<PizzaModel> Pizzas { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ApplicationUser>()
.HasMany(p => p.Pizzas)
.WithOne(u => u.ApplicationUser)
.IsRequired()
.HasForeignKey(p => p.ApplicationUserId);
base.OnModelCreating(builder);
}
I want to create a "create action" and an "index action" that shows only the pizzas created by the current connected user. Here is what I have done so far :
1/ Index action method :
public async Task<IActionResult> Index(string searchByName)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
IEnumerable<PizzaModel> pizzas = new List<PizzaModel>();
pizzas = _context.Pizzas.Where(x => x.ApplicationUserId == userId);
return View(pizzas);
}
2/ Create Action Method :
public async Task<IActionResult> Create(PizzaModel model)
{
_context.ApplicationUsers.Add(model);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index), "Pizza");
}
Could you please help me with these 2 actions (Create and Index) ?

According to your Model and DbContext, I create the actions as below: I'm using the Home Controller and Project name is "WebApplication3"
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ApplicationDbContext _dbContext;
public HomeController(ILogger<HomeController> logger, ApplicationDbContext dbContext)
{
_logger = logger;
_dbContext = dbContext;
}
public IActionResult Index()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
IEnumerable<PizzaModel> pizzas = new List<PizzaModel>();
pizzas = _dbContext.Pizzas.Where(x => x.ApplicationUserId == userId);
return View(pizzas);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(PizzaModel model)
{
//Note: if you check the ModelState.IsValid, it will return false, because there is no ApplicationID and PizzaID,
//you can create a view model to enter the new value, then, convert it to PizzaModel
//validate the model
//if (ModelState.IsValid)
//{
//get current user id
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId != null)
{
//based on the userid to find current user and get its pizzas.
var currentuser = _dbContext.ApplicationUsers.Include(c => c.Pizzas).First(c => c.Id == userId);
List<PizzaModel> pizzas = new List<PizzaModel>();
pizzas = currentuser.Pizzas.ToList();
//add the new item to pizza list
pizzas.Add(new PizzaModel()
{
nom = model.nom,
prix = model.prix,
vegetarienne = model.vegetarienne,
ingredients = model.ingredients
});
//update the pizzas for current user.
currentuser.Pizzas = pizzas;
await _dbContext.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
//}
//else
//{
// return View();
//}
}
The Index view as below:
#model IEnumerable<WebApplication3.Data.PizzaModel>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.PizzaID)
</th>
<th>
#Html.DisplayNameFor(model => model.nom)
</th>
<th>
#Html.DisplayNameFor(model => model.prix)
</th>
<th>
#Html.DisplayNameFor(model => model.vegetarienne)
</th>
<th>
#Html.DisplayNameFor(model => model.ingredients)
</th>
<th>
#Html.DisplayNameFor(model => model.ApplicationUserId)
</th>
<th></th>
</tr>
</thead>
<tbody>
#if(Model.ToList().Count > 0)
{
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PizzaID)
</td>
<td>
#Html.DisplayFor(modelItem => item.nom)
</td>
<td>
#Html.DisplayFor(modelItem => item.prix)
</td>
<td>
#Html.DisplayFor(modelItem => item.vegetarienne)
</td>
<td>
#Html.DisplayFor(modelItem => item.ingredients)
</td>
<td>
#Html.DisplayFor(modelItem => item.ApplicationUserId)
</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>
}
}
else
{
<tr><td colspan="7">Empty</td></tr>
}
</tbody>
</table>
<p>
<a asp-action="Create">Create New Pizza</a>
</p>
The Create View:
#model WebApplication3.Data.PizzaModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>PizzaModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="nom" class="control-label"></label>
<input asp-for="nom" class="form-control" />
<span asp-validation-for="nom" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="prix" class="control-label"></label>
<input asp-for="prix" class="form-control" />
<span asp-validation-for="prix" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="vegetarienne" /> #Html.DisplayNameFor(model => model.vegetarienne)
</label>
</div>
<div class="form-group">
<label asp-for="ingredients" class="control-label"></label>
<input asp-for="ingredients" class="form-control" />
<span asp-validation-for="ingredients" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
The result as below:
Generally, in the HttpPost method such as the Create or Update action method, we need to validte the model is valid or not, then based on the result to show validation message or go the next steps. You can refer the following tutorials:
Model validation in ASP.NET Core MVC and Razor Pages
Tutorial: Implement CRUD Functionality - ASP.NET MVC with EF Core

Related

MVC AD search - Displaying multiple results as .cshtml

I have fully working code that searches active directory and displays it with MVC .cshtml But I have been trying to figure out away to add all the users found to a list then display them. As currently it just displays the first user found.
This is the HomeController that takes a value, Searches AD and returns the results.
public class HomeController : Controller
{
public ActionResult Index(IndexViewModel profile)
{
if (ModelState.IsValid)
{
//List<Principal> users = new List<Principal>();
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.DisplayName = profile.Name + "*";
using (PrincipalSearcher srch = new PrincipalSearcher(qbeUser))
{
if(!(srch.FindAll().Count() < 0))
{
foreach(var found in srch.FindAll())
{
//users.Add(found);
IndexViewModel returnmodel = new IndexViewModel(found);
return View(returnmodel);
}
}
}
}
}
return View(profile);
}
}
The IndexViewModel
public class IndexViewModel
{
public IndexViewModel(Principal found)
{
Name = found.DisplayName;
Email = found.UserPrincipalName;
Description = found.Description;
}
[Required(ErrorMessage = "Please enter a name")]
[Display(Name = "Persons Name")]
public string Name { get; set; }
public string Email { get; set; }
public string Description { get; set; }
//public List<Principal> user { get; set; }
}
Index.cshtml
<div id="content">
#Html.ValidationSummary(true)
#using (Html.BeginForm("Index", "Home"))
{
<fieldset>
<div class="form-group col-md-12">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(modelItem => Model.Name, new { htmlAttributes = new { #class = "form-control", #style = "width:280px" }, })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div class="col-md-2">
<input type="submit" class="btn btn-default" value="Search">
</div>
<div class="col-md-3">
</div>
</div>
</fieldset>
}
<br>
</div>
<table id="historyTable" class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>#Model.Name</td>
<td>#Model.Email</td>
<td>#Model.Description</td>
</tr>
</tbody>
</table>
EDIT-----------
This is one method I tried ----------------
HomeController.cs
public class HomeController : Controller
{
public ActionResult Index(IndexViewModel profile)
{
if (ModelState.IsValid)
{
List<Principal> users = new List<Principal>();
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.DisplayName = profile.Name + "*";
using (PrincipalSearcher srch = new PrincipalSearcher(qbeUser))
{
if(!(srch.FindAll().Count() < 0))
{
foreach(var found in srch.FindAll())
{
users.Add(found);
IndexViewModel returnmodel = new IndexViewModel(users);
return View(returnmodel);
}
}
}
}
}
return View(profile);
}
IndexViewModel.cs
public class IndexViewModel
{
public IndexViewModel(List<Principal> found)
{
user = found;
}
[Required(ErrorMessage = "Please enter a name")]
[Display(Name = "Persons Name")]
public string Name { get; set; }
public string Email { get; set; }
public string Description { get; set; }
public List<Principal> user { get; set; }
}
index.html
<div id="content">
#Html.ValidationSummary(true)
#using (Html.BeginForm("Index", "Home"))
{
<fieldset>
<div class="form-group col-md-12">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(modelItem => Model.Name, new { htmlAttributes = new { #class = "form-control", #style = "width:280px" }, })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div class="col-md-2">
<input type="submit" class="btn btn-default" value="Search">
</div>
<div class="col-md-3">
</div>
</div>
</fieldset>
}
<br>
</div>
<table id="historyTable" class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#using System.DirectoryServices.AccountManagement
#foreach (Principal prin in Model.user)
{
<tr>
<td>#prin.DisplayName</td>
<td>#prin.UserPrincipalName</td>
<td>#prin.Description</td>
</tr>
}
</tbody>
</table>
The error I get on compile is --
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 37: <tbody>
Line 38: #using System.DirectoryServices.AccountManagement
Line 39: #foreach (Principal prin in Model.user)
Line 40: {
Line 41: <tr>
Source File: C:\Users\hga\Documents\Visual Studio 2015\Projects\Intra AD people searcher\Intra AD people searcher\Views\Home\Index.cshtml Line: 39
You can add if statement to check for null
#if(Model.user !=null)
{
#foreach (Principal prin in Model.user)
{
<!--your code here-->
}
}
In your controller, your return statement is inside your foreach loop. So the first time it goes through the loop, it will return. That means you will only have one result.
Try this:
foreach(var found in srch.FindAll())
{
users.Add(found);
}
IndexViewModel returnmodel = new IndexViewModel(users);
return View(returnmodel);

Post Multiple Data from View to Controller MVC

I want to post quantity property to Controller (It's an edit action). I'm editing OrderedProductSet which is connected with ProductSet in my SQL Database (I get the name and price from there). How to pass multiple data from the view to controller? How to write method in controller class to receive the data (I'm asking about method arguments in this specific case).
My view:
#model Shop.Models.ProductViewModel#{
ViewBag.Title = "Edycja zamówienia";
}
<h2>Edycja zamówienie</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
<b>Nazwa produktu</b>
</th>
<th>
<b>Cena</b>
</th>
<th>
<b>Ilość</b>
</th>
<th></th>
</tr>
#foreach (var item in Model.orderedProductSet)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.price)
</td>
<td>
#Html.EditorFor(model => item.quantity, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Potwierdź zmiany" class="btn btn-default" />
</div>
</div>
}
<div>
#Html.ActionLink("Powrót", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My model (in separated classes of course):
public class ProductViewModel
{
public OrderSet orderSet { get; set; }
public IEnumerable<OrderedProductSet> orderedProduktSet { get; set; }
}
public partial class OrderedProduktSet
{
public int orderNumber{ get; set; }
public int productNumber { get; set; }
public int ilosc { get; set; }
public virtual ProduktSet ProduktSet { get; set; }
public virtual OrderSet OrderSet { get; set; }
}
You need to construct controls for you collection in a for loop or use a custum EditorTemplate for OrderedProduktSet so that the controls are correctly named with indexers and can be bound on post back. Note the for loop approach required that the collection be IList.
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
for(int i = 0; i < Model.orderedProductSet.Count; i++)
{
#Html.DisplayFor(m => m.orderedProductSet[i].ProduktSet.name)
....
#Html.EditorFor(m => m.orderedProductSet[i].quantity, new { htmlAttributes = new { #class = "form-control" } })
}
<input type="submit" />
}
Controller (the model will be bound, including the collection of OrderedProductSet)
public ActionResult Edit(ProductViewModel model)
{
....
}
Alternatively, you can create an EditorTemplate
/Views/Shared/EditorTemplates/OrderedProduktSet.cshtml
#model OrderedProduktSet
#Html.DisplayFor(m => m.ProduktSet.name)
#Html.EditorFor(m => m.quantity, new { htmlAttributes = new { #class = "form-control" } })
and in the main view
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
#Html.EditorFor(m => m.orderedProductSet)
<input type="submit" />
}
Viewbag is your friend here. You normally pass data from View to Controller in MVC. You can access data set in a Viewbag in the controller in your View.
The simplest way to let your controller handle your view is to create an actionresult method in your controller with the same name as your view.
For example, your view is called Index, thus you would have the following method in your controller to handle the view data:
public ActionResult Index()
{
return View();
}
Accessing a list:
Use a Viewbag.
Controller
Viewbag.MyList = myList
View
#foreach (var item in Viewbag.MyList)
Here is good link for more info:
http://www.asp.net/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-a-view

Passing back child entity from MVC view to controller

I'm trying to delete entries which are marked (checked) in view, but not sure how to pass back the collection back to the controller
my mode is:
Group which has ICollection<SubGroup> SubGroups and SubGroup has ICollection<Event> Events
I pass Group to the view and iterate and display Event details including a checkbox so if it's checked the event entry should be deleted.
When I get the postback to the controller, Group.SubGroups is null
How do I make sure the child entities are passed back to the controller?
Can I use #Html.CheckBox instead Of <input type="checkbox"... ?
Update: Model
public class Group
{
[Key]
public int GroupId { get; set; }
public virtual IList<SubGroup> SubGroups { get; set; }
....
}
public class SubGroup
{
[Key]
public int SubGroupId { get; set; }
public virtual IList<Event> Events { get; set; }
....
}
public class Events
{
[Key]
public int EventId { get; set; }
public string EventName { get; set; }
public bool IsDeleted { get; set; }
....
}
I am passing Group to the view (see below) as the Model and want to delete events which are checked by the user
View:
#using System.Globalization
#model NS.Models.Group
#{
ViewBag.Title = "Edit";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Booking Details</legend>
<div class="display-label">
Group Name
</div>
<div class="display-field">
#Html.DisplayFor(model => model.GroupName)
</div>
<div class="display-field">
#foreach (var b in Model.SubGroup)
{
groupNo += 1;
<table class="main" style="width: 80%; margin-top: 10px">
<tr>
<th>
#Html.DisplayName("Sub Group ")
#Html.DisplayName(b.SubGroupName)
</th>
</tr>
<table class="main" style="width: 80%;">
<tr>
<th>Event</th>
<th>Delete</th>
</tr>
#foreach (var ev in b.Events)
{
<tr>
<td>
#Html.DisplayFor(modelItem => ev.EventName)
</td>
<td>
<input type="checkbox" id="eventToDelete" name="eventToDelete" value="#ev.EventId" />
</td>
</tr>
}
</table>
</table>
}
</div>
<p>
<input type="submit" name="xc" value="Delete" class="button" />
</p>
</fieldset>
}
Thank You
Try this...
public ActionResult ViewName(FormCollection collection)
{
if(collection['eventToDelete']!=null && collection['eventToDelete'].ToString()!="")
{
//delete....
}
return....
}
Try this
public ActionResult ViewName(Group model)
{
if(model != null && ModelState.IsValid)
{
//delete....
}
return....
}

error to get value from controller

i try to create new room, but roomTypeID always return 1, whats wrong with my code?
i can make a new room type, but i cant insert room facility in my database, because RoomType ID always return 1
this my code..
my controller
public ActionResult NewRoom()
{
ViewBag.hotel = _hotelService.GetByID(_HotelID).HotelName;
List<ShowEditRoomViewModel> showEditRoomViewModel = _roomTypeService.showNewRooms();
return View(showEditRoomViewModel.FirstOrDefault());
}
[HttpPost]
public ActionResult NewRoom(FormCollection typeRoom)
{
_roomTypeService.NewRoom(_HotelID, typeRoom["RoomTypeName"], typeRoom["RoomTypeDescription"]);
List<string> IDs = typeRoom["FacilityIDs"].Split(',').ToList();
List<int> FacilityIDs = new List<int>();
foreach (string ID in IDs)
{
FacilityIDs.Add(Convert.ToInt32(ID));
}
_roomTypeService.UpdateFacilityInRooms(FacilityIDs, Convert.ToInt32(typeRoom["RoomTypeID"]));
return NewRoom();
}
my service
public void UpdateFacilityInRooms(List<int> FacilityIDs, int RoomTypeID)
{
List<HotelRoomFacility> hotelRoomFacilities = _HotelRoomFacilityRopository.AsQueryable().Where(f => f.RoomTypeID == RoomTypeID).ToList();
foreach (int newRoomFacility in FacilityIDs)
{
if (hotelRoomFacilities.Where(h => h.RoomFacilityID == newRoomFacility).Count() == 0)
{
HotelRoomFacility facility = new HotelRoomFacility
{
RoomFacilityID = newRoomFacility,
RoomTypeID = RoomTypeID
};
_HotelRoomFacilityRopository.Add(facility);
}
}
_HotelRoomFacilityRopository.CommitChanges();
}
my view model
public class ShowEditRoomViewModel
{
public int RoomTypeID { get; set; }
public string RoomTypeName { get; set; }
public string RoomTypeDescription { get; set; }
public List<FaciliyInRoom> facilityinRoom { get; set; }
}
my view
#model XNet.Repository.Model.ShowEditRoomViewModel
#{
ViewBag.Title = "NewRoom";
}
<h2>New Room</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Isikan Data</legend>
<div>
#Html.Label("Hotel Name")
</div>
<div>
#ViewBag.hotel
</div>
<br />
<div>
#Html.HiddenFor(model => model.RoomTypeID)
</div>
<br />
<div>
#Html.Label("Room Type Name")
</div>
<div>
#Html.EditorFor(model => model.RoomTypeName)
#Html.ValidationMessageFor(model => model.RoomTypeName)
</div>
<br />
<div>
#Html.Label("Room Type Description")
</div>
<div>
#Html.TextAreaFor(model => model.RoomTypeDescription)
#Html.ValidationMessageFor(model => model.RoomTypeDescription)
</div>
<br />
<table>
<thead>
<tr>
<th>Facility Name</th>
<th> is available</th>
</tr>
</thead>
<tbody>
#foreach (var facility in Model.facilitiesInRoom)
{
<tr>
<td>
#(facility.RoomFacilityName)
</td>
<td style="text-align:center;">
<input type="checkbox" #(facility.RoomFacilityAvailable ? " checked=checked" : null) name="FacilityIDs" value="#facility.RoomFacilityID" />
</td>
</tr>
}
</tbody>
</table>
<br />
<p>
<input type="submit" value="Save" />
<input style="width:100px;" type="button" title="EditHotelDetail" value="Back to Detail" onclick="location.href='#Url.Action("Room", "Hotel") '" />
</p>
</fieldset>
}
My method
public List<ShowEditRoomViewModel> showNewRooms()
{
List<RoomType> roomTypes = (from d in _RoomTypeRepository.All()
select d).ToList();
List<ShowEditRoomViewModel> showEditRoomViewModel = new List<ShowEditRoomViewModel>();
foreach (RoomType roomType in roomTypes)
{
showEditRoomViewModel.Add(new ShowEditRoomViewModel
{
RoomTypeID = roomType.RoomTypeID,
facilitiesInRoom = LoadFacilityInRoom()
});
}
return showEditRoomViewModel;
}
can someone tell me, where is my mistake??
thanks
When you are inserting RoomtypeId in Database, you are using ExecuteNonQuery() method, It will always return 1 whenever you insert a new record in it,
If you are using stored procedure for inserting,you can use
select Scope_identity()
after insertion.

Object reference not set to an instance of an object. Custom object returning null when POST

I'm trying to create a list of objects in my view, but in the post method, the parameter coming in is null. The create page loads fine with all the information I want, but when I click "Create" I get the error listed in my title. What can I be doing wrong?
Employees have an availability linked to them for each day. That's what i am trying to achieve
Model
public class Availability
{
[Required]
public long Id { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Weekday")]
public string weekday { get; set; }
[DataType(DataType.Time)]
[Display(Name = "Start Time")]
public DateTime StartTime { get; set; }
[DataType(DataType.Time)]
[Display(Name = "End Time")]
public DateTime EndTime { get; set; }
public virtual Employee employee { get; set; }
}
}
Custom Class
public class SetAvailability
{
public long EmpID { get; set; }
public String firstName {get; set;}
public String lastName { get; set; }
public Availability availability {get; set;}
}
Controller methods
// GET: /Availability/Create
public ActionResult Create(long id)
{
string[] weekdays ={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
//find the employee
Employee emp = db.Employees.Find(id);
//create a list of setAvailability objects for each day
List<SetAvailability> editor = new List<SetAvailability>();
//instantiate each SetAvailability object and populate accordingly for 7 days
for (int i = 0; i < 7; i++)
{
//create a blank SetAvilability
var _editor = new SetAvailability();
//create a blank availability
_editor.availability = new Availability();
//set the weekday
_editor.availability.weekday = weekdays[i].ToString();
//set the employee id, first name and last name
_editor.EmpID = emp.Id;
_editor.firstName = emp.FirstName;
_editor.lastName = emp.LastName;
//add the _editor to the editorlist
editor.Add(_editor);
}
return View(editor);
}
//
// POST: /Availability/Create
[HttpPost]
public ActionResult Create(List<SetAvailability> dto)
{
//dto object is coming in null! (checked by debugging)
List<SetAvailability> temp = new List<SetAvailability>();
temp = dto;
if (ModelState.IsValid)
{
// set the values for each availability object
// breaks here!
foreach (var item in dto)
{
// get the employee
item.availability.employee = db.Employees.Find(item.EmpID);
// weekday should already be set
// start and end times should come in from the create view
db.Availability.Add(item.availability);
}
db.SaveChanges();
return RedirectToAction("Index","employee");
}
return View(temp);
}
View
#model List<VolumeV2.Models.DTOs.SetAvailability>
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Availability</legend>
<table>
<tr>
#foreach (var item in Model)
{
#Html.HiddenFor(model => item.EmpID)
#Html.HiddenFor(model => item.firstName)
#Html.HiddenFor(model => item.lastName)
<td>
#Html.LabelFor(model => item.availability.weekday)
</td>
<td>
#Html.LabelFor(model => item.availability.StartTime)
</td>
<td>
#Html.LabelFor(model => item.availability.EndTime)
</td>
break;
}
</tr>
#foreach (var item in Model){
#Html.HiddenFor(model => item.EmpID)
#Html.HiddenFor(model => item.firstName)
#Html.HiddenFor(model => item.lastName)
<tr>
<td>
#Html.EditorFor(modelitem => item.availability.weekday)
#Html.ValidationMessageFor(modelitem => item.availability.weekday)
</td>
<td>
#Html.EditorFor(modelitem => item.availability.StartTime)
#Html.ValidationMessageFor(modelitem => item.availability.StartTime)
</td>
<td>
#Html.EditorFor(modelitem => item.availability.EndTime)
#Html.ValidationMessageFor(modelitem => item.availability.EndTime)
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
To fix this up quickly, you may use the indexing on the list items using a "for" loop since that will render the name of the controls to suite the naming convention for MVC model binding. For e.g, [0].availability.weekday, [1].availability.weekday etc., that represent an array of items with corresponding index to be able to construct a list of items while Model Binding.
#model List<MvcApplication5.Models.SetAvailability>
#using (Html.BeginForm("Create","Employee")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Availability</legend>
<table>
<tr>
#for (int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(model => Model[i].EmpID)
#Html.HiddenFor(model => Model[i].firstName)
#Html.HiddenFor(model => Model[i].lastName)
<td>
#Html.LabelFor(model => Model[i].availability.weekday)
</td>
<td>
#Html.LabelFor(model => Model[i].availability.StartTime)
</td>
<td>
#Html.LabelFor(model => Model[i].availability.EndTime)
</td>
break;
}
</tr>
#for (int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(model => Model[i].EmpID)
#Html.HiddenFor(model => Model[i].firstName)
#Html.HiddenFor(model => Model[i].lastName)
<tr>
<td>
#Html.EditorFor(modelitem => Model[i].availability.weekday)
#Html.ValidationMessageFor(modelitem => Model[i].availability.weekday)
</td>
<td>
#Html.EditorFor(modelitem => Model[i].availability.StartTime)
#Html.ValidationMessageFor(modelitem => Model[i].availability.StartTime)
</td>
<td>
#Html.EditorFor(modelitem => Model[i].availability.EndTime)
#Html.ValidationMessageFor(modelitem => Model[i].availability.EndTime)
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
And now, you should see seven items in the list after the POST. Hope this help you.
Try this
To get the collection of object at POST methods it need to modified Html.BeginForm,
add following Helper class in your application which will return you List of objects.
(e.g location of Helper class DemoApplication/Models/)
public static class HtmlPrefixScopeExtensions
{
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
{
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));
return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
}
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
{
// We need to use the same sequence of IDs following a server-side validation failure,
// otherwise the framework won't render the validation error messages next to each item.
string key = idsToReuseKey + collectionName;
var queue = (Queue<string>)httpContext.Items[key];
if (queue == null) {
httpContext.Items[key] = queue = new Queue<string>();
var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
if (!string.IsNullOrEmpty(previouslyUsedIds))
foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
queue.Enqueue(previouslyUsedId);
}
return queue;
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
after that modified you view
#model List<VolumeV2.Models.DTOs.SetAvailability>
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#Html.ValidationSummary(true)
#using(Html.BeginForm())
{
<fieldset>
<legend>Availability</legend>
<table>
#foreach (var item in Model)
{
#Html.Partial("_partialView",item)
}
</table>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
create a partial view
_partailView.cshtml
#model VolumeV2.Models.DTOs.SetAvailability
#using DemoApplication.Models // Added Helper class reference
<tr>
#using (Html.BeginCollectionItem("dto")) {
#Html.HiddenFor(model => model.EmpID)
#Html.HiddenFor(model => model.firstName)
#Html.HiddenFor(model => model.lastName)
<td>
#Html.LabelFor(model => model.availability.weekday)
</td>
<td>
#Html.LabelFor(model => model.availability.StartTime)
</td>
<td>
#Html.LabelFor(model => model.availability.EndTime)
</td>
break;
}
</tr>
NOTE: you must keep same name of Html.BeginCollectionItem("dto") and Parameter name in POST method otherwise you will not get value in POST method and also add Helper class reference in your view.

Resources