Ваша проблема действительна - на часто используемом веб-сайте это может произойти, и решение не очень простое. Вы можете использовать клиентскую сторону Guid
, как описано в @Mikecito, но она значительно снижает производительность, и я думаю, вы не хотите ее использовать.
То, как вы делаете это в данный момент, очень плохо, потому что единственное решение - заключить ваш код в одну сериализуемую транзакцию - транзакция должна содержать как выбор Id, так и сохранение записи. Это сделает доступ к вашей InventoryObjects
последовательной, потому что каждый выбор max будет блокировать всю таблицу до фиксации транзакции - никто больше не сможет читать или записывать данные в таблицу во время транзакции вставки. Это не должно быть проблемой на редко посещаемых сайтах, но на часто посещаемых сайтах это НЕ БУДЕТ. Невозможно сделать это по-другому в вашей текущей настройке.
Частичное улучшение заключается в использовании отдельной таблицы для хранения максимального значения + хранимой процедуры, чтобы получить следующее значение и увеличить хранимое значение в атомарной операции - (на самом деле она имитирует последовательности из Oracle). Теперь единственное осложнение, если вам нужна последовательность без пробелов. Например, если что-то пойдет не так с сохранением нового InventoryObject
, выбранный идентификатор будет потерян, и это создаст пробел в последовательности идентификатора. Если вам нужна последовательность без пробелов, вы должны снова использовать транзакцию, чтобы получить следующий Id и сохранить запись, но на этот раз вы заблокируете только одну запись в таблице последовательности. Извлечение идентификатора из таблицы последовательности должно быть максимально приближено к сохранению изменений, чтобы минимизировать время, когда запись последовательности заблокирована.
Вот пример таблицы последовательности и процедуры последовательности для сервера SQL:
CREATE TABLE [dbo].[Sequences]
(
[SequenceType] VARCHAR(20) NOT NULL, /* Support for multiple sequences */
[Value] INT NOT NULL
)
CREATE PROCEDURE [dbo].[GetNextSequenceValue]
@SequenceType VARCHAR(20)
AS
BEGIN
DECLARE @Result INT
UPDATE [dbo].[Sequences] WITH (ROWLOCK, UPDLOCK)
SET @Result = Value = Value + 1
WHERE SequenceType = @SequenceType
RETURN @Result
END
Таблица не должна сначала отображаться кодом - вы никогда не получите к ней доступ напрямую. Вы должны создать собственный инициализатор базы данных, чтобы добавить таблицу и хранимую процедуру для вас, когда EF создает базу данных. Вы можете попробовать подобный подход как , описанный здесь . Вы также должны добавить запись инициализации для вашей последовательности с начальным значением.
Теперь вам нужно только вызвать хранимую процедуру, чтобы получить значение, прежде чем вы собираетесь сохранить запись:
// Prepare and insert record here
// Transaction is needed only if you don't want gaps
// This whole can be actually moved to overriden SaveChanges in your context
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
record.Id = context.Database.ExecuteStoreCommand("dbo.GetNextSequenceValue @SequenceType",
new SqlParameter("SequenceType", "InventoryObjects"));
context.SaveChanges();
}