Get data from an Ilist model to a controller [closed] - c#

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Ok, please forgive if this is newbie error.. I have been looking here at various solutions but still can't get anywhere.
I need to be able to associate a User (Id) with a Company. I have linked to SelectCompInst and have passed in the user Id.
I have a general bool flag on the companyinstitution model which I set up in the get method. The flag is correctly shown in the view. I can change the fact that its a checkbox if needed.. but I just want to put a button, link that ideally causes a postback in the list in order to allow the operator to select a company to be attached to this user.. something I am sure is done all the time.
I have seen all kinds of references to this sort of problem saying I might have to use an Edit Template? but I never find it explained why? Surely it can't be that complicated can it?
Here's my model:
[Key]
public int Id { get; set; }
public DateTime RecordCreateDate { get; set; }
[ForeignKey("Address")]
public int AddressId { get; set; }
public string Name { get; set; }
[ForeignKey("PrimaryContact")]
public string PrimaryContactId { get; set; }
public virtual Address Address { get; set; }
public virtual ApplicationUser PrimaryContact { get; set; }
[NotMapped]
public bool UserFlag { get; set; } //Bool to define if user is in this activity type when searching
Here's the View:
#model IList<Dune.Models.CompanyInstitution>
#{
ViewBag.Title = "Index";
string staffid = ViewBag.StaffId;
}
<h2>Select Company/Institution for user #ViewBag.UserName</h2>
#using (Html.BeginForm("SelectCompInst","UserAdmin",FormMethod.Post, new {id ="compselect"}))
{
#Html.AntiForgeryToken()
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model[0].Name)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Address)
</th>
<th>
#Html.DisplayNameFor(model => model[0].PrimaryContact)
</th>
<th>
#Html.DisplayNameFor(model => model[0].RecordCreateDate)
</th>
<th></th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(m => m.ElementAt(i).Name)
</td>
<td>
#{
var AddressString = Model.ElementAt(i).Address.AddressAsString();
}
#Html.DisplayFor(modelItem => AddressString)
</td>
<td>
#{ var NameString = "Not Allocated";
if (Model.ElementAt(i).PrimaryContact != null)
{
NameString = Model.ElementAt(i).PrimaryContact.FullNameNoMidString();
}
}
#Html.DisplayFor(modelItem => NameString)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem.ElementAt(i).RecordCreateDate)
</td>
<td>
#Html.EditorFor(modelItem => modelItem.ElementAt(i).UserFlag)
</td>
<td>
#* Ideally I would like to put a simple 'Select' Link here and also post back the UserId
contained in Viewbag.StaffId.. I figure I can add that to the view model though
if I can get anything at all*#
</td>
</tr>
}
</table>
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Select" />
</div>
}
.. and here's the controller methods:
//
// GET:
public async Task<ActionResult> SelectCompInst(string StaffId)
{
ApplicationUser user = await UserManager.FindByIdAsync(StaffId);
ViewBag.UserName = "Error User not found!";
if (user != null)
{
ViewBag.UserName = user.FullNameString();
ViewBag.StaffId = StaffId;
}
var companysInstitutions = db.CompanysInstitutions.Include(c => c.Address).Include(c => c.PrimaryContact);
// Walk the list and set the flag if this user is already mapped to this institution.
foreach (CompanyInstitution comp in companysInstitutions)
{
if (user.CompanyInstitutionStaffMember.CompanyInstituteId == comp.Id)
{
comp.UserFlag = true;
}
else
{
comp.UserFlag = false;
}
}
IList<CompanyInstitution> companys = await companysInstitutions.ToListAsync();
return View(companys);
}
//
// POST:
[HttpPost, ActionName ("SelectCompInst")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SelectComp(IList<CompanyInstitution> Company )
{
//
// Code removed because it doesnt get this far with any data :)
//
ModelState.AddModelError("", "Something failed.");
return View();
}
I have looked at the post back using fiddler and am getting nothing at all.. although the Post function is being hit. The IList Company is null...
I am sure this boils down to a fundamental misunderstanding on my part but having struggled with it for 2 days now.. have to throw myself on the mercy of the wiser community :)
I would like to put a link against each row in the table and simply pass back an id for the row and the user id.. Thanks.
Further edit after Stephens comment..
The scenario is in the editing of a User. A user can have a CompanyInstituion Id as part of its model. I wish to present a list of companies to the operator (not necessarily the User) to allow them to attach a company id to that user. So in concept its dead simple - present a list of companies to pick from. Press a 'select' link on the company line and return to the controller to sort it out. Hence I need to preserve the UserId in the Viewbag - yes I can create a viewmodel if needed but getting the company id alone would be a start)..
So that's it.. present the list, select one and return it.. I don't really want to use checkboxes anyway.. I kind of 'got there' after trying some things. I originally had the submit button in the loop before I added the checkbox. I did try putting the Company Id on the button but that didn't work either.

If the operator must select a single company that will be associated with the user, then it would make more sense your POST action to take the selected company id and the user id. That should be enough information for you to do the job:
[HttpPost, ActionName ("SelectCompInst")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SelectComp(int companyId, string staffId)
{
...
}
and then in your view you could have multiple forms and a submit button on each row of the table:
#model IList<Dune.Models.CompanyInstitution>
#{
ViewBag.Title = "Index";
string staffid = ViewBag.StaffId;
}
<h2>Select Company/Institution for user #ViewBag.UserName</h2>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model[0].Name)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Address)
</th>
<th>
#Html.DisplayNameFor(model => model[0].PrimaryContact)
</th>
<th>
#Html.DisplayNameFor(model => model[0].RecordCreateDate)
</th>
<th></th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(m => m[i].Name)
</td>
<td>
#{
var AddressString = Model[i].Address.AddressAsString();
}
#Html.DisplayFor(modelItem => AddressString)
</td>
<td>
#{
var NameString = "Not Allocated";
if (Model[i].PrimaryContact != null)
{
NameString = Model[i].PrimaryContact.FullNameNoMidString();
}
}
#Html.DisplayFor(modelItem => NameString)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem[i].RecordCreateDate)
</td>
<td>
#Html.EditorFor(modelItem => modelItem[i].UserFlag)
</td>
<td>
#using (Html.BeginForm("SelectCompInst", "UserAdmin", new { companyId = Model[i].Id, staffId = staffid }, FormMethod.Post, new { #class = "compselect" }))
{
#Html.AntiForgeryToken()
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Select" />
</div>
}
</td>
</tr>
}
</tbody>
</table>
Basically we would have an HTML form on each row of the table which will pass the necessary information to the server:
#using (Html.BeginForm(
actionName: "SelectCompInst",
controllerName: "UserAdmin",
routeValues: new { companyId = Model[i].Id, staffId = staffid },
method: FormMethod.Post,
htmlAttributes: new { #class = "compselect" }))
{
#Html.AntiForgeryToken()
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Select" />
</div>
}

Related

ASP.NET MVC Complex Object property stays null on form submit

I'm getting myself acquainted with ASP.NET MVC but i'm running into something probably trivial. I have a model called ToDoList, this is a complex type with a list of ToDoItems:
public class ToDoList
{
public Guid Id {get;set;}
public string Name { get; set; }
public virtual ICollection<ToDoItem> Items {get;set;}
}
public class ToDoItem
{
public int Id { get; set; }
public string Task { get; set; }
public bool IsDone { get; set; }
public virtual ToDoList ToDoList { get; set; }
}
My Details page with form looks like this:
#model DataLayer.TomTest.Entities.ToDoList
<h2>#Model.Name</h2>
#using (#Html.BeginForm())
{
#Html.AntiForgeryToken()
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Items.First().Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().Task)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().IsDone)
</th>
</tr>
#foreach (var toDoItem in Model.Items)
{
<tr>
<td>
#toDoItem.Id
</td>
<td>
#Html.EditorFor(model => toDoItem.Task)
</td>
<td>
#Html.EditorFor(model => toDoItem.IsDone, new {htmlAttributes = new {#Style = "margin-left: 10px;"}})
</td>
</tr>
}
</table>
<input type="submit" value="Save" class="btn btn-default"/>
}
And this is the method it posts to:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Details([Bind(Include = "Id,Name,Items")] ToDoList todoList)
{
if (ModelState.IsValid)
{
_context.Entry(todoList).State = EntityState.Modified;
await _context.SaveChangesAsync();
return View();
}
return View();
}
As you can see I included the [Bind] attribute as I read somewhere that would ensure i get the correct properties passed. When I debug this however, only the Id property is filled the rest remains null.
What can I do to fix this? Is it a mistake in the View? Or is it possible Entity Framework isn't setup correctly?
Thanks in advance for your help.
Model binding to a list doesn't work with a foreach; you need to use a for loop instead.
You'll also need hidden inputs for any properties which don't have editors within the loop.
#for (int index = 0; index < Model.Items.Count; index++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Items[index].Id)
#Model.Items[index].Id
</td>
<td>
#Html.EditorFor(m => m.Items[index].Task)
</td>
<td>
#Html.EditorFor(m => m.Items[index].IsDone, new { htmlAttributes = new { #Style = "margin-left: 10px;" } })
</td>
</tr>
}
ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog

Can't pass an enumerable model to a controller?

I'm a bit confused because I thought this a very straight-forward thing, it's possibly something simple tripping me up.
I have a view:
#model IEnumerable<CarViewModel>
#using (Html.BeginForm("SummarySaveAll", "VroomVroom", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
#Html.DisplayNameFor(model => model.Driver)
</th>
<th width="1">
#Html.DisplayNameFor(model => model.Colour.Name)
</th>
</tr>
</thead>
<tbody>
#foreach (var element in Model)
{
<tr>
<td width="1">
#Html.DisplayFor(m => element.Driver)
</td>
<td width="1">
#Html.DropDownListFor(m => element.Colour, element.Colours, "Unknown")
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
and the list/enumerable of CarViewModel is supposed to bounce back to the VroomVroom controller, action SummarySaveAll which it does - but the viewmodel on the page doesn't get passed back to it:
[HttpPost]
public ActionResult SummarySaveAll(IEnumerable<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View();
}
I tried to encapsulate the List in another ViewModel and cycle through elements using a for i loop but that wouldn't pass back to the controller either.
Surely it's possible to send a List or IEnumerable of models back to a controller?
My CarVM:
public class CarViewModel
{
[MaxLength(150)]
[Display(AutoGenerateField = true, Name = "Entered By")]
public string Driver { get; set; }
[Display(AutoGenerateField = true)]
public Colour Colour { get; set; }
[Key]
[Display(AutoGenerateField = false)]
public int Id { get; set; }
[Display(AutoGenerateField = false)]
public bool IsDeleted { get; set; } = false;
public IEnumerable<SelectListItem> Colours { get; set; }
public CarViewModel() { }
public CarViewModel(Model CarModel summaryModel, CarPropertyCollection propertyCollection)
{
Driver = summaryModel.Driver;
Id = summaryModel.Id;
IsDeleted = summaryModel.IsDeleted;
Colour = summaryModel.Colour == null ? null :
propertyCollection.Colours.Where(x => x.Id == summaryModel.Colour.Id).FirstOrDefault();
Colours = propertyCollection.Colours.Select(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name });
}
}
}
Must stress that Colour is a custom class but only has Id and Name properties
Colours doesn't relate to a specific car, it relates to cars in general, so rather than using a collection as your view model, create a wrapper:
class EditCarsViewModel
{
public IEnumerable<SelectListItem> Colours { get; set; }
public IList<CarViewModel> Cars { get; set; }
}
Then your view:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour, Model.Colours, "Unknown")
</td>
}
Any other CarViewModel properties will need their own input as well. HiddenFor can be used if they should be readonly:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
#Html.HiddenFor(m => Model.Cars[i].Id)
#Html.HiddenFor(m => Model.Cars[i].Driver)
<!-- etc. -->
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour.Id, Model.Colours, "Unknown")
</td>
}
And your controller:
[HttpPost]
public ActionResult SummarySaveAll(EditCarViewModel model)
{
// model.Cars should be populated
return View();
}
Note that an indexable collection, such as IList<T> should be used, as the form field names need to include the index to differentiate the items.
Edit by OP
The Colour class consists of a [Key] int Id property and a string Name property. For DropDownList items I had to make sure the Id property was specified on the m => Model.Cars[i].Colour.Id line otherwise that particular prop was coming back as null even though other items were coming through fine.
try
[HttpPost]
public ActionResult SummarySaveAll(IList<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View(summaries);
}
I've also added this model as a param for your view
This how you do it:
First my View which posts back to a controller named Home and an action named ListView:
#model List<MyModel>
#{
ViewData["Title"] = "Using a list as model";
}
<h1>#ViewData["Title"]</h1>
#using (Html.BeginForm("ListView", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
Name
</th>
<th width="1">
Description
</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td width="1">
#Html.DisplayFor(m => Model[i].Name)
</td>
<td width="1">
#Html.TextBoxFor(m => Model[i].Description)
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
Notice how I used an indexer to render the controls [i]
This is my model:
public class MyModel
{
public string Name { get; set; }
public string Description { get; set; }
}
This is my controller action:
[HttpPost]
public IActionResult ListView(IEnumerable<MyModel> model)
{
return View(model);
}
And this is the result:

ASP.NET passing List back to controller from IEnumerable #model inside Table header ActionLink with non-indexing

I have been reviewing possible ways to return a View's #model information which is of type IEnumerable back to the controller, so that if I sort/filter on a query from database, I can refine the return each iteration without restarting with a fresh full list being returned. All the ways show you need to POST back based on model[index] which works if you are inside a for loop. But I am working with sending the collection back from an #HTML.ActionLink within a table's header section, so there is no possible indexing available.
My WebAPI setup is based on this where they show how to sort and filter. I am trying to make it a little more complex in that after I filter a list based on my original DB query, I will then be able to sort (from a clickable-actionLink on a table's header column) from that filtered list; where as currently it would just sort from a fresh complete list.
The only way I can think of to do this is pass back (by a POST) to the controller the updated list of the customClass.
#Html.ActionLink("Name", "Index", new { orderBy = ViewBag.sortByName,
companyListIds = Model.???? })
A better option (based on comments from Tacud) which would require a smaller POST URL would be by returning a list of the id properties only which can then be applied to a query. But its still a list and still needs to be sent back without an index from and ActionLink. This will help keep track and allow me to continue drilling down to a smaller and smaller list.
Below is parts of my model class, the index Action from the controller, and the index view.
Model namespace:
public class Company
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
}
Controller Namespace:
public async Task<IActionResult> Index(ICollection<int> prev, string orderBy , string searchCategory ="", string searchString = "")
{
List<string> Categories = new List<string>() { "Name", "City", "State", "Zip", "Contact Person" };
ViewBag.searchCategory = new SelectList(Categories);
ViewBag.sortByName = orderBy == null ? "name" : orderBy == "name" ? "namedesc" : "name";
ViewBag.sortByCity = orderBy == "city" ? "citydesc" : "city";
ViewBag.sortByState = orderBy == "state" ? "statedesc" : "state";
ViewBag.companyIndex = companyList.Count==0 ? await _context.Company.ToListAsync() : companyList ;
List<Company> resultSet = new List<Company>(ViewBag.companyIndex);
if (!String.IsNullOrEmpty(searchCategory) && !String.IsNullOrEmpty(searchString))
{
switch (searchCategory)
{
.....
}
}
switch (orderBy)
{
....
}
return View(resultSet);
}
View namespace:
#model IEnumerable<Laier_It.Models.Company> <p>
#using (Html.BeginForm() {
<p>
Search By: #Html.DropDownList("SearchCategory", "")
Search For: #Html.TextBox("SearchString")
<input type="submit" value="Filter" />
</p> }
<table class="table ">
<thead>
<tr>
<th>
#Html.ActionLink("Name", "Index", new { orderBy = ViewBag.sortByName, companyList = Model })
</th>
<th>
#Html.DisplayNameFor(model => model.Address)
</th>
<th>
#Html.ActionLink("City", "Index", new { orderBy = ViewBag.sortByCity, companyList = Model })
</th>
<th>
#Html.ActionLink("State", "Index", new { orderBy = ViewBag.sortByState, companyList = Model })
</th> </tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.State)
</td> </tr>
}
</tbody>
</table>
To first obtain the list of Id's that I want to post back to the controller of the current rendition showing within the WebAPI view page I used #Model.Select(x => x.Id)
My controller index method was only changed by this
var resultSet = prev.Count == 0 ? await _context.Company.ToListAsync() :
await _context.Company.Where(x => prev.Contains(x.Id)).ToListAsync();
And my View looks like this:
#model IEnumerable<Laier_It.Models.Company>
#using (Html.BeginForm() )
{
<p>
Search By: #Html.DropDownList("SearchCategory", "")
Search For: #Html.TextBox("SearchString")
<input type="submit" value="Filter" />
</p>
}
<table class="table ">
<thead>
<tr>
<th>
#Html.ActionLink("Name", "Index", new { orderBy = ViewBag.sortByName, prev = #Model.Select(x => x.Id) } )
</th>
<th>
#Html.DisplayNameFor(model => model.Address)
</th>
<th>
#Html.ActionLink("City", "Index", new { orderBy = ViewBag.sortByCity, prev = #Model.Select(x => x.Id) } )
</th>
<th>
#Html.ActionLink("State", "Index", new { orderBy = ViewBag.sortByState, prev = #Model.Select(x => x.Id) } )
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.State)
</td>
</tr>
}
</tbody>
</table>

MVC passing the selected object from a List view page

I have a little serach box that returns results from a database. That works fine. The results are in a List page and display correctly. However, I need to take the selected object and pass it to my controller. I am getting NULL values when I debug it, and an empty results page.
Here is the model:
public class CodeSnip
{
public short Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public LangType Language { get; set; }
public string Creator { get; set; }
}
public class ListOfCodeSnips : List<CodeSnip>
{
public CodeSnip CodeSnip { get; set; }
}
public enum LangType
{
CSharp,
css,
HTML,
JavaScript,
Perl,
PHP,
Python,
Ruby,
SQL,
VB,
XML,
Other
}
Here is the controller method (which does nothing atm):
[HttpPost]
public ActionResult Display(CodeSnip snip)
{
return View(snip);
}
Here is what I have for a view. Again, it posts only NULLS for the object values:
#model Models.ListOfCodeSnips
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Language)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Creator)
</th>
</tr>
#using (Html.BeginForm("Display", "Home", FormMethod.Post))
{
foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Language)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Creator)
</td>
<td>
<input type="submit" value ="Display"/>
</td>
</tr>
}
}
</table>
so, in short, what I want to do is take the selected model-item from the view and pass it into my controllers Display method, but as I said, all I get are nulls.
I have looked around and all i find are examples f how to pass a List of objects. I tried monkeying with those, but got nothing.
Thanks for your time.
Since you said that you have tried with ActionLink and it did not work, here is how it would work.. instead of passing the type that you are looking for as a parameter for the Display action, pass the ID of the record.
So it would look something like this:
Controller Action
[HttpGet]
public ActionResult Display(int id)
{
var snip = db/* connection string */.TableName.Find(id);
return View(snip);
}
ActionLink
#Html.ActionLink("Display", "Display", "ControllerName", new { id = item.Id }, null )
// Or if you want a button that acts as a link, and not just a plain link
<input type="button" class="btn" value="Display" onclick="location.href='#Url.Action("Display", "ControllerName", new { id = item.Id })'" />
Let me know if this helps!
There are two problems with your code:
1) You do not render any <input> elements in which you send the selected values back to the controller. Use Html.HiddenFor or Html.EditorFor in addition to Html.DisplayFor.
2) In order for the MVC Modelbinder to be able to bind your list, use a for loop instead of foreach.
See also MVC Form submit not transferring the updated model back to action method for List
for (var i = 0; i < Model.Count(); i++) {
<tr>
<td>
#Html.DisplayFor(m => m[i].Title)
#Html.HiddenFor(m => m[i].Title)
</td>
#* etc ... *#
<tr>
}
PS: this loop would post all displayed CodeSnips, i.e. the receiving action would have the signature public ActionResult Display(ListOfCodeSnips postData).

Update multiple objects using EntityFramework in ASP.NET MVC 3 with TryUpdateModel method

I am struggling with ASP.NET MVC3 when trying to update data via Entity Framework. The process is as followed:
I have this Model:
public class Bet
{
public string BetID { get; set; }
public int FixtureID { get; set; }
public string Better { get; set; }
public int GoalsHome { get; set; }
public int GoalsGuest { get; set; }
public virtual Fixture { get; set; }
}
In the controller I filter the table via a where statement to get only the entrys that match the user:
[HttpGet]
public ActionResult Index()
{
var user = User.Identity.Name;
var model = _db.Bets.Where(t => t.Better == user);
return View(model);
}
The View is two parts, one that takes care of the table headers, and the other that lists all bets of the user:
<fieldset>
<legend>Bets</legend>
<table>
<tr>
<th>
Kickoff
</th>
<th>
Home Team
</th>
<th>
Guest Team
</th>
<th>
Group
</th>
<th>
Bet
</th>
</tr>
#Html.EditorFor(m => m) //this is resolved in an self made EditorTemplate
</table>
The EditorTemplate:
#model Betting.Models.Bet
<tr>
<td>
#Html.DisplayFor(modelItem => Model.Fixture.Kickoff)
</td>
<td>
#Html.DisplayFor(modelItem => Model.Fixture.HomeTeam)
</td>
<td>
#Html.DisplayFor(modelItem => Model.Fixture.GuestTeam)
</td>
<td>
#Html.DisplayFor(modelItem => Model.Fixture.Group)
</td>
<td>
#Html.TextBoxFor(modelItem => Model.GoalsHome, new { style = "width: 30px" }):
#Html.TextBoxFor(modelItem => Model.GoalsGuest, new { style = "width: 30px" })
</td>
#Html.HiddenFor(modelItem => Model.FixtureID)
</tr>
Back in the controller I try to update the model:
[HttpPost]
public ActionResult Index(FormCollection collection)
{
var user = User.Identity.Name;
var model = _db.Bets.Where(t => t.Better == user);
TryUpdateModel(model);
_db.SaveChanges();
return RedirectToAction("Index");
}
This does absolutely nothing. The Entity Framework won't update the database at all. I even separated the table so that the resulting bet tags in the html file will be distinguishable (note the [index] before the name value:
<input data-val="true" data-val-number="The field GoalsHome must be a number." data-val-required="The GoalsHome field is required." name="[1].GoalsHome" style="width: 30px" type="text" value="3" />:
<input data-val="true" data-val-number="The field GoalsGuest must be a number." data-val-required="The GoalsGuest field is required." name="[1].GoalsGuest" style="width: 30px" type="text" value="0" />
Can somebody tell me why the Entity Framework isn't updating the database? Is there something wrong with the object mapping?
I was having the same issue before. MSDN can be pretty confusing on this subject but halfway down the page states that if you are creating your POCO entities without proxycreationenabled, you will have to call detectchanges before calling savechanges.
TryUpdateModel(model);
_db.DetectChanges();
_db.SaveChanges();
Ok, it seems that I found a way to update the database without using the TryUpdateModel method. Since the items sent in the html post are distinguishable, I can add a parameter to the controller method which holds the bets I get from the view. Then I iterate over the results from the database and update the fields that changed with the field values of the bets from the view:
[HttpPost]
public ActionResult Index(FormCollection collection, List<Bet> bets)
{
var user = User.Identity.Name;
var model = _db.Bets.Where(t => t.Better== user);
int i = 0;
foreach (var bet in model)
{
Bet the_Bet= bets.Find(
delegate(Bet _bet)
{
return _bet.BetID == bet.BetID;
});
bet.GoalsHome= the_Bet.GoalsHome;
bet.GoalsGuest= the_Bet.GoalsGuest;
i++;
}
_db.SaveChanges();
return RedirectToAction("Index");
}
I wonder if there is still a way to get it work with the TryUpdateModel method.

Categories

Resources