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>