Код: Выделить всё
using var context = new PersonContext();
// Fetch a person from database and change phone number
var person = context.People.Single(p => p.PersonId == 1);
person.PhoneNumber = "555-555-5555";
// Change the person's name in the database to simulate a concurrency conflict
context.Database.ExecuteSqlRaw(
"UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1");
var saved = false;
while (!saved)
{
try
{
// Attempt to save changes to the database
context.SaveChanges();
saved = true;
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
if (entry.Entity is Person)
{
var proposedValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
foreach (var property in proposedValues.Properties)
{
var proposedValue = proposedValues[property];
var databaseValue = databaseValues[property];
// TODO: decide which value should be written to database
// proposedValues[property] = ;
}
// Refresh original values to bypass next concurrency check
entry.OriginalValues.SetValues(databaseValues);
}
else
{
throw new NotSupportedException(
"Don't know how to handle concurrency conflicts for "
+ entry.Metadata.Name);
}
}
}
}
Код: Выделить всё
public WeatherForecast UpdateMeasurementsForToday()
{
var saved = false;
var date = DateTime.Today;
var specified = DateTime.SpecifyKind(date, DateTimeKind.Utc);
while (!saved)
{
try
{
var entityFromDb = _source.GetWeatherByDate(specified);
entityFromDb.TemperatureC = Random.Shared.Next(30);
_source.UpdateEntity(entityFromDb);
saved = true;
return entityFromDb;
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
if (entry.Entity is WeatherForecast)
{
var proposedValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
if (databaseValues is null)
throw new ArgumentException("Сущность была удалена");
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(databaseValues);
}
else
{
throw new NotSupportedException(
"Don't know how to handle concurrency conflicts for "
+ entry.Metadata.Name);
}
}
}
}
throw new ArgumentException();
}
Код: Выделить всё
public void UpdateEntity(WeatherForecast newEntity)
{
var entityFromDb = _ctx.Forecasts.First(c => c.Id.Equals(newEntity.Id));
entityFromDb.TemperatureC = newEntity.TemperatureC;
entityFromDb.SummaryUpdates++;
entityFromDb.Version = Guid.NewGuid();
_ctx.SaveChanges();
}
Не могу мы просто возвращаемся и запрашиваем НОВЫЕ данные (я пробовал этот способ, и он действительно не работал должным образом, цикл бесконечно выбрасывал DbConcurrencyUpdateEx, я думаю, это связано с тем, что EF Core кэширует данные).
Другая проблема заключается в том, что нам может понадобиться ситуация, когда нам нужны предлагаемые нами значения и НОВЫЕ значения из базы данных для разрешения конфликта, поскольку наши предлагаемые значения основаны на старых, логически это выглядит так: если нам просто нужно написать
Код: Выделить всё
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(databaseValues);
Но в документации мы опять подробно разбираем свойства
Тоже наверное глупый вопрос со стороны, но я искренне не понимаю, почему мой тест на конкурентоспособность нестабилен:
Код: Выделить всё
public async void UpdateWeather_Updatingsimultaneously_SummaryShouldConsiderUpdateCounters()
{
var rep1 = new EfWeatherRepository(_ctx1);
var rep2 = new EfWeatherRepository(efDbInitializer.Ctx);
var weatherService1 = new WeatherService(rep1, _fakeService.Object);
var weatherService2 = new WeatherService(rep2, _fakeService.Object);
var createdEntity = weatherService1.MeasureTodayWeather();
var task1 = Task.Run(() => weatherService1.UpdateMeasurementsForToday());
var task2 = Task.Run(() => weatherService2.UpdateMeasurementsForToday());
await Task.WhenAll(task1, task2);
var result = rep1.GetEntityById(createdEntity.Id);
result.SummaryUpdates.Should().Be(2);
}
Спасибо, что уделили время!
Я использовал код из документации MS
Подробнее здесь: https://stackoverflow.com/questions/785 ... ess-issues