Правильно увеличивая значения с помощью Linq to SQL - PullRequest
2 голосов
/ 22 декабря 2008

У меня есть таблица MS SQL, которую я не могу контролировать, и мне нужно писать в нее. Эта таблица имеет первичный ключ int, который не увеличивается автоматически. Я не могу использовать хранимые процедуры, и я хотел бы использовать Linq to SQL, поскольку это упрощает другую обработку.

Мое текущее решение - прочитать последнее значение, увеличить его, попытаться использовать его, если я получу конфликт, увеличить его еще раз и повторить попытку.

Что-то в этом роде:

var newEntity = new Log()
{
    ID = dc.Logs.Max(l => l.ID) + 1,
    Note = "Test"       
};

dc.Logs.InsertOnSubmit(newEntity);

const int maxRetries = 10;
int retries = 0;

bool success = false;
while (!success && retries < maxRetries)
{               
    try
    {                                                           
        dc.SubmitChanges();
        success = true;
    }
    catch (SqlException)
    {
        retries++;
        newEntity.ID = dc.Logs.Max(l => l.ID);                  
    }
}           
if (retries >= maxRetries)
{
    throw new Exception("Bummer...");
}

У кого-нибудь есть лучшее решение?

РЕДАКТИРОВАТЬ: Благодаря Jon , я упростил расчет максимального идентификатора. Я все еще был в режиме мышления SQL.

Ответы [ 4 ]

2 голосов
/ 22 декабря 2008

Это похоже на дорогой способ получить максимальный ID. Вы уже пробовали

var maxId = dc.Logs.Max(s => s.ID);

? Может быть, это не работает по какой-то причине, но я действительно надеюсь это работает ...

(правда, более чем возможно, что SQL Server оптимизирует это соответствующим образом.)

Кроме того, это выглядит хорошо (вонючий, но обязательно так) для меня - но я не эксперт по этому вопросу ...

0 голосов
/ 02 января 2010

Сделать поле идентификатора автоматически увеличивающимся и позволить серверу обрабатывать генерацию идентификатора.

В противном случае вы столкнетесь с проблемой, которую сказал liggett78. Ничто не мешает другому потоку прочитать тот же идентификатор между чтением и отправкой максимального идентификатора для этого потока.

0 голосов
/ 22 декабря 2008

Вы можете поместить всю операцию в транзакцию, используя класс TransactionScope , как показано ниже:

using  (TransactionScope scope = new TransactionScope()){
   var maxId = dc.Logs.Max(s => s.ID); 
   var newEntity = new Log(){        
       ID = maxId,        
       Note = "Test"           
   };
   dc.Logs.InsertOnSubmit(newEntity);
   dc.SubmitChanges();
   scope.Complete();
}  

Поместив извлечение максимального идентификатора и вставку новых записей в одну и ту же транзакцию, вы сможете извлечь вставку без повторных попыток.

Одна из проблем, с которой вы можете столкнуться при использовании этого метода, - это взаимные блокировки транзакций, особенно если таблица интенсивно используется. Проверьте это, чтобы узнать, требуется ли вам дополнительная обработка ошибок.

P.S. Я включил код Джона Скита, чтобы получить максимальный идентификатор в своем коде, потому что я уверен, что он будет работать правильно. :)

0 голосов
/ 22 декабря 2008

Вы не указали, является ли ваше приложение единственным, вставляющим в таблицу. Если это так, тогда я получу максимальное значение один раз сразу после запуска приложения / веб-приложения и буду использовать Interlocked.Increment для него каждый раз, когда вам понадобится следующий идентификатор (или простое добавление, если возможные условия гонки могут быть исключены).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...