// Test model
public class Test
{
public int TestId { get; set; }
public string Title { get; set; }
}
// Question model
public class Question
{
public int QuestionId { get; set; }
public int TestId { get; set; }
public string Title { get; set; }
public virtual Test Test { get; set; }
}
// Answer model
public class Answer
{
public int AnswerId { get; set; }
public int QuestionId { get; set; }
public string Title { get; set; }
public bool IsCorrect { get; set; }
public virtual Question Question { get; set; }
}
// TestDeteils view model
public class TestDeteils
{
public Test Tests { get; set; }
public IEnumerable<Question> Questions { get; set; }
public IEnumerable<Answer> Answers { get; set; }
}
// Test controller, Edit action
public ActionResult Edit(int id)
{
TestDeteils testDeteils = new TestDeteils();
testDeteils.Tests = db.Tests.Find(id);
testDeteils.Questions = db.Questions.Where(q => q.TestId == id);
testDeteils.Answers = db.Answers;
return View(testDeteils);
}
// Edit view
@using (Html.BeginForm()) {
@Html.HiddenFor(model => model.Tests.TestId)
<div>@Html.EditorFor(model => model.Tests.Title)</div>
foreach (var question in Model.Questions)
{
<div>@Html.EditorFor(model => question.Title)</div>
foreach (var answer in Model.Answers.Where(a => a.QuestionId == question.QuestionId))
{
<div>@Html.EditorFor(model => answer.Title)</div>
}
}
<p><input type="submit" value="Save" /></p>
}
// Test conroller, Edit action
[HttpPost]
public ActionResult Edit(TestDeteils testDeteils)
{
if (ModelState.IsValid)
{
db.Entry(testDeteils.Tests).State = EntityState.Modified;
// It's not work, because testDeteils.Questions is null
db.Entry(testDeteils.Questions).State = EntityState.Modified;
// It's not work, because testDeteils.Answers is null
db.Entry(testDeteils.Answers).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(testDeteils);
}
unless you supply a custom binder, you can not use IEnumerable in the binding model. you need to use a concrete type. for collection use List<> or array.
unless you supply a custom binder, you can not use IEnumerable in the binding model. you need to use a concrete type. for collection use List<> or array.
This isn't true. I just finished up a project that binds an IEnumerable<int> without a custom model binder.
PLS NOTICE THE DEFAULT MODEL BINDER IS NOT ABLE TO BIND IEnumerable, BUT IT DO BIND OTHER MORE SPECIFIC INTERFACES such as ICollection<T>, IList<T>, TDictionary<T, U>.
The way it do this is very simple, it has hardcoded rules that map each of the above types to concrete types that implement them. It DOESN'T BIND The IEnumerable because it is too generic...and there is no concrete type that maps "naturally" in it. In fact
also an IDictionary implements the IEnumerable interface, so it is not possible to choose in a way that makes sense a "default" concrete type to map to an IEnumerable.
Alexey Turko...
Member
20 Points
18 Posts
How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 03:48 AM|LINK
// Test model public class Test { public int TestId { get; set; } public string Title { get; set; } } // Question model public class Question { public int QuestionId { get; set; } public int TestId { get; set; } public string Title { get; set; } public virtual Test Test { get; set; } } // Answer model public class Answer { public int AnswerId { get; set; } public int QuestionId { get; set; } public string Title { get; set; } public bool IsCorrect { get; set; } public virtual Question Question { get; set; } } // TestDeteils view model public class TestDeteils { public Test Tests { get; set; } public IEnumerable<Question> Questions { get; set; } public IEnumerable<Answer> Answers { get; set; } } // Test controller, Edit action public ActionResult Edit(int id) { TestDeteils testDeteils = new TestDeteils(); testDeteils.Tests = db.Tests.Find(id); testDeteils.Questions = db.Questions.Where(q => q.TestId == id); testDeteils.Answers = db.Answers; return View(testDeteils); } // Edit view @using (Html.BeginForm()) { @Html.HiddenFor(model => model.Tests.TestId) <div>@Html.EditorFor(model => model.Tests.Title)</div> foreach (var question in Model.Questions) { <div>@Html.EditorFor(model => question.Title)</div> foreach (var answer in Model.Answers.Where(a => a.QuestionId == question.QuestionId)) { <div>@Html.EditorFor(model => answer.Title)</div> } } <p><input type="submit" value="Save" /></p> } // Test conroller, Edit action [HttpPost] public ActionResult Edit(TestDeteils testDeteils) { if (ModelState.IsValid) { db.Entry(testDeteils.Tests).State = EntityState.Modified; // It's not work, because testDeteils.Questions is null db.Entry(testDeteils.Questions).State = EntityState.Modified; // It's not work, because testDeteils.Answers is null db.Entry(testDeteils.Answers).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(testDeteils); }ignatandrei
All-Star
137692 Points
22152 Posts
Moderator
MVP
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 04:53 AM|LINK
UpdateModel(<object retrieved from database with id from form>, "NameOfTheObject")
bruce (sqlwo...
All-Star
37616 Points
5574 Posts
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 03:15 PM|LINK
unless you supply a custom binder, you can not use IEnumerable in the binding model. you need to use a concrete type. for collection use List<> or array.
ryanw51
Contributor
2363 Points
511 Posts
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 06:50 PM|LINK
This isn't true. I just finished up a project that binds an IEnumerable<int> without a custom model binder.
ignatandrei
All-Star
137692 Points
22152 Posts
Moderator
MVP
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 07:45 PM|LINK
Really? And how do you create an Interface without a CONCRETE class ?!
ryanw51
Contributor
2363 Points
511 Posts
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 08:31 PM|LINK
Now that I look at my code, I think I'm binding to an IEnumerable, but initializing it as a List type when null... maybe that resolves it?
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace Intranet.Web.Models.Entities { public class Group { private ICollection<Page> _pages; private ICollection<User> _users; private ICollection<MediaRestriction> _mediaRestrictions; private ICollection<Application> _applications; private IEnumerable<int> _userIds; private IEnumerable<int> _pageIds; private IEnumerable<int> _applicationIds; public int GroupId { get; set; } [Required()] [StringLength(50)] [Display(Name = "Group Name")] public string GroupName { get; set; } public virtual ICollection<User> Users { get { return _users ?? (_users = new List<User>()); } set { _users = value; } } public virtual ICollection<Page> Pages { get { return _pages ?? (_pages = new List<Page>()); } set { _pages = value; } } public virtual ICollection<Application> Applications { get { return _applications ?? (_applications = new List<Application>()); } set { _applications = value; } } public virtual ICollection<MediaRestriction> MediaRestrictions { get { return _mediaRestrictions ?? (_mediaRestrictions = new List<MediaRestriction>()); } set { _mediaRestrictions = value; } } [NotMapped] [Display(Name = "Users")] public IEnumerable<int> UserIds { get { return _userIds ?? (_userIds = new List<int>()); } set { _userIds = value; } } [NotMapped] [Display(Name = "Pages")] public IEnumerable<int> PageIds { get { return _pageIds ?? (_pageIds = new List<int>()); } set { _pageIds = value; } } [NotMapped] [Display(Name = "Applications")] public IEnumerable<int> ApplicationIds { get { return _applicationIds ?? (_applicationIds = new List<int>()); } set { _applicationIds = value; } } } }ignatandrei
All-Star
137692 Points
22152 Posts
Moderator
MVP
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 08:34 PM|LINK
Of course. No one can create an interface, but a real object from a class that implements the interface...
ryanw51
Contributor
2363 Points
511 Posts
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 22, 2012 08:39 PM|LINK
That makes sense then. I guess I thought there was some magic happening in the model binder until I looked at my code and realized what it was doing.
Alexey Turko...
Member
20 Points
18 Posts
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 23, 2012 05:12 PM|LINK
But how to retrieve object from database with id from form in this case?
francesco ab...
All-Star
20944 Points
3286 Posts
Re: How to pass data form view to controller to save changes in database if few models are used?
Feb 23, 2012 06:02 PM|LINK
PLS NOTICE THE DEFAULT MODEL BINDER IS NOT ABLE TO BIND IEnumerable, BUT IT DO BIND OTHER MORE SPECIFIC INTERFACES such as ICollection<T>, IList<T>, TDictionary<T, U>.
The way it do this is very simple, it has hardcoded rules that map each of the above types to concrete types that implement them. It DOESN'T BIND The IEnumerable because it is too generic...and there is no concrete type that maps "naturally" in it. In fact also an IDictionary implements the IEnumerable interface, so it is not possible to choose in a way that makes sense a "default" concrete type to map to an IEnumerable.
Mvc Controls Toolkit | Data Moving Plug-in Videos