web-dev-qa-db-fra.com

Entity Framework - Récupérer l'identifiant avant 'SaveChanges' dans une transaction

Dans Entity Framework - Y a-t-il un moyen de récupérer un ID (identité) nouvellement créé dans une transaction avant d'appeler 'SaveChanges'?

J'ai besoin de l'ID pour une deuxième insertion, mais il est toujours renvoyé sous la forme 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();
50
user1948635

L'ID est généré par la base de données une fois la ligne insérée dans la table. Vous ne pouvez pas demander à la base de données quelle sera cette valeur avant l'insertion de la ligne.

Vous avez deux solutions: le plus simple serait d’appeler SaveChanges. Puisque vous êtes dans une transaction, vous pouvez annuler en cas de problème une fois que vous avez obtenu l'ID.

La deuxième solution consisterait à ne pas utiliser les champs IDENTITY de la base de données mais à les implémenter vous-même. Cela peut être très utile lorsque vous avez beaucoup d'opérations d'insertion en bloc, mais cela vient avec un prix - sa mise en œuvre n'est pas triviale.

EDIT: SQL Server 2012 possède un type SEQUENCE intégré qui peut être utilisé à la place d'une colonne IDENTITY. Il n'est pas nécessaire de l'implémenter vous-même.

43
zmbq

@zmbq a raison, vous ne pouvez obtenir l'identifiant qu'après avoir appelé les changements de sauvegarde.

Ma suggestion est que vous ne devriez PAS vous fier aux identifiants générés de la base de données . La base de données ne devrait contenir qu'un détail de votre application, et non une partie intégrante et non modifiable.

Si vous ne parvenez pas à résoudre ce problème, utilisez un GUID comme identifiant car il est unique . MSSQL supporte GUID en tant que type de colonne natif et est rapide (bien que pas plus rapide que INT.).

À votre santé

7
SeriousM

Si votre entité tblTest est connectée à d'autres entités que vous souhaitez attacher, vous n'avez pas besoin de l'identifiant pour créer la relation. Disons que tblTest est attaché à un autre objet, comme dans l'objet anotherTest, vous avez les propriétés tblTest et tblTestId. Dans ce cas, vous pouvez avoir ce code:

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

Après la soumission, la relation serait créée et vous n'avez pas à vous soucier des identifiants, etc.

4
Armen

Un moyen simple de contourner ce problème serait

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();

ParentTable et ChildTable sont deux tables connectées à une clé étrangère.

0
Hamza Khanzada

Comme d'autres l'ont déjà souligné, vous n'avez pas accès à la valeur d'incrément générée par la base de données avant l'appel de saveChanges(). Toutefois, si vous êtes uniquement intéressé par la id comme moyen d'établir une connexion avec une autre entité ) alors vous pouvez également vous fier aux identifiants temporaires attribués par EF Core :

Selon le fournisseur de base de données utilisé, des valeurs peuvent être générées côté client par EF ou dans la base de données. Si la valeur est générée par la base de données, EF peut alors affecter une valeur temporaire lorsque vous ajoutez l'entité au contexte. Cette valeur temporaire sera alors remplacée par la valeur générée par la base de données lors de SaveChanges ().

Voici un exemple pour montrer comment cela fonctionne. Dites que MyEntity est référencé par MyOtherEntity via la propriété MyEntityId qui doit être affectée avant que saveChanges soit appelé.

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

Ce qui précède fonctionne également lorsque attribuer des références via des propriétés de navigation , c.-à-d. y.MyEntity = x au lieu de y.MyEntityId = x.Id

0
B12Toaster