Error using TryUpdateModelAsync in a form with navigation properties - c#

Suppose I have a form that contains the following model structure:
TestRecord.cs
class TestRecord {
public string Id { get; set; }
public string RecordName { get; set; }
...other props
public ICollection<RtdbFiles> RtdbFiles { get; set; }
}
Where the corresponding RtdbFile contains the following props:
RtdbFile.cs
class RtdbFile {
public int Id { get; set; }
public string Filename { get; set; }
...other props
}
When I POST this model to my controller to update, I receive the following error:
The instance of entity type 'RtdbFile' cannot be tracked because another instance with the key value '{Id: 2021}' is already being tracked
So it appears that two of the same RtdbFile are being attached to the context. Here's how my controller method is formatted:
[HttpPost("UpdateMilestones")]
public async Task<IActionResult> UpdateMilestones(string testRecordId)
{
db.ChangeTracker.LazyLoadingEnabled = false;
var record = db.TestRecords
.Include(tr => tr.RtdbFiles)
.FirstOrDefault(tr => tr.TestRecordId == testRecordId);
if (await TryUpdateModelAsync(record))
{
await db.SaveChangesAsync();
}
return RedirectToAction("Milestones", new { id = testRecordId });
}
Is TryUpdateModelAsync() not made to handle situations with a One-to-Many relationship? When is the duplicate RtdbFile being added to the context? I've disabled lazy loading and eagerly load the RtdbFiles. This is similar to what is done in the Contoso University example by Microsoft but the difference is their eagerly loaded property is a One-to-One relationship.
How can I fix this? Thanks!
EDIT to show Razor Pages:
UpdateMilestones.cshtml
#model rtdb.Models.TestRecord
#addTagHelper *, rtdb
<input type="hidden" asp-for="#Model.TestRecordId" />
<div class="form-section-text">Milestones & Tracking</div>
<!--unrelated inputs removed -->
<div class="form-group">
<vc:file record="#Model" type="#RtdbFile.AttachmentType.TPR" approvers="true"></vc:file>
</div>
The RtdbFiles are abstracted out a bit in to view components:
File View Component
#model rtdb.Models.ViewModels.FileViewModel
#addTagHelper *, rtdb
#using HtmlHelpers.BeginCollectionItemCore
<div class="form-group attachments">
<div class="link-header">#(Model.AttachmentType.ToString("G"))</div>
<div class="row">
<div class="col-sm-12">
#if (Model.TestRecord.RtdbFiles.Count > 0)
{
foreach (var file in Model.TestRecord.RtdbFiles.Where(f => f.IsDeleted != true && f.Type == Model.AttachmentType && f.TestRecordId == Model.TestRecord.TestRecordId).ToList())
{
<div class="attachment">
#using (Html.BeginCollectionItem("RtdbFiles"))
{
<div class="form-group">
<div class="form-row">
<input asp-for="#file.Id" hidden />
<input asp-for="#file.Type" hidden />
<div class="col-sm-6">
#if (#file.Id < 1)
{
<input class="FileInput" asp-for="#file.UploadedFile" type="file" />
}
else
{
<div><span data-file-id="#file.Id"><a href='#Url.Action("Download", "RtdbFiles", new { id = file.Id })'>#file.Filename (#file.Type.ToString("G"))</a></span></div>
}
</div>
<div class="col-sm-6">
<div>
<label asp-for="#file.FileApproverPersonnel" class="col-form-label col-form-label-sm">Approvers:</label>
<input asp-for="#file.FileApproverPersonnel" class="form-control file-approver-personnel ldap-tags" />
</div>
</div>
</div>
</div>
}
</div>
}
}
<div id="#(Model.AttachmentType.ToString("G"))s"></div>
<button type="button" class="add-file btn btn-primary" data-test-type="Other" data-attachment-type="TPR" data-container="#(Model.AttachmentType.ToString("G"))s">Add #(Model.AttachmentType.ToString("G"))</button>
<small style="display: block; margin-top: 6px">File size limit: 100MB</small>
</div>
</div>
</div>

What is obvious is TryUpdateModelAsync or maybe ChangeTracker has some issues with string ForeignKeys. First of all I highly recommend you to change PrimaryKey to int because EF shows some odd behaviour in such cases.
But if you insist on it, I tried some ways and finally reach this way:
Preventing object from tracking with AsNoTracking and use context.Update after updating record based on controller model
Based on your latest models, It's my sample that works well:
Models:
public class TestRecord
{
public string Id { get; set; }
public string RecordName { get; set; }
public virtual IList<RtdbFile> RtdbFiles { get; set; }
}
public class RtdbFile
{
public int Id { get; set; }
public string TestRecordId { get; set; }
public string Filename { get; set; }
}
Razor Page:
Note: This part has the most important effect on your result. specially RtdbFiles[{i}].Id and RtdbFiles[{i}].Filename
Your View have to send items and values with exactly same name to server object to take effect correctly:
#model Jordan.TestRecord
#using (Html.BeginForm("UpdateMilestones", "Home", FormMethod.Post))
{
#Html.HiddenFor(p => p.Id);
#for (int i = 0; i < Model.RtdbFiles.Count; i++)
{
#Html.Hidden($"RtdbFiles[{i}].Id", Model.RtdbFiles[i].Id);
#Html.TextBox($"RtdbFiles[{i}].Filename", Model.RtdbFiles[i].Filename);
}
<button type="submit">Save</button>
}
Controller:
namespace Jordan
{
[Route("")]
public class HomeController : Controller
{
private readonly AppDbContext context;
public HomeController(AppDbContext context)
{
this.context = context;
context.Database.EnsureCreated();
}
[HttpGet]
public IActionResult Index()
{
var sampleRecord = context.TestRecords
.Include(r => r.RtdbFiles)
.FirstOrDefault();
return View(sampleRecord);
}
[HttpPost]
[Route("UpdateMilestones")]
public async Task<IActionResult> UpdateMilestones(int Id)
{
context.ChangeTracker.LazyLoadingEnabled = false;
var record = context.TestRecords
.Include(tr => tr.RtdbFiles)
.AsNoTracking()
.FirstOrDefault(tr => tr.Id == Id);
if (await TryUpdateModelAsync(record))
{
context.Update(record);
await context.SaveChangesAsync();
}
return RedirectToAction("Index");
}
}
}

I got it, but there seems to be no case of TryUpdateModelAsync on
one-to-many online. (And I tried without success).
Therefore, I suggest that you can use our common method of updating the one-to-many data model.
In the view, you need to bind each field of TestRecord to the corresponding control and pass the latest data of TestRecord to UpdateMilestones action.
Please refer to the following code:
View (which show one record of TestRecord and it related to multiple RtdbFiles datas):
#model WebApplication_core_mvc.Controllers.TestRecord
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
var i = 0;
}
<h1>Index</h1>
<form asp-action="UpdateMilestones" method="post">
<input id="Text1" type="text" asp-for="Id" hidden />
<label>#Model.Id</label>
<input id="Text2" type="text" asp-for="RecordName" />
<br />
<h4>Rtb:</h4>
<table>
<tr>
<th>Id</th>
<th>FileName</th>
</tr>
#foreach (var item in Model.RtdbFiles)
{
<tr>
<td> <input id="Text1" type="text" value="#item.Id" name="RtdbFiles[#i].Id" hidden/>#item.Id</td>
<td> <input id="Text1" type="text" value="#item.Filename" name="RtdbFiles[#i].Filename" /></td>
</tr>
i++;
}
</table>
<input id="Submit1" type="submit" value="submit" />
</form>
Update
Controller:
[HttpPost("UpdateMilestones")]
public async Task<IActionResult> UpdateMilestones(TestRecord testRecord)
{
db.Entry(testRecord).State = EntityState.Modified;
db.Entry(testRecord).Property(x => x.Id).IsModified = false;
foreach (var item in testRecord.RtdbFiles)
{
db.Entry(item).State = EntityState.Modified;
db.Entry(item).Property(x => x.Id).IsModified = false;
}
await db.SaveChangesAsync();
return RedirectToAction("Milestones", new { id = testRecord.Id });
}
Here is the test result:

Related

many to many relationship update data mvc .net core

I have a many to many relationship in .net core mvc, but I have no idea how to implement a edit view.
The models are
Studios
public class Studio
{
public int StudioID { get; set; }
public string Name { get; set; }
public ICollection<StudioAddress>StudioAddresses { get; set; }
}
Addresses
public class Address
{
public int AddressID { get; set; }
public string Street { get; set; }
public ICollection<StudioAddress> StudioAddresses { get; set; }
}
StudioAddress
public class StudioAddress
{
public int StudioID { get; set; }
public Studio Studio { get; set; }
public int? AddressID { get; set; }
public Address Address { get; set; }
}
My databasecontext
modelBuilder.Entity<StudioAddress>()
.HasKey(sa => new { sa.StudioID, sa.AddressID });
modelBuilder.Entity<StudioAddress>()
.HasOne(sa => sa.Studio)
.WithMany(s => s.StudioAddresses)
.HasForeignKey(sa => sa.StudioID);
modelBuilder.Entity<StudioAddress>()
.HasOne(sa => sa.Address)
.WithMany(a => a.StudioAddresses)
.HasForeignKey(sa => sa.AddressID);
Now, I have created the edit Get method in my studioscontroller
// get
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var studio = await _context.Studios
.Include(s => s.StudioAddresses).ThenInclude(s => s.Address)
.Where(s => s.StudioID == id)
.AsNoTracking()
.FirstOrDefaultAsync();
if (studio == null)
{
return NotFound();
}
return View(studio);
}
But I have no idea how to update the related data for studio and address?
Bot are forms with textfields. The original microsoft docs are confusing (they work with tickboxes) and weird methods to whitelist fields. Is there a simpler, more intuitive way of doing this?
Based on your model definition, you could try to design the Edit view and the Post method like below :
Here is the “Edit” view:
#model SOMVCDemo.Models.Studio
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="StudioID" />
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">StudioAddresses</label>
<table>
<tbody>
#{ int i = 0;}
<tr>
#foreach (var StudioAddress in #Model.StudioAddresses)
{
<td>
<input type="hidden" name="studioAddresses[#i].AddressID" asp-for="#StudioAddress.AddressID" class="form-control" />
<input type="text" name="studioAddresses[#i].Address.Street" asp-for="#StudioAddress.Address.Street" class="form-control" />
</td>
i++;
}
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Here is the POST method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, Studio studio)
{
if (id != studio.StudioID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
var st = _context.Studios.FirstOrDefault(n => n.StudioID == studio.StudioID);
st.Name = studio.Name;
_context.Update(st);
foreach(var i in studio.StudioAddresses)
{
var address = _context.Addresses.FirstOrDefault(n=>n.AddressID == i.AddressID);
address.Street = i.Address.Street;
_context.Update(address);
}
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!StudioExists(studio.StudioID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(studio);
}

aspnet core how to post data from View, if model complicated

I have some probles with using Post methods in controllers ASP.NET Core MVC. Maybe i using wrong architecture.
I have 2 Models from DB.
public class RecipeTable
{
public int Id { get; set; }
public string MetrologyRecipe { get; set; }
public string MetrologyTool { get; set; }
//other properties
}
public class ParamTable
{
public int AupId { get; set; }
public string ParamName{ get; set; }
public string RecipeName { get; set; }
public int? ParamOrderAuto { get; set; }
//other properties
}
And box for them. Because one entry in RecipeTable is associated with several entres from ParamTable.
public class FullModel
{
public List<ParamTable> ParamRows ;
public RecipeTable RecipeRow { set; get; }
public FullModel()
{
ParamRows = new List<ParamTable> ();
}
}
For [Get]"Edit" method this is work great.
[HttpGet]
public async Task<IActionResult> Edit(int? id, string recipeName)
{
var fullModel = new FullModel();
if (id == null) return NotFound();
fullModel.RecipeRow = await
_context.RecipeTable.SingleOrDefaultAsync(m => m.Id == id);
foreach (var row in _context.ParamTable)
if (recipeName == row.RecipeName)
fullModel.ParamRows.Add(row);
if (fullModel.RecipeRow.MetrologyRecipe == null) return NotFound();
return View(fullModel);
}
But for [Post]"Edit" this is does not work, of course.
Only Recipe part updated. I dont understand how post method get data from View. How work with this complicated models, when you can't change database and can't
specify connection directly in database designer.
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, FullModel fullModel)
{
if (id != fullModel.RecipeRow.Id) return NotFound();
if (ModelState.IsValid)
{
try
{
//**Here fullModel.ParamRows.Count = 0**
_context.Update(fullModel.RecipeRow);
await _context.SaveChangesAsync();
foreach (var elem in fullModel.ParamRows)
{
_context.Update(elem);
await _context.SaveChangesAsync();
}
}
catch (DbUpdateConcurrencyException)
{
if (!RecipeTableExists(fullModel.RecipeRow.Id))
return NotFound();
throw;
}
return RedirectToAction(nameof(Index));
}
return View(fullModel);
View part look like this:
#model FullModel
#{
ViewData["Title"] = "Edit";
}
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<table class="table" style="margin-left: -50px">
<thead>
<tr>
<th>Order</th>
<th>Param</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.ParamRows.Count; i++)
{
<tr>
<td>
<div class="form-group">
<input asp-for="#Model.ParamRows[i].ParamOrderAuto" type="text" class="form-control" />
</div>
</td>
<td>
<div class="form-group">
<input asp-for="#Model.ParamRows[i].ParamName" class="form-control" />
</div>
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
You can't use a foreach to iterate over the ParamRows. In order for the modelbinder to be able to bind this back to a list on post, the fields need to have names like ParamRows[N].ParamName. Using a foreach, you'll end up with names like row.ParamName instead, which the modelbinder will not recognize and will simply discard.
Instead, use a standard for loop:
#for (var i = 0; i < Model.ParamRows.Count; i++)
{
...
<input asp-for="ParamRows[i].ParamName" />
...
}
Alternatively, you can create an editor template for a ParamTable, i.e. ~/Views/Shared/EditorTemplates/ParamTable.cshtml, and put all the HTML (fields, labels, etc.) for editing a ParamTable instance in that. Then, instead of iterating over the items in ParamRows, you can simply do:
#Html.EditorFor(m => m.ParamRows)
Razor will automatically iterate over each item and render you editor template for each one, giving each field a correct name for binding.
You have a small problem with the FullModel, need to add get; set; methods. Just correct like this:
public class FullModel
{
public List<ParamTable> ParamRows { get; set; };
public RecipeTable RecipeRow { set; get; }
...

asp net mvc post gives empty viewmodel

So I have a list of viewmodels that i iterate through and one of the properties in the viewmodel object is a dictionary. In my customercontroller, in the details action I get all the viewmodels that correspond to the id from the asp-route and in the view i have a form where I present all the dictionary values so that you can modify them if you like. Afterwards you can submit the form. This is where I se that the list of viewmodels are "0". Why is this?
this is my model:
public class CustomerViewModel
{
public CustomerViewModel()
{
EmployeeAndHours = new Dictionary<string, int>();
}
public string projectName { get; set; }
public Dictionary<string, int> EmployeeAndHours { get; set; }
}
this is the get action:
// GET: Customers/Details/5
public IActionResult Details(int? id)
{
if (id == null)
{
return NotFound();
}
var customers = _customerHelper.GetCustomerDetails(id);
if (customers == null)
{
return NotFound();
}
return View(customers);
}
this is the post action:
[HttpPost]
public IActionResult EditCustomerDetailViewModel(List<customerViewModel> customers)
{
//TODO
return View("Details");
}
this is my view:
#model List<myNamespace.Models.ViewModels.CustomerViewModel>
<div class="container-fluid">
<form asp-controller="Customers" asp-action="EditCustomerDetailViewModel" method="post">
#foreach (var customer in Model)
{
<div class="media">
<div class="media-body">
<div class="text-wrapper">
<h5 class="mt-0">#customer.projectName</h5>
</div>
<div class="slider-wrapper">
#foreach (var employee in customer.EmployeeAndHours) // This is the dictionary
{
<input name="#("EmployeeAndHours[" + employee.Key + "]")" type="range" min="0" max="1000" step="1" value="#employee.Value" data-orientation="horizontal">
<hr />
}
</div>
</div>
</div>
}
<div class="form-group" style="text-align:center">
<input id="customer-detail-form-button" type="submit" value="Save changes" class="btn btn-success" />
</div>
</form>
</div>
You cannot use a foreach loop to generate form controls for collection items and get correct 2-way model binding. You need to use a for loop or and EditorTemplate for typeof CustomerViewModel as explained in Post an HTML Table to ADO.NET DataTable.
In addition, you should avoid binding to a Dictionary because you cannot use the strong typed HtmlHelper method or TagHelpers to give 2-way model binding.
In order oo bind to your current model, your name attribute would need to be in the format name="[#].EmployeeAndHours[Key]" where # is the zero-based collection indexer.
Instead, modify your view models to
public class CustomerViewModel
{
public string ProjectName { get; set; }
public List<EmployeeHoursViewMode> EmployeeHours { get; set; }
}
public class EmployeeHoursViewModel
{
public string Employee { get; set; }
public int Hours{ get; set; }
}
And the view then becomes
#model List<CustomerViewModel>
<form asp-controller="Customers" .... >
#for(int i = 0; i < Model.Count i++)
{
....
<h5>#Model[i].ProjectName</h5>
#Html.HiddenFor(m => m[i].ProjectName)
// or <input type="hidden" asp-for-"Model[i].ProjectName />
<div>
#for(int j = 0; j < Model[i].EmployeeHours.Count; j++)
{
#Html.HiddenFor(m => m[i].EmployeeHours[j].Employee)
#Html.LabelFor(m => m[i].EmployeeHours[j].Hours, Model[i].EmployeeHours[j].Employee)
// or <label asp-for="Model[i].EmployeeHours[j].Hours">#Model[i].EmployeeHours[j].Employee</label>
#Html.TextBoxFor(m => m[i].EmployeeHours[j].Hours, new { type = "range", ... })
// or <input asp-for-"Model[i].EmployeeHours[j].ProjectName type="range" ... />
}
</div>
}
<input type="submit" value="Save changes" class="btn btn-success" />
}
Note the above code assumes you want to post back the value of ProjectName (hence the hidden input), and that you want to display the employee name adjacent each 'Hours' input.
You are not referencing the model name from your POST action, try this:
#{int index = 0;}
#foreach (var customer in Model)
{
...
#foreach (var employee in customer.EmployeeAndHours)
{
<input name="customer[#(index)].EmployeeAndHours[#(employee.Key)]" type="range" min="0" max="1000" step="1" value="#employee.Value" data-orientation="horizontal">
<hr />
}
...
index++;
}

List of Checkboxes is NULL Upon Submit of the Form

The issue here is that upon submitting the form, the list of UserCustomerClaimsproperties in the ApplicationUserEditViewModel object is null; the responses of the check boxes in the for loop are not being or making it to the controller.
View
#model WaterManagementSystem.Features.Admin.ApplicationUserEditViewModel
#{
ViewBag.Title = "Edit User";
Layout = "_Layout";
}
<form asp-action="UpdateUser" asp-controller="Admin" method="post">
...
<div style="color: White;">
#foreach (var userClaim in Model.UserCustomerClaims)
{
<div>
<input type="checkbox" asp-for="#userClaim.HasClaim" id="#userClaim.ID" name="#userClaim.ID" checked="#(userClaim.HasClaim)" />
<input type="hidden" asp-for="#userClaim.ID" /> #userClaim.UserClaim.Type
</div>
}
</div>
<div>
<div>
<button type="submit" class="btn btn-default">Save</button>
</div>
</div>
</form>
View Model
public class ApplicationUserEditViewModel : ApplicationUserViewModel
{
...
private List<UserCustomerClaim> userCustomerClaims_ = null;
public ApplicationUserEditViewModel() : this(new ApplicationUser())
{
userCustomerClaims_ = new List<UserCustomerClaim>();
}
public List<UserCustomerClaim> UserCustomerClaims
{
get
{
return userCustomerClaims_;
}
}
public class UserCustomerClaim
{
public UserCustomerClaim(Claim userClaim, bool hasClaim)
{
UserClaim = userClaim;
HasClaim = hasClaim;
ID = Guid.NewGuid();
}
public Guid ID { get; set; }
public Claim UserClaim { get; set; }
public bool HasClaim { get; set; }
}
}
Controller
[HttpPost]
public async Task<ActionResult> UpdateUser(ApplicationUserEditViewModel vm)
{
...
}
The proposed duplicate does not use the asp-for tag helper.

Can’t update ICollection property

The problem is when I try to update Master and Details Tables at the same time.
When call Post Edit Task the Details objects don´t appear.
The Edit View displays all Details rows correctly, but while debugging the Edit POST, Casas is empty
MODELS
public partial class Modelo : IValidatableObject {
public Modelo()
{
Casas = new HashSet<Casa>();
}
public int Modeloid { get; set; }
public string Modelo1 { get; set; }
public virtual ICollection<Casa> Casas { get; set; }//Don’t work to update
}
public partial class Casa // DETAIL TABLE
{
public int Casaid { get; set; }
public int Modeloid { get; set; } // FK to Modelo
public string Casa1 { get; set; }
public virtual Modelo Modelo { get; set; }
}
CONTROLLER
public class ModelosController : Controller
. . . . . . . . .
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, Modelo modelo)
{
if (id != modelo.Modeloid)
{
return NotFound();
}
if (ModelState.IsValid)
{
// Here modelo.Modelo1 has current modified value
// but modelo.Casas.Count == 0
_context.Update(modelo);
await _context.SaveChangesAsync();
}
}
// GET: Modelos/Edit
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var modelo = await _context.Modelo
.AsNoTracking()
.Include(m => m.Fotomodelos)
.Include(m => m.Casas)
.SingleOrDefaultAsync(m => m.Modeloid == id);
if (modelo == null)
{
return NotFound();
}
return View(modelo);
}
View EDIT.CSHTML
#using System.IO
#model Disponibilidad.Models.Modelo
<form asp-action="Edit">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Modeloid" />
<div class="form-group">
<label asp-for="Modelo1" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Modelo1" class="form-control" />
<span asp-validation-for="Modelo1" class="text-danger"></span>
</div>
</div>
#{
for (int i = 0; i < Model.Casas.Count; i++)
{
<input type="hidden" asp-for="#Model.Casas.ElementAt(i).Modeloid"
value="#Model.Modeloid" />
<input type="hidden" asp-for="#Model.Casas.ElementAt(i).Casaid" />
<div class="form-group">
<label asp-for="#Model.Casas.ElementAt(i).Casa1"
class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="#Model.Casas.ElementAt(i).Casa1"
class="form-control" /> <!-- DISPLAY OK Detail rows -->
<span asp-validation-for="#Model.Casas.ElementAt(i).Casa1"
class="text-danger"></span>
</div>
</div>
}
}
<div class="btn-group">
<button type="submit" class="btn btn-danger">Save</button>
</div>
</div>
</form>
When you use a for cycle instead of foreach in Razor, the name of the properties doesn't get rendered correctly when using the default asp-for TagHelpers.
You can correct your example changing your razor form inputs as follow:
From:
<input type="hidden" asp-for="#Model.Casas.ElementAt(i).Casaid" />
To:
<input type="hidden" name="modelo.Casas[#i].Casaid" value="#Model.Casas.ElementAt(i).Casaid" />

Categories

Resources