Чтобы упростить проблему, на 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
Итак, вопрос в том, как мне сделать так, чтобы мне нужно было обновлять только поля, существующие в форме?
Есть три способа Обновление сущности в EF Core. Выберите наиболее подходящий.
@AlexanderPetrov ни один из этих методов не подходит для моего случая использования
@stuartd эта тема совсем не соответствует моему вопросу, и да, я хочу установить значения существующего объекта в моей базе данных, не записывая много строк
Чувак, других подходов нет! Используйте Update
и установите для свойства EntityEntry.State
значение EntityState.Unchanged
для тех свойств, которые не нужно изменять в базе данных.
Я нашел лучший подход, по какой-то причине этот подход не очень хорошо документирован Microsoft, это ответ, который я принял ниже @AlexanderPetrov
Вы можете циклически просматривать свойства формы с отражением, затем в модели искать свойства с тем же именем и типом, а затем назначать их.
Примером этого может быть что-то вроде этого:
//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.
Если вы используете тот же тип, то при отражении вам просто нужно циклически перемещаться по свойствам и назначать их, не проверяя другой тип!
Я нашел ответ, по какой-то причине это плохо документировано 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/…
Вы хотите сказать, что хотите просто установить значения из формы без написания для этого кода? (ps см. stackoverflow.com/a/1148505/43846 относительно
short
)