Как я уже сказал в своем комментарии выше, в EF Core нет настоящих методов синхронизации. Методы синхронизации (например, SaveChanges
) просто блокируют асинхронные методы (например, SaveChangesAsync
). Таким образом, невозможно, чтобы SaveChanges
работал, если SaveChangesAsync
не работает, поскольку первый просто прокси-сервер для второго. Здесь есть еще одна проблема, которая не очевидна из предоставленного вами кода.
Однако причина, по которой я пишу это в качестве ответа, заключается в том, что в целом вы делаете это неправильно, и я считаю, что, если вы сделаете это правильно, проблема может исчезнуть. Вы никогда не должны, я имею в виду, никогда напрямую не сохранять экземпляр, созданный из тела запроса, непосредственно в вашу базу данных. Это обеспечивает вектор атаки, который позволит злоумышленнику изменить вашу базу данных нежелательными способами. Вы частично решили это, проверив, что идентификатор не был изменен, но пользователь все равно может изменять то, что ему не разрешено.
Помимо этой уязвимости в системе безопасности, есть практическая причина не делать этого таким образом. API служит уровнем защиты от коррупции, но только в том случае, если вы отделяете свою сущность от объекта, с которым взаимодействует клиент. Когда вы используете свою сущность напрямую, вы тесно связываете свою базу данных с уровнем API, так что любое изменение на уровне базы данных требует новой версии вашего API и, что еще хуже, не дает возможности отказаться от предыдущей версии. Все клиенты должны немедленно обновиться, иначе их реализации сломаются. Предоставляя клиенту класс DTO, база данных может развиваться независимо от API, поскольку вы можете добавить любую антикоррупционную логику, необходимую для преодоления разрыва между ними.
Короче говоря, вот как должен быть структурирован ваш метод:
// PUT: api/Persons/5
[HttpPut("{id}")]
public async Task<IActionResult> PutPerson([FromRoute] int id, PersonModel model)
{
// not necessary if using `[ApiController]`
if (!ModelState.IsValid)
return BadRequest();
var person = await _context.People.FindAsync(id);
if (person == null)
return NotFound();
// map `model` onto `person`
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
// use an optimistic concurrency strategy from:
// https://docs.microsoft.com/en-us/ef/core/saving/concurrency#resolving-concurrency-conflicts
}
return NoContent();
}
Я хотел, чтобы код был прямолинейным, но для обработки оптимистичного параллелизма я бы действительно рекомендовал использовать библиотеку обработки исключений Polly. Вы можете настроить политики повторных попыток, которые могут продолжать попытки обновления после исправления ошибок. В противном случае вам нужно будет try / catch внутри try / catch внутри try / catch и т. Д. Кроме того, DbUpdateConcurrencyException
- это то, что вы всегда должны каким-то образом обрабатывать, поэтому повторный бросок не имеет смысла.
person
Chris Pratt
schedule
10.09.2019
SaveChanges
буквально просто вызываетSaveChangesAsync
и блокирует его, пока он не вернется. В EF Core нет настоящего неасинхронного доступа. - person Chris Pratt   schedule 09.09.2019