Защита критических разделов на основе условия в C#
Я имею дело с курьезным сценарием.
Я использую EntityFramework для сохранения (вставки / обновления) в базу данных SQL в многопоточной среде. Проблема в том, что мне нужно получить доступ к базе данных, чтобы увидеть, был ли уже создан регистр с определенным ключом, чтобы установить значение поля (выполняется), или он является новым для установки другого значения (в ожидании). Эти регистры идентифицируются уникальным путеводителем.
Я решил эту проблему, установив блокировку, поскольку знаю, что сущность не будет присутствовать ни в одном другом процессе, иными словами, у меня не будет одинакового guid в разных процессах, и, похоже, он работает нормально. Это выглядит примерно так:
static readonly object LockableObject = new object();
static void SaveElement(Entity e)
{
lock(LockableObject)
{
Entity e2 = Repository.FindByKey(e);
if (e2 != null)
{
Repository.Insert(e2);
}
else
{
Repository.Update(e2);
}
}
}
Но это подразумевает, что когда у меня огромное количество запросов на сохранение, они будут поставлены в очередь.
Интересно, есть ли что-то подобное (пожалуйста, примите это просто как идею):
static void SaveElement(Entity e)
{
(using ThisWouldBeAClassToProtectBasedOnACondition protector = new ThisWouldBeAClassToProtectBasedOnACondition(e => e.UniqueId)
{
Entity e2 = Repository.FindByKey(e);
if (e2 != null)
{
Repository.Insert(e2);
}
else
{
Repository.Update(e2);
}
}
}
Идея заключалась бы в том, чтобы иметь своего рода защиту, защищенную на основе условия, поэтому каждый объект e будет иметь свою собственную блокировку на основе свойства e.UniqueId.
Любая идея?
2 ответа
Не используйте блокировки приложений там, где необходимы транзакции или ограничения базы данных.
Использование блокировки для предотвращения дублирования записей в базе данных не очень хорошая идея. Это ограничивает масштабируемость вашего приложения, заставляя когда-либо существовать только один экземпляр, который может добавлять или обновлять такие записи. Или, что еще хуже, кто-то в конечном итоге попытается масштабировать приложение для нескольких процессов или серверов, и это приведет к повреждению данных (поскольку блокировки являются локальными для одного процесса).
Вместо этого вам следует рассмотреть использование комбинации уникальных ограничений в базе данных и транзакциях, чтобы гарантировать, что никакие две попытки добавить одну и ту же запись не могут быть успешными одновременно. Один будет успешным - другой будет вынужден откат.
Это может сработать для вас, вы можете просто заблокировать экземпляр e:
lock(e)
{
Entity e2 = Repository.FindByKey(e);
if (e2 != null)
{
Repository.Insert(e2);
}
else
{
Repository.Update(e2);
}
}