Настраивается пользовательское ограничение в EF, проблема асинхронности - PullRequest
0 голосов
/ 18 марта 2012

У меня есть такое действие контроллера (ASP.NET web api)

public HttpResponseMessage<Component> Post(Component c)
{
    //Don't allow equal setup ids in within the same installation when the component is of type 5
    if (db.Components.Any(d => d.InstallationId == c.InstallationId && d.SetupId == c.SetupId && d.ComponentTypeId == 5)) return new HttpResponseMessage<Component>(c, HttpStatusCode.Conflict);

    db.Components.Add(c);
    db.SaveChanges();
    return new HttpResponseMessage<Component>(c, HttpStatusCode.OK);
}      

Я отправляю запрос количества сообщений из javascript, причем 2 из них равны

{SetupId : 7, InstallationId : 1, ComponentTypeId: 5}

Я проверил это как с помощью fiddler, так и пошагово просматривая код на сервере.

Однако иногда ограничение, которое я выполняю выше, работает так, как должно, а в других случаях это не так.Я предполагаю, что поскольку Post является асинхронным действием, запрос № 2 иногда проверяет базу данных на наличие дубликатов ДО того, как первый запрос удалось сохранить в базе данных.

Как я могу решить эту проблему?Есть ли способ заблокировать операции EF в базе данных от начала действия post до конца?Это даже хорошая идея?

Однако у меня есть ограничения базы данных, так как это только когда компонент typetype равен 5, тогда я не уверен, как это реализовать.Или, если это вообще возможно.

Ответы [ 2 ]

0 голосов
/ 18 марта 2012

Я решил это в базе данных с помощью этого ответа: https://stackoverflow.com/a/5149263/94394 Условное ограничение разрешено в SQL Server 2008.

create unique nonclustered index [funcix_Components_setupid_installationid_RecordStatus]
on [components]([setupid], [Installationid])
where [componenttypeid] = 5

Затем я перехватил DbUpdateException и проверил, получил ли я код ошибки исключения ограничения

        try
        {
            db.Components.Add(c);
            db.SaveChanges();                
        }
        catch (DbUpdateException ex) {
            Exception innermostException = ex;
            while (innermostException.InnerException != null)//Get innermost exception
            {
                innermostException = innermostException.InnerException;
            }

            if (((System.Data.SqlClient.SqlException)innermostException).Number == 2601)//Constraint exception id
            {
                return new HttpResponseMessage<Component>(c, HttpStatusCode.Conflict);    
            }
        }
        return new HttpResponseMessage<Component>(c, HttpStatusCode.OK);
0 голосов
/ 18 марта 2012

Этого довольно сложно достичь с помощью EF.В обычном SQL вы запускаете транзакцию и добавляете некоторую табличную подсказку в запрос ограничения для принудительной блокировки записей.Проблема в том, что EF не поддерживает табличные подсказки.Вы не можете заставить запрос linq или ESQL заблокировать запись.

Ваши варианты:

  • Ручная блокировка в вашем методе.Например, использование lock значительно снизит пропускную способность вашего метода, поэтому вам, скорее всего, понадобится некоторая настраиваемая умная блокировка реализации для этих идентификаторов
  • Использование настраиваемого SQL или хранимой процедуры вместо запроса LINQ и принудительной блокировки.Я думаю, что UPDLOCK с HOLDLOCK подсказками, вероятно, должно работать в этом случае.
  • В качестве альтернативы вы можете поместить уникальный индекс в ваши InstallationId, SetupId и ComponentTypeId и просто перехватить исключение, если параллельный запроспытается вставить дубликат записи.Проблема в том, что эта комбинация должна быть уникальной только для одних случаев, но не для других.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...