Как имитировать использование последовательностей (из Oracle) с использованием SQL Server и Entity Framework - PullRequest
2 голосов
/ 19 февраля 2012

Нам всегда нравилось использовать последовательность в базах данных Oracle для создания глобально уникальных идентификаторов первичных ключей для всей базы данных. Настолько, что мы будем имитировать то же самое при использовании баз данных SQL Server:

CREATE TABLE MainSequence(
    Id int IDENTITY(1,1) CONSTRAINT pkMainSequence PRIMARY KEY
)

Я пытаюсь перейти на Entity Framework, который для меня очень новый. Казалось, что было бы тривиально создать метод расширения, который я мог бы использовать, чтобы быстро получить следующий доступный глобально уникальный идентификатор.

public static int GetNextId( this Entities db ) {
    var ms = new MainSequence();
    db.MainSequences.AddObject( ms );
    db.SaveChanges( SaveOptions.None );
    return ms.Id;
}

Поскольку это столбец идентификаторов, все, что мне нужно сделать, это добавить новый объект в базу данных и сохранить изменения, чтобы свойство Id было обновлено с реальным значением. Это отлично работает. Но, похоже, у меня возникают проблемы при попытке использовать его для таблиц, связанных с внешними ключами:

var dataId = db.GetNextId();
db.Datas.AddObject( Data.CreateData( dataId, someValueForThisColumn );
db.Caches.AddObject( 
                    Cache.CreateCache( db.GetNextId(),
                    DatabaseMethods.GetOrAddLocation( source.GetLocationText() ),
                    DateTime.Now,
                    dataId ) );

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

System.Data.SqlClient.SqlException: нарушение ограничения PRIMARY KEY 'pkData'. Невозможно вставить дубликат ключа в объект 'dbo.Data'. Значение дубликата ключа (78162). Заявление было прекращено.

Почему-то кажется, что новую строку данных пытаются вставить в базу данных дважды, хотя я не уверен, почему это так. Я подтвердил, что при каждом запуске кода возвращается другой идентификатор MainSequence. Кажется, что вызов db.SaveChanges всякий раз, когда получается новый идентификатор, является проблемой, но нет другого способа, которым можно заполнить реальный int, и я не понимаю, почему это будет проблемой. Что я делаю не так?

1 Ответ

1 голос
/ 20 февраля 2012

Моя проблема - линия db.SaveChanges( SaveOptions.None );.Моя причина использования этой опции состояла в том, чтобы избежать SaveOptions.AcceptAllChangesAfterSave, который, по моему мнению, благодаря информации, полученной из других вопросов StackOverflow (недействительных или неправильно понятых), я думал, предоставит мне базовую форму транзакции базы данных.Я прочитал документацию MSDN несколько раз:

После сохранения изменений вызывается метод AcceptAllChangesAfterSave (), который сбрасывает отслеживание изменений в ObjectStateManager.

Это вменя звучит ужасно, как отслеживание изменений транзакций.Я думал, что если изменения не будут приняты при вызове сохранения, то «транзакция» базы данных будет отменена в случае ошибки (позже я обнаружил, что это не так).Вместо этого все, что это делает, это заставляет ObjectContext по-прежнему считать изменение несохраненным.Поэтому при следующем вызове сохранения несохраненные изменения будут применены повторно.Та-да, исключение первичного ключа. Это также объясняет, почему моя основная последовательность пропускала значения.

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