Подтвердить что ты не робот

Entity Framework - получить идентификатор до "SaveChanges" внутри транзакции

В Entity Framework. Есть ли способ получить вновь созданный идентификатор (идентификатор) внутри транзакции до вызова "SaveChanges"?

Мне нужен идентификатор для второй вставки, однако он всегда возвращается как 0...

        ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;

        objectContext.Connection.Open();

        using (var transaction = objectContext.Connection.BeginTransaction())
        {
            foreach (tblTest entity in saveItems)
            {
                this.context.Entry(entity).State = System.Data.EntityState.Added;
                this.context.Set<tblTest>().Add(entity);

                int testId = entity.TestID;

                .... Add another item using testId
            }

            try
            {
                context.SaveChanges();
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                objectContext.Connection.Close();
                throw ex;
            }
        }

        objectContext.Connection.Close();
4b9b3361

Ответ 1

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

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

Второй способ - не использовать базу данных, встроенную в поля IDENTITY, а реализовать их самостоятельно. Это может быть очень полезно, когда у вас много операций с объемной вставкой, но она поставляется с ценой - ее нетривиально реализовать.

EDIT: SQL Server 2012 имеет встроенный тип SEQUENCE, который можно использовать вместо столбца IDENTITY, без необходимости его реализовать.

Ответ 2

@zmbq прав, вы можете получить идентификатор только после вызова изменений.

Мое предложение состоит в том, что вы НЕ должны полагаться на сгенерированный идентификатор базы данных. База данных должна содержать только деталь вашего приложения, а не неотъемлемую и неизменную часть.

Если вы не можете обойти эту проблему, используйте идентификатор GUID в качестве идентификатора из-за его уникальности. MSSQL поддерживает GUID как родной тип столбца и быстро (хотя и не быстрее INT.).

Приветствия

Ответ 3

Если ваш объект tblTest подключен к другим объектам, которые вы хотите присоединить, вам не нужно, чтобы Id создавал отношение. Допустим, что tblTest привязан к объекту anotherTest, так как в объекте anotherTest у вас есть объекты tblTest и свойства tblTestId, в этом случае вы можете получить этот код:

using (var transaction = objectContext.Connection.BeginTransaction())
    {
        foreach (tblTest entity in saveItems)
        {
            this.context.Entry(entity).State = System.Data.EntityState.Added;
            this.context.Set<tblTest>().Add(entity);

            anotherTest.tblTest = entity;
            ....
        }
    }

После отправки отношения будет создано, и вам не нужно беспокоиться об идентификаторах и т.д.

Ответ 4

Как уже отмечали другие, у вас нет доступа к значению приращения, сгенерированному базой данных до saveChanges() - однако, если вас интересует только id как средство для установления соединения с другим объектом (например, в та же транзакция), то вы также можете рассчитывать на временные идентификаторы, назначенные EF Core:

В зависимости от используемого поставщика базы данных значения могут быть сгенерированы на стороне клиента EF или в базе данных. Если значение генерируется базой данных, тогда EF может назначить временное значение при добавлении объекта в контекст. Это временное значение будет затем заменено значением, сгенерированным базой данных, во время SaveChanges().

Вот пример, чтобы продемонстрировать, как это работает. Скажем, MyEntity ссылается на MyOtherEntity через свойство MyEntityId которое необходимо назначить до saveChanges.

var x = new MyEntity();        // x.Id = 0
dbContext.Add(x);              // x.Id = -2147482624 <-- EF Core generated id
var y = new MyOtherEntity();   // y.Id = 0
dbContext.Add(y);              // y.Id = -2147482623 <-- EF Core generated id
y.MyEntityId = x.Id;           // y.MyEntityId = -2147482624
dbContext.SaveChangesAsync();
Debug.WriteLine(x.Id);         // 1261 <- EF Core replaced temp id with "real" id
Debug.WriteLine(y.MyEntityId); // 1261 <- reference also adjusted by EF Core

y.MyEntity = x также работает при назначении ссылок с помощью навигационных свойств, т. y.MyEntity = x вместо y.MyEntityId = x.Id