从视图模型更新时隐藏字段的任何替代方法?我不想在编辑页面中包含所有字段
Any alternative to hidden fields when updating from a viewmodel? I don't want to have all fields in the edit page
我知道有一种“正确”的方法可以做到这一点,但出于某种原因我找不到答案。我什至在微软的指南上看到隐藏字段是要走的路,但感觉“不对”。
当我将所有隐藏字段放入编辑表单时,我发现我的更新工作正常:
<input type="hidden" asp-for="OrgUnits.Organizations" />
<input type="hidden" asp-for="OrgUnits.Address" />
<input type="hidden" asp-for="OrgUnits.AlternateId" />
<input type="hidden" asp-for="OrgUnits.Category" />
<input type="hidden" asp-for="OrgUnits.City" />
<input type="hidden" asp-for="OrgUnits.FriendlyPath" />
<input type="hidden" asp-for="OrgUnits.IsTop" />
<input type="hidden" asp-for="OrgUnits.Name" />
<input type="hidden" asp-for="OrgUnits.NextChildId" />
<input type="hidden" asp-for="OrgUnits.RecoveryOverride" />
<input type="hidden" asp-for="OrgUnits.RowStatus" />
<input type="hidden" asp-for="OrgUnits.RowVersion" />
<input type="hidden" asp-for="OrgUnits.State" />
<input type="hidden" asp-for="OrgUnits.UseAppVersion" />
<input type="hidden" asp-for="OrgUnits.ZipCode" />
但是,这似乎是一种糟糕的代码编写方式。我只希望此 table 中的一些字段为 editable.
这是我的控制器:
public async Task<IActionResult> Edit(string id, [Bind("OrgUnits")] OrgUnitsViewModel orgUnitsViewModel)
{
id = Uri.UnescapeDataString(id);
if (id != orgUnitsViewModel.OrgUnits.OrgUnitId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
//Get org for the DbCatalog
var org = await _opkCoreContext.Organizations.FindAsync(orgUnitsViewModel.OrgUnits.OrgId);
_serverConnectionHelper.SetDatabaseConnectStringToSession(org.DbCatalog);
_opkDataContext.Update(orgUnitsViewModel.OrgUnits);
await _opkDataContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!OrgUnitsExists(orgUnitsViewModel.OrgUnits.OrgUnitId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index), new { currentSearchFilter = orgUnitsViewModel.OrgUnits.OrgUnitId });
}
return View(orgUnitsViewModel);
}
真的应该这样做吗?我走了 AutoMapper 的路线,但这对我来说是失败的,我不太明白如何使用它。无论如何,这是我的错误:
DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.
希望你们中的聪明人知道答案。我很惊讶我在 Google 或 SO 上找不到任何东西,因为我知道这很常见。只是隐藏的 fleids 似乎是错误的,因为如果你错过了一个怎么办?
非常感谢您。
我个人做以下部分更新实体:
如果我不想将整个模型发送到操作以更改整个实体,我会创建一个端点(API 操作)来部分更新实体和 return 成功状态代码看法。我会使用 ajax 请求端点来更改实体而不刷新页面。
这是我用于部分更新 Employee
实体的代码:
Employee.cs
public class Employee
{
public int Id { get; set; }
[Required(ErrorMessage ="Employee name is a required field.")]
[MaxLength(30,ErrorMessage ="Maximum length for the Name is 30 chrachters.")]
public string Name { get; set; }
[Required(ErrorMessage = "Age is a required field.")]
public int Age{ get; set; }
[Required(ErrorMessage = "Position is a required field.")]
[MaxLength(20, ErrorMessage = "Maximum length for the Position is 20 chrachters.")]
public string Position { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
}
EmployeeUpdateDto.cs
public class EmployeeUpdateDto
{
[Required(ErrorMessage = "Employee name is required")]
[MaxLength(30, ErrorMessage = "Maximum length for the Name is 30 characters")]
public string Name { get; set; }
[Range(18, int.MaxValue, ErrorMessage = "Minimum age must be 18")]
public int Age { get; set; }
[Required(ErrorMessage = "Employee position is required")]
[MaxLength(20, ErrorMessage = "Maximum length for the Position is 20 characters")]
public string Position { get; set; }
}
Controller.cs
public class EmployeesController : ControllerBase
{
private IRepositoryManager _repository;
private ILoggerManager _logger;
private IMapper _mapper;
public EmployeesController(IRepositoryManager repository, ILoggerManager logger, IMapper mapper)
{
_repository = repository;
_logger = logger;
_mapper = mapper;
}
[HttpPatch("{id}")]
public async Task<IActionResult> PartiallyUpdateEmployee(int id, JsonPatchDocument<EmployeeUpdateDto> employeePatches)
{
if (employeePatches is null)
{
_logger.LogError("JsonPatchDocument object sent from client is null");
return BadRequest();
}
var employeeEntity = await _repository.EmployeeRepository.GetEmployeeAsync(employeeId, trackChanges:true);
if (employeeEntity null)
{
_logger.LogInfo($"Employee with id {id} doesn't exist in the database.");
return NotFound();
}
var employeeUpdateDto = _mapper.Map<EmployeeUpdateDto>(employeeEntity);
employeePatches.ApplyTo(employeeUpdateDto, ModelState);
TryValidateModel(employeeUpdateDto);
if (!ModelState.IsValid)
{
_logger.LogError("invalid model state for the patch document");
return UnprocessableEntity(ModelState);
}
_mapper.Map(employeeUpdateDto, employeeEntity);
await _repository.SaveAsync();
return NoContent();
}
//other action methods
}
您必须按照以下标准补丁格式 (json) 发送您的请求正文:
[
{ "op": "replace", "path": "/name", "new_name": "new name" },
{ "op": "remove", "path": "/position" }
]
就是这样。上面的示例请求会将 Employee 名称更改为“new_name”并将 Position 设置为其默认值(在本例中为 null)。
以上示例需要这些先决条件才能工作:
Microsoft.AspNetCore.JsonPatch支持JsonPatchDocument
类型。
Microsoft.AspNetCore.Mvc.NewtonsoftJson支持到JsonPatchDocument<T>
的映射请求。在 ConfigureServices()
方法中配置:
services.AddControllersWithViews
.AddNewtonsoftJson();
AutoMapper.Extensions.Microsoft.DependencyInjection 将 EmployeeUpdateDto 映射到 Employee。添加映射配置文件 class 并在 ConfigureServices()
方法中配置 AutoMapper:
services.AddAutoMapper(typeof(Startup));
和
public class MappingpProfile : Profile
{
public MappingpProfile()
{
CreateMap<CompanyUpdateDto, Company>();
CreateMap<CompanyCreationDto, Company>();
CreateMap<Employee, EmployeeDto>();
CreateMap<EmployeeCreationDto, Employee>();
CreateMap<EmployeeUpdateDto, Employee>().ReverseMap();
}
}
在上面的代码中,我们使用 CreateMap<EmployeeUpdateDto, Employee>().ReverseMap();
来满足我们的需要。
我知道有一种“正确”的方法可以做到这一点,但出于某种原因我找不到答案。我什至在微软的指南上看到隐藏字段是要走的路,但感觉“不对”。
当我将所有隐藏字段放入编辑表单时,我发现我的更新工作正常:
<input type="hidden" asp-for="OrgUnits.Organizations" />
<input type="hidden" asp-for="OrgUnits.Address" />
<input type="hidden" asp-for="OrgUnits.AlternateId" />
<input type="hidden" asp-for="OrgUnits.Category" />
<input type="hidden" asp-for="OrgUnits.City" />
<input type="hidden" asp-for="OrgUnits.FriendlyPath" />
<input type="hidden" asp-for="OrgUnits.IsTop" />
<input type="hidden" asp-for="OrgUnits.Name" />
<input type="hidden" asp-for="OrgUnits.NextChildId" />
<input type="hidden" asp-for="OrgUnits.RecoveryOverride" />
<input type="hidden" asp-for="OrgUnits.RowStatus" />
<input type="hidden" asp-for="OrgUnits.RowVersion" />
<input type="hidden" asp-for="OrgUnits.State" />
<input type="hidden" asp-for="OrgUnits.UseAppVersion" />
<input type="hidden" asp-for="OrgUnits.ZipCode" />
但是,这似乎是一种糟糕的代码编写方式。我只希望此 table 中的一些字段为 editable.
这是我的控制器:
public async Task<IActionResult> Edit(string id, [Bind("OrgUnits")] OrgUnitsViewModel orgUnitsViewModel)
{
id = Uri.UnescapeDataString(id);
if (id != orgUnitsViewModel.OrgUnits.OrgUnitId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
//Get org for the DbCatalog
var org = await _opkCoreContext.Organizations.FindAsync(orgUnitsViewModel.OrgUnits.OrgId);
_serverConnectionHelper.SetDatabaseConnectStringToSession(org.DbCatalog);
_opkDataContext.Update(orgUnitsViewModel.OrgUnits);
await _opkDataContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!OrgUnitsExists(orgUnitsViewModel.OrgUnits.OrgUnitId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index), new { currentSearchFilter = orgUnitsViewModel.OrgUnits.OrgUnitId });
}
return View(orgUnitsViewModel);
}
真的应该这样做吗?我走了 AutoMapper 的路线,但这对我来说是失败的,我不太明白如何使用它。无论如何,这是我的错误:
DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.
希望你们中的聪明人知道答案。我很惊讶我在 Google 或 SO 上找不到任何东西,因为我知道这很常见。只是隐藏的 fleids 似乎是错误的,因为如果你错过了一个怎么办?
非常感谢您。
我个人做以下部分更新实体:
如果我不想将整个模型发送到操作以更改整个实体,我会创建一个端点(API 操作)来部分更新实体和 return 成功状态代码看法。我会使用 ajax 请求端点来更改实体而不刷新页面。
这是我用于部分更新 Employee
实体的代码:
Employee.cs
public class Employee
{
public int Id { get; set; }
[Required(ErrorMessage ="Employee name is a required field.")]
[MaxLength(30,ErrorMessage ="Maximum length for the Name is 30 chrachters.")]
public string Name { get; set; }
[Required(ErrorMessage = "Age is a required field.")]
public int Age{ get; set; }
[Required(ErrorMessage = "Position is a required field.")]
[MaxLength(20, ErrorMessage = "Maximum length for the Position is 20 chrachters.")]
public string Position { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
}
EmployeeUpdateDto.cs
public class EmployeeUpdateDto
{
[Required(ErrorMessage = "Employee name is required")]
[MaxLength(30, ErrorMessage = "Maximum length for the Name is 30 characters")]
public string Name { get; set; }
[Range(18, int.MaxValue, ErrorMessage = "Minimum age must be 18")]
public int Age { get; set; }
[Required(ErrorMessage = "Employee position is required")]
[MaxLength(20, ErrorMessage = "Maximum length for the Position is 20 characters")]
public string Position { get; set; }
}
Controller.cs
public class EmployeesController : ControllerBase
{
private IRepositoryManager _repository;
private ILoggerManager _logger;
private IMapper _mapper;
public EmployeesController(IRepositoryManager repository, ILoggerManager logger, IMapper mapper)
{
_repository = repository;
_logger = logger;
_mapper = mapper;
}
[HttpPatch("{id}")]
public async Task<IActionResult> PartiallyUpdateEmployee(int id, JsonPatchDocument<EmployeeUpdateDto> employeePatches)
{
if (employeePatches is null)
{
_logger.LogError("JsonPatchDocument object sent from client is null");
return BadRequest();
}
var employeeEntity = await _repository.EmployeeRepository.GetEmployeeAsync(employeeId, trackChanges:true);
if (employeeEntity null)
{
_logger.LogInfo($"Employee with id {id} doesn't exist in the database.");
return NotFound();
}
var employeeUpdateDto = _mapper.Map<EmployeeUpdateDto>(employeeEntity);
employeePatches.ApplyTo(employeeUpdateDto, ModelState);
TryValidateModel(employeeUpdateDto);
if (!ModelState.IsValid)
{
_logger.LogError("invalid model state for the patch document");
return UnprocessableEntity(ModelState);
}
_mapper.Map(employeeUpdateDto, employeeEntity);
await _repository.SaveAsync();
return NoContent();
}
//other action methods
}
您必须按照以下标准补丁格式 (json) 发送您的请求正文:
[
{ "op": "replace", "path": "/name", "new_name": "new name" },
{ "op": "remove", "path": "/position" }
]
就是这样。上面的示例请求会将 Employee 名称更改为“new_name”并将 Position 设置为其默认值(在本例中为 null)。
以上示例需要这些先决条件才能工作:
Microsoft.AspNetCore.JsonPatch支持
JsonPatchDocument
类型。Microsoft.AspNetCore.Mvc.NewtonsoftJson支持到
JsonPatchDocument<T>
的映射请求。在ConfigureServices()
方法中配置:services.AddControllersWithViews .AddNewtonsoftJson();
AutoMapper.Extensions.Microsoft.DependencyInjection 将 EmployeeUpdateDto 映射到 Employee。添加映射配置文件 class 并在
ConfigureServices()
方法中配置 AutoMapper:services.AddAutoMapper(typeof(Startup));
和
public class MappingpProfile : Profile
{
public MappingpProfile()
{
CreateMap<CompanyUpdateDto, Company>();
CreateMap<CompanyCreationDto, Company>();
CreateMap<Employee, EmployeeDto>();
CreateMap<EmployeeCreationDto, Employee>();
CreateMap<EmployeeUpdateDto, Employee>().ReverseMap();
}
}
在上面的代码中,我们使用 CreateMap<EmployeeUpdateDto, Employee>().ReverseMap();
来满足我们的需要。