Любая альтернатива скрытым полям при обновлении из модели просмотра? Я не хочу, чтобы на странице редактирования были все поля - PullRequest
0 голосов
/ 11 июля 2020

Я знаю, что есть способ сделать это «правильным» способом, но по какой-то причине я не могу найти ответа. Я даже видел в руководстве Microsoft, что скрытые поля - это путь к go, но это кажется «неправильным».

Я обнаружил, что мое обновление работает нормально, когда я помещаю все скрытые поля в форму редактирования:

    <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" />

Но похоже, что это плохой способ писать код. Я хочу, чтобы только несколько полей в этой таблице были доступны для редактирования.

Вот мой контроллер:

    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, потому что знаю, что это очень часто. Просто скрытые флиды кажутся такими неправильными, потому что что, если вы пропустите одну?

Заранее большое спасибо.

1 Ответ

2 голосов
/ 11 июля 2020

Я лично делаю следующее Для частичного обновления объекта:

Если я не хочу отправлять всю модель в действие для изменения всего объекта, я бы сделал конечную точку (действие API), которая частично обновляет объект и вернуть код состояния успеха вместо просмотра. Я бы использовал запрос 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" } 
]

Вот и все. приведенный выше пример запроса изменит имя сотрудника на «new_name» и установит для Position значение по умолчанию (в данном случае null).

В приведенном выше примере для работы требуются следующие предварительные условия:

enter image description here

  • Microsoft.AspNetCore.JsonPatch to support JsonPatchDocument type.

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson to support mapping request to JsonPatchDocument. Configure this in ConfigureServices() method:

    services.AddControllersWithViews .AddNewtonsoftJson();

  • AutoMapper.Extensions.Microsoft.DependencyInjection to map EmployeeUpdateDto to Employee. Add a mapping profile class and configure AutoMapper in ConfigureServices() method:

    services.AddAutoMapper(typeof(Startup));
    

and

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(); для наших нужд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...