Jak mogę uzyskać identyfikator wstawionego obiektu w ramach Entity? [Zamknięte]

611

Mam problem z Entity Framework w Asp.net. Chcę uzyskać wartość Id za każdym razem, gdy dodam obiekt do bazy danych. W jaki sposób mogę to zrobić?

Ahmet
źródło
16
Odpowiedź Ladislava Mrnki poniżej jest poprawną odpowiedzią i jako taka powinna zostać zaakceptowana.
CShark
3
PO opublikował tylko raz na swoim koncie, pytanie to jest bardziej szczegółowe. To zapewniło mu prawie 2k reputacji (!), Odpowiedź nie jest oznaczona jako zaakceptowana, ponieważ konto zostało porzucone od tego dnia.
MurariAlex,
1
Jeśli potrzebujesz tylko identyfikatora, aby użyć go w relacji z kluczem obcym, możesz zamiast tego rozważyć ustawienie właściwości nawigacyjnej encji zależnej na poprzednio dodaną encję. W ten sposób nie musisz zawracać sobie głowy dzwonieniem SaveChangesod razu, aby uzyskać identyfikator. Więcej informacji tutaj .
B12Toaster
W przypadku Entity Framework Core 3.0 dodaj parametr acceptAllChangesOnSuccess jako true: czekaj _context.SaveChangesAsync (true);
Mike

Odpowiedzi:

1005

To całkiem proste. Jeśli używasz DB wygenerowane identyfikatory (jak IDENTITYw MS SQL) wystarczy dodać jednostce ObjectSeti SaveChangesna związane ObjectContext. Idzostaną automatycznie wypełnione dla Ciebie:

using (var context = new MyContext())
{
  context.MyEntities.Add(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Yes it's here
}

Entity Framework domyślnie następujące każda INSERTze SELECT SCOPE_IDENTITY()gdy auto generowane Idsą wykorzystywane s.

Ladislav Mrnka
źródło
34
Więc zadajesz złe pytanie. Jeśli masz problem z wyjątkiem, powinieneś zadać pytanie pokazujące twój wyjątek (i wyjątek wewnętrzny) oraz fragment kodu powodujący ten błąd.
Ladislav Mrnka
3
Upewnij się, że dodawana jednostka jest poprawną jednostką, np. Wszystko z ograniczeniem bazy danych „nie zerowa” musi zostać wypełnione itp.
fran
4
@LadislavMrnka: Co z właściwościami nawigacyjnymi nowo utworzonego obiektu? Wygląda na to, że nie zapełniają się automatycznie po zapisaniu zmian.
Isaac Kleinman,
4
Rozważmy następujący scenariusz: table1 ma wygenerować tożsamość @@, aby użyć jej w table2. Zarówno tabela1, jak i tabela2 powinny znajdować się w tej samej transakcji, np. Jedna operacja SaveChange musi zapisać dane obu tabel. @@ tożsamość nie zostanie wygenerowana, dopóki nie zostanie wywołane polecenie „SaveChanges”. jak możemy rozwiązać tę sytuację?
Arash
7
@Djeroen: Jeśli używasz identyfikatora wygenerowanego przez bazę danych, musisz najpierw wstawić te obiekty do bazy danych. Jeśli potrzebujesz ID przed wstawieniem, musisz zbudować własną logikę, aby uzyskać unikalne ID przed wstawieniem danych i nie używaj kolumny tożsamości w bazie danych.
Ladislav Mrnka
124

Korzystałem z odpowiedzi Ladislava Mrnki, aby z powodzeniem odzyskać ID podczas korzystania z Entity Framework, ale piszę tutaj, ponieważ nie wykorzystałem go (tj. Nie wykorzystałem go) i pomyślałem, że opublikuję tutaj moje odkrycia w na wypadek, gdyby ludzie chcieli „rozwiązać” problem, który miałem.

Rozważ obiekt zamówienia, który ma relację klucza obcego z klientem. Kiedy jednocześnie dodałem nowego klienta i nowe zamówienie, robiłem coś takiego;

var customer = new Customer(); //no Id yet;
var order = new Order(); //requires Customer.Id to link it to customer;
context.Customers.Add(customer);
context.SaveChanges();//this generates the Id for customer
order.CustomerId = customer.Id;//finally I can set the Id

Jednak w moim przypadku nie było to wymagane, ponieważ miałem relację klucza obcego między klientem i identyfikatorem zamówienia a klientem

Musiałem tylko to;

var customer = new Customer(); //no Id yet;
var order = new Order{Customer = customer}; 
context.SaveChanges();//adds customer.Id to customer and the correct CustomerId to order

Teraz, gdy zapiszę zmiany, identyfikator generowany dla klienta jest również dodawany do zamówienia. Nie potrzebuję dodatkowych kroków

Wiem, że to nie odpowiada na pierwotne pytanie, ale pomyślałem, że może to pomóc programistom, którzy są nowicjuszami w EF, nadużywać najczęściej głosowanej odpowiedzi na coś, co może nie być wymagane.

Oznacza to również, że aktualizacje są ukończone w ramach jednej transakcji, potencjalnie unikając danych orphin (albo wszystkie aktualizacje są ukończone, albo żadna nie).

mark_h
źródło
8
Dzięki ! WAŻNA Porada dotycząca najlepszych praktyk: var order = new Order {Customer = customer}; To pokazuje moc Entity Framework do przypisywania powiązanych obiektów, a identyfikator zostanie automatycznie zaktualizowany.
LA Guy 88
Dzięki za te dodatkowe informacje do odpowiedzi. Był bardzo pomocny w zapisywaniu obchodu DB podczas korzystania z EF.
Prasad Korhale,
Dziękuję bardzo za to. Właśnie tego szukałem. Po prostu wierzyłem, że może być lepszy sposób niż dwukrotne wołanie „SaveChanges”
Josh
1
W odpowiedzi należy również wspomnieć (najlepiej pogrubionymi literami), że rozwiązuje to zachowanie transakcyjne. Jeśli dodanie jednego z obiektów nie powiedzie się, część transakcji nie powiedzie się. Np. Dodanie osoby i studenta. Osoba odnosi sukces, a uczeń nie. Teraz osoba jest zbędna. Dziękuję za to świetne rozwiązanie!
Imran Faruqi
32

Musisz ponownie załadować encję po zapisaniu zmian. Ponieważ został zmieniony przez wyzwalacz bazy danych, którego EF nie może śledzić. Więc musimy ponownie załadować byt z bazy danych,

db.Entry(MyNewObject).GetDatabaseValues();

Następnie

int id = myNewObject.Id;

Spójrz na odpowiedź @jayantha w poniższym pytaniu:

Jak mogę uzyskać identyfikator wstawionego obiektu w strukturze Entity, gdy używasz defaultValue?

Pomocna może być odpowiedź @christian w poniższym pytaniu:

Entity Framework Odśwież kontekst?

QMaster
źródło
2
Cześć, kiedy mi się to podoba, pojawia się błąd kompilacji, który mówi: nie mogę rozwiązać właściwości na przykład Id lub Nazwa, czy możesz mi pomóc, proszę?
Morteza Azizi
1
@MortezaAzizi Wygląda na to, że jest to błąd nieobsługiwanego lub zerowego problemu z właściwością, ale czy możesz bardziej wyjaśnić lub napisać fragment kodu?
QMaster
3
Nie powinno to być konieczne, .GetDatabaseValues();jeśli właśnie zapisałeś swój model w bazie danych. Identyfikator powinien zostać automatycznie ponownie wypełniony do modelu.
vapcguy,
3
@QMaster Tyle, że EF jest również w stanie (i wykonuje) tę samą instrukcję SQL i zapełnia identyfikator obiektu. W przeciwnym razie, w jaki sposób jest w stanie zapisać wiele zależnych obiektów jednocześnie? Jak by to było, GetDatabaseValues()gdyby nie znała identyfikatora obiektu, którego ma szukać w bazie danych?
krillgar
2
@vapcguy Rozumiem. Dzięki za twoje uwagi. Sprawdzę to w obu wersjach EF ASAP.
QMaster
16

Proszę odnieść się do tego linku.

http://www.ladislavmrnka.com/2011/03/the-bug-in-storegeneratedpattern-fixed-in-vs-2010-sp1/

Musisz ustawić właściwość StoreGeneratedPattern na tożsamość, a następnie wypróbować własny kod.

Możesz też tego użyć.

using (var context = new MyContext())
{
  context.MyEntities.AddObject(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Your Identity column ID
}
Azhar Mansuri
źródło
7
Taki sam jak w przypadku najlepiej głosowanej odpowiedzi.
Lewis86
2
StoreGeneratedPatternbył dla mnie brakującym elementem.
BurnsBA
1
Tak właśnie należy postępować podczas pracy z ORACLE i ON-Insert Trigger,
Karl
link jest uszkodzony - zaparkowana domena
t.durden
Cześć z Oracle musiałem ustawić StoreGeneratedPattern w Entity - przejdź do przeglądarki modelu, znajdź swoją tabelę i PK, wybierz właściwość StoreGeneratedPattern i ustaw Tożsamość. Działa jak wskazano powyżej. Jest tak w przypadku, gdy masz wyzwalacz, który zapełnia PK z sekwencji na wstawce.
Rob
15

Zapisany obiekt powinien mieć poprawny Idformat po propagowaniu zmian do bazy danych.

Snowbear
źródło
27
widziałeś wewnętrzny wyjątek? ;-)
Snowbear
6

Możesz uzyskać identyfikator dopiero po zapisaniu, zamiast tego możesz utworzyć nowego Guida i przypisać go przed zapisaniem.

Vaduganathan Pillappan
źródło
4

Natrafiam na sytuację, w której muszę wstawić dane do bazy danych i jednocześnie wymagać podstawowego identyfikatora za pomocą struktury encji. Rozwiązanie :

long id;
IGenericQueryRepository<myentityclass, Entityname> InfoBase = null;
try
 {
    InfoBase = new GenericQueryRepository<myentityclass, Entityname>();
    InfoBase.Add(generalinfo);
    InfoBase.Context.SaveChanges();
    id = entityclassobj.ID;
    return id;
 }
Pan Kunal
źródło
4

Wszystkie odpowiedzi są bardzo dobrze dostosowane do ich własnych scenariuszy, co zrobiłem inaczej, to to, że przypisałem int PK bezpośrednio z obiektu (TEntity), który Add () zwrócił do zmiennej int takiej jak ta;

using (Entities entities = new Entities())
{
      int employeeId = entities.Employee.Add(new Employee
                        {
                            EmployeeName = employeeComplexModel.EmployeeName,
                            EmployeeCreatedDate = DateTime.Now,
                            EmployeeUpdatedDate = DateTime.Now,
                            EmployeeStatus = true
                        }).EmployeeId;

      //...use id for other work
}

więc zamiast tworzyć cały nowy obiekt, po prostu bierz co chcesz :)

EDYCJA Dla pana @GertArnold:

wprowadź opis zdjęcia tutaj

IteratioN7T
źródło
3
Nie jest zbyt przydatne dodawanie nieujawnionej metody przypisywania identyfikatora. Nie jest to nawet związane z Entity Framework.
Gert Arnold,
1
@GertArnold .. miałem to samo pytanie co OP. Znalazłem odpowiedzi i przekształciłem je w jedno wierszowe oświadczenie, i nigdy nie słyszałem o tym, jak termin niejawna metoda ma wyjaśnić?
IteratioN7T
3
Oznacza to po prostu, że nie pokazujesz (ujawniasz) wszystkiego, co robisz. Może po prostu nie jest to jasno opisane, nie wiem. Wiem tylko, że Add(jeśli tak jest DbSet.Add) magicznie nie przypisuje wartości EmployeeId.
Gert Arnold,
3
Nie bez SaveChanges. Niemożliwy. Chyba że masz jakiś inny proces, który przypisuje EmployeeId, na przykład w Employeekonstruktorze. LUB używasz EF core ForSqlServerUseSequenceHiLo. W każdym razie nie dajesz całego obrazu.
Gert Arnold,
3
Moja ostatnia uwaga brzmi: to nie jest standardowe zachowanie EF. Powinieneś podać więcej szczegółów na temat swojego środowiska. Wersja EF, marka i wersja bazy danych, klasa pracownika, szczegóły jego mapowania.
Gert Arnold,
3
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

To zadziała.

Saket Choubey
źródło
4
Repozytorium nie ponosi odpowiedzialności za zapisywanie zmian w bazie danych. To praca UnitOfWork.
tchelidze
5
Co więcej, absolutnie niejasne Repositoryjest, co jest. A „to zadziała” to jakieś wyjaśnienie!
Gert Arnold,
2

Istnieją dwie strategie:

  1. Użyj wygenerowanej przez bazę danych ID( intlub GUID)

    Cons:

    Powinieneś wykonać, SaveChanges()aby uzyskać IDtylko zapisane elementy.

    Plusy:

    Może używać inttożsamości.

  2. Użyj wygenerowanego przez klienta ID- tylko GUID.

    Plusy: minimalizacja SaveChangesoperacji. Możliwość wstawienia dużego wykresu nowych obiektów na jedną operację.

    Cons:

    Dozwolone tylko dla GUID

Vladislav Furdak
źródło
1

Przy pierwszym użyciu kodu EF 6.x.

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

i zainicjuje tabelę bazy danych, umieści ona

(newsequentialid())

wewnątrz właściwości tabeli pod nagłówkiem Wartość domyślna lub Powiązanie, umożliwiając zapełnianie identyfikatora podczas wstawiania.

Problem polega na tym, że tworzysz tabelę i dodajesz

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

część później, przyszłe aktualizacje baz danych nie dodają ponownie (newsequentialid ())

Poprawnym sposobem jest wyczyszczenie migracji, usunięcie bazy danych i ponowna migracja ... lub możesz po prostu dodać (newsequentialid ()) do projektanta tabel.

Jeff Li
źródło
Czy potrafisz wyjaśnić, dokąd zmierza newsequentialid ()? Mam już modele baz danych tworzone ręcznie, nie używając najpierw kodu EF i nie wypełnia on identyfikatora, ponieważ nie jest to mój klucz podstawowy. Chciałbym znaleźć rozwiązanie tego problemu.
Josh
1
@ josh Zakładając, że kod jest najpierw Guid, kliknij prawym przyciskiem myszy tabelę w Sql Server Management Studio, kliknij kolumnę Id, a na karcie Właściwości kolumny w (Ogólne) zobaczysz Wartość domyślną lub Powiązanie. Jeśli tworzysz tabele DB ręcznie, zapoznaj się z NewSequentialId lub NewId . Ogólny przypadek użycia jest podobnywhen -column- is NULL, then NEWID()
Jeff Li