web-dev-qa-db-ja.com

Entity Framework-トランザクション内で「SaveChanges」の前にIDを取得

Entity Framework-「SaveChanges」を呼び出す前に、トランザクション内で新しく作成されたID(ID)を取得する方法はありますか?

2番目の挿入にはIDが必要ですが、常に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();
53
user1948635

IDは、行がテーブルに挿入された後にデータベースによって生成されます。行が挿入される前にその値がどうなるかをデータベースに問い合わせることはできません。

これには2つの方法があります-最も簡単なのはSaveChangesを呼び出すことです。トランザクション内にいるので、IDを取得した後に問題が発生した場合にロールバックできます。

2番目の方法は、データベースの組み込みIDENTITYフィールドを使用せず、自分で実装することです。これは、大量の一括挿入操作がある場合に非常に役立ちますが、代償が伴います-実装するのは簡単ではありません。

編集:SQL Server 2012には、IDENTITY列の代わりに使用できる組み込みのSEQUENCE型があり、自分で実装する必要はありません。

45
zmbq

@zmbqは正しいです。変更の保存を呼び出した後にのみIDを取得できます。

私の提案は、データベースの生成されたIDに頼るべきではないということです。データベースは、アプリケーションの詳細のみであり、不可欠で変更不可能な部分ではありません。

その問題を回避できない場合は、一意であるためGUIDを識別子として使用してください。MSSQLはネイティブ列型としてGUIDをサポートし、高速です(ただし、 INTより高速ではありません。)。

乾杯

6
SeriousM

他の人がすでに指摘しているように、saveChanges()が呼び出される前にデータベースによって生成された増分値にアクセスすることはできません–ただし、idにのみ興味がある場合は別のエンティティへの接続(たとえば、同じトランザクション内)に依存する場合は、 EF Coreによって割り当てられた一時ID

使用されているデータベースプロバイダーに応じて、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.MyEntityId = x.Id

4
B12Toaster

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;
            ....
        }
    }

送信すると、関係が作成され、IDなどについて心配する必要はありません。

4
Armen

このための簡単な回避策は

var ParentRecord = new ParentTable () {
SomeProperty = "Some Value",
AnotherProperty = "Another Property Value"
};

ParentRecord.ChildTable.Add(new ChildTable () {
ChildTableProperty = "Some Value",
ChildTableAnotherProperty = "Some Another Value"
});

db.ParentTable.Add(ParentRecord);

db.SaveChanges();

ParentTableChildTableは、Foreginキーで接続された2つのテーブルです。

0
Hamza Khanzada