我有这个控制器操作方法:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "UserID,UserName,UserPW,UserLoc,LngID,DictionaryChange,DateEtry,EntryBy,UserEnabled,UserDept,UpdateBy,UpdateDate,UserSafeBox,MainMenuBGColor,SubMenuBGColor,MainMenuFont,SubMenuFont,MainMenuForeColor,SubMenuForeColor,MainlblBGCol,SublblBGCol,CheckboxSelections,ModifiedCheckboxSelections")] tblUserInfo tblUserInfo, string CheckboxSelections, string ModifiedCheckboxSelections)
{
Console.WriteLine($"Checkbox Selections: {CheckboxSelections}");
if (ModelState.IsValid)
{
try
{
db.Entry(tblUserInfo).State = EntityState.Modified;
db.SaveChanges();
// Check if CheckboxSelections is not null
if (!string.IsNullOrEmpty(CheckboxSelections))
{
// Convert the comma-separated string to an array
var selectionsArray = CheckboxSelections.Split(',');
// Remove unchecked checkboxes from tblUserMainForms
var uncheckedMainForms = db.tblUserMainForms
.Where(u => u.UserID == tblUserInfo.UserID && selectionsArray.Contains(u.MformNm))
.ToList();
foreach (var uncheckedMainForm in uncheckedMainForms)
{
db.tblUserMainForms.Remove(uncheckedMainForm);
}
// Remove unchecked checkboxes from tblUserSubMenus
var uncheckedSubMenus = db.tblUserSubMenus
.Where(u => u.UserID == tblUserInfo.UserID && selectionsArray.Contains(u.subFrmNm))
.ToList();
foreach (var uncheckedSubMenu in uncheckedSubMenus)
{
db.tblUserSubMenus.Remove(uncheckedSubMenu);
}
// Process CheckboxSelections (for new selections)
foreach (var newSelection in selectionsArray)
{
if (!db.tblUserMainForms.Any(u => u.UserID == tblUserInfo.UserID && u.MformNm == newSelection))
{
// Checkbox is the first from any select item, save to tblUserMainForm
tblUserMainForm userMainForm = new tblUserMainForm
{
UserID = tblUserInfo.UserID,
MformNm = newSelection
};
db.tblUserMainForms.Add(userMainForm);
}
else if (!db.tblUserSubMenus.Any(u => u.UserID == tblUserInfo.UserID && u.subFrmNm == newSelection))
{
// Checkbox is not the first, save to tblUserSubMenu
tblUserSubMenu userSubMenu = new tblUserSubMenu
{
UserID = tblUserInfo.UserID,
subFrmNm = newSelection
};
db.tblUserSubMenus.Add(userSubMenu);
}
}
// Process ModifiedCheckboxSelections
if (!string.IsNullOrEmpty(ModifiedCheckboxSelections))
{
var modifiedSelectionsArray = JsonConvert.DeserializeObject<List<string>>(ModifiedCheckboxSelections);
// Handle the modified selections as needed
}
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch (Exception ex)
{
// Log the exception for debugging
Console.WriteLine($"Exception: {ex.Message}");
// You might want to handle the exception differently, e.g., return a custom error view
return View("Error");
}
}
return View(tblUserInfo);
}
我在
handleSubmit
文件中有这个 SelectMenu.jsx
方法:
handleSubmit = async (event) => {
event.preventDefault();
const { checkboxSelections, modifiedCheckboxSelections } = this.state;
// Debug: Check if data is correctly populated
console.log("Selected Checkboxes:", checkboxSelections);
console.log("Modified Checkboxes:", modifiedCheckboxSelections);
const formData = new FormData(document.forms[0]);
// Append UserID
formData.append('UserID', document.getElementById("UserID").value);
// Append CheckboxSelections as a comma-separated string
formData.append('CheckboxSelections', checkboxSelections.join(','));
// Append ModifiedCheckboxSelections as JSON string
formData.append('ModifiedCheckboxSelections', JSON.stringify(modifiedCheckboxSelections));
// Log FormData content
for (var pair of formData.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
try {
const response = await fetch("/UserInfoForm/edit", {
method: 'POST',
body: formData,
});
if (response.ok) {
console.log('Form submitted successfully');
} else {
console.error('Form submission failed');
}
} catch (error) {
console.error('Error:', error);
}
};
问题是,当我单击提交并将其放入 jsx 文件中时,
CheckboxSelections
和 ModifiedCheckboxSelections
正在获取我提供给它们的值,但是在我单击编辑视图中的“保存”后,两者的值都更改为null 我犯了什么错误以及如何解决它。
我尝试了所有方法,在 jsx 文件中更改了很多内容,但仍然是同样的事情
您所看到的行为很可能是由于对在控制器和视图之间传递实体的错误假设造成的。首次加载视图(即 View())时,您将从 DbContext 中获取实体,并可能急切加载相关实体(复选框选择),然后传递该实体以在视图中呈现。对于提交,您期望将传回相同的实体和关联。事实并非如此。返回控制器的是 MVC 将用来构造实体的表单数据值的集合。虽然您可能已将包含的实体传递给视图渲染器,但除非视图本身具有跟踪这些相关实体的每个实例的每个属性的元素,否则提交回调将无法填充它们,因此它们将为空。如果您的代码在提交操作中不依赖这些,那么这不是问题,但如果您获取传入的实体,并通过
View(tblUserInfo)
将其发送回视图引擎,则该数据会丢失。
简单的修复:确保重新加载实际实体和相关数据以传回视图。例如:
var user = _context.Users
.Include(x => x.CheckboxSelections)
.AsNoTracking()
.Single(x => x.UserId == userId);
return View(user);
请注意,在这种情况下建议使用
AsNoTracking
来确保返回当前数据状态,因为您的代码正在执行各种删除等操作。
我强烈建议避免使用如下代码:
db.Entry(tblUserInfo).State = EntityState.Modified;
...从视图中传递“实体”。这些类实例不是您发送到视图引擎的实体,它们仅由表单数据组成,并且可能不完整和被篡改。像上面这样的代码相信这个准实体是一个完整的表示,并且最终可能会覆盖您不打算被覆盖的数据。
相反,您应该始终获取有问题的实体(和关联),这两者都有助于验证传递的数据没有被篡改,检查当前的真实数据状态自获取副本以来尚未被修改,(陈旧)数据覆盖)并确保您正在处理一组完整的数据,然后可以将其返回到视图引擎。例如,获取用户及其当前的“主表单”,然后浏览更新的表单选择列表,加载任何新选择并将其关联到用户(添加到加载的 user.tblUserMainForms)并删除不在其中的任何现有引用。选择。
通常给出选定 FormId 的列表,这可以使用 Linq 来完成:
var user = db.Users
.Include(x => x.Forms)
.Single(x => x.UserId == userId);
var existingFormIds = user.Forms.Select(x => x.FormId).ToList();
var formIdsToAdd = selectedFormIds.Except(existingFormIds);
var formIdsToRemove = existingFormIds.Except(selectedFormIds);
if(formIdsToRemove.Any())
{
var formsToRemove = user.Forms
.Where(x => formIdsToRemove.Contains(x.FormId))
.ToList();
foreach(var form in formsToRemove)
user.Forms.Remove(form);
}
if(formIdsToAdd.Any())
{
var formsToAdd = db.Forms
.Where(x => formIdsToAdd.Contains(x.FormId))
.ToList();
foreach(var form in formsToAdd)
user.Forms.Add(form);
}
db.SaveChanges();
return View(user);
以上只是概述更新关联的模式。根据选择传递方式的具体情况(即表单名称而不是 Id 等)以及多对多关系的映射方式(即是否使用连接实体),实际代码看起来会有些不同。该模式的要点是从数据库中获取用户的当前数据状态及其关联。请注意,不要使用
AsNoTracking
,因为我们将依靠更改跟踪来保存更改。从那里我们确定需要添加和删除哪些项目。对于已删除的 ID,我们只需获取要删除的关联项目并将其从用户中删除。对于添加的 ID,我们获取这些实体并将它们添加到用户。当我们调用 SaveChanges
时,更改跟踪会完成剩下的工作,并且由于我们加载了用户及其关联的表单,因此我们可以将该用户发送回具有可用关联的视图引擎。请注意,我们只需要用户 ID 和更新的选择 ID 来执行此操作,而不是传递某种方式的重建实体。如果调用采用准实体,主要是不要将其视为/相信它是一个完整的实体,它只是来自视图的数据的数据容器。