Как обновить только поля, присутствующие в форме модели в EF Core?

Чтобы упростить проблему, на C# у меня есть такая модель

public class Person
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    [MaxLength(128)]
    public string? Id { get; set; }

    [MaxLength(256)]
    public string PersonName { get; set; }

    public short PersonAge { get; set; }

// ommitted other properties

теперь у меня есть такая форма в ASP Core во внешнем интерфейсе

@model Person


<form action = "/api/new-person">
    <input form = "PersonName"/>
    <button type = "submit"></button>
</form>

Как вы можете видеть, в моей форме есть только PersonName, в моем бэкэнде я хочу иметь возможность обновлять объект Person в моей базе данных только для значения PersonName, потому что это свойство, которое существует в форме, опять же, это упрощенный пример, потому что мой объект огромен и меняется почти все время, не говоря уже о множестве внешних ключей, теперь я не хочу кропотливо устанавливать каждое свойство в серверной части, например

var existingPersonFromDatabase = await _appDbContext.User.FirstOrDefaultAsync(x=>x.Id == formPerson.Id);

existingPersonFromDatabase.PersonName = formPerson.PersonName;

existingPersonFromDatabase.PersonAge = formPerson.PersonAge ;
// + settings 20 more properties one by one

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

Вы хотите сказать, что хотите просто установить значения из формы без написания для этого кода? (ps см. stackoverflow.com/a/1148505/43846 относительно short)

stuartd 27.06.2024 21:09

Есть три способа Обновление сущности в EF Core. Выберите наиболее подходящий.

Alexander Petrov 27.06.2024 21:37

@AlexanderPetrov ни один из этих методов не подходит для моего случая использования

Alizer 28.06.2024 04:36

@stuartd эта тема совсем не соответствует моему вопросу, и да, я хочу установить значения существующего объекта в моей базе данных, не записывая много строк

Alizer 28.06.2024 04:36

Чувак, других подходов нет! Используйте Update и установите для свойства EntityEntry.State значение EntityState.Unchanged для тех свойств, которые не нужно изменять в базе данных.

Alexander Petrov 28.06.2024 04:50

Я нашел лучший подход, по какой-то причине этот подход не очень хорошо документирован Microsoft, это ответ, который я принял ниже @AlexanderPetrov

Alizer 28.06.2024 06:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете циклически просматривать свойства формы с отражением, затем в модели искать свойства с тем же именем и типом, а затем назначать их.

Примером этого может быть что-то вроде этого:

//Copy properties from FormPerson to EntityPerson
var formPerson = new FormPerson();
var entityPerson = new EntityPerson();
formPerson.Name = "Marco";
formPerson.Description = "Ciao come stai ?";

foreach (var property in formPerson.GetType().GetProperties())
{
    var entityProperty = entityPerson.GetType().GetProperty(property.Name);
    if (entityProperty != null)
    {
        var formValue = property.GetValue(formPerson);
        entityProperty.SetValue(entityPerson, formValue);
    }
}

public class FormPerson
{
    public string Name { get; set; }
    public string Description { get; set; }
}

public class EntityPerson
{
    public string Name { get; set; }
    public string Description { get; set; }
}

В противном случае вы можете использовать такую ​​библиотеку, как automapper, которая сделает всю работу за вас с помощью автоматической карты.

Я использую одну и ту же форму для формы и базовой модели ef.

Alizer 28.06.2024 04:31

Если вы используете тот же тип, то при отражении вам просто нужно циклически перемещаться по свойствам и назначать их, не проверяя другой тип!

Marco Ortali 28.06.2024 09:30
Ответ принят как подходящий

Я нашел ответ, по какой-то причине это плохо документировано Microsoft и скрыто под документами, я использовал TryUpdateModelAsync в Controller,

внутри контроллера вы можете иметь

var existingObject = await _appDbContext.Objects.FirstAsync(x => x.Id == 1);

// then we can populate this existing object with the one in the form
await TryUpdateModelAsync(existingObject);

ниже мой полный код

    [HttpPost]
    public async Task<IActionResult> PostNewEmployee(IFormCollection formCollection)
    {
        if (!ModelState.IsValid) // check model validity
        {
            var errors = ModelState
                .Select(x => x.Value)
                .Where(y => y is not null && y.Errors.Count > 0)
                .ToList();


            return BadRequest(JsonConvert.SerializeObject(errors));
        }

        // if the form has an Id then this is an existing person
        if (formCollection.TryGetValue("Id", out var existingId))
        {
            var realId = existingId.ToString(); 

            var existingUser = await _applicationDbContext.Users.FirstOrDefaultAsync(x => x.Id == realId); // get the current existing user
            if (existingUser is not null) // make sure the user exists
            {

                await TryUpdateModelAsync(existingUser); // this will populate the current ``existingUser`` magically with the current model, it will only overwrite fields in the form, it will not touch properties that are NOT in the model

                // update a foreign key
                _applicationDbContext.Entry(existingUser.EmployeeAdditionalData).State = EntityState.Modified;
                
                await _applicationDbContext.SaveChangesAsync();
                return Ok();
            }
        }

Что делает TryUpdateModelAsync, так это то, что он заполняет объект текущими свойствами из модели, перезаписывая только свойства из модели и игнорируя свойства, которые отсутствуют ни в модели, ни в объекте. Он также поддерживает внешние свойства или свойства класса в модели.

Хороший! Ссылка на документацию для ControllerBase.TryUpdateModelAsync: Learn.microsoft.com/en-us/dotnet/api/…

stuartd 28.06.2024 15:15

Другие вопросы по теме