Wstaw / zaktualizuj wiele do wielu Entity Framework. Jak mam to zrobić?

104

Używam EF4 i nowego. Mam wiele za wiele w swoim projekcie i nie mogę wymyślić, jak wstawić lub zaktualizować. Zbudowałem mały projekt tylko po to, żeby zobaczyć, jak powinien być zakodowany.

Załóżmy, że mam 3 stoły

  1. Klasa: ClassID-ClassName
  2. Student: StudentID-FirstName-Nazwisko
  3. StudentClass: StudentID-ClassID

Po dodaniu całej relacji i zaktualizowaniu modelu przez przeglądarkę modeli zauważyłem, że StudentClass nie pojawia się, wydaje się, że jest to zachowanie domyślne.

Teraz muszę zrobić zarówno wstawkę, jak i aktualizację. Jak ty to robisz? Jakieś próbki kodu lub link, z którego mogę pobrać przykład, czy możesz poświęcić 5 minut?

user9969
źródło

Odpowiedzi:

139

Pod względem bytów (lub obiektów) masz Classobiekt, który ma kolekcję Studentsi Studentobiekt, który ma kolekcję Classes. Ponieważ StudentClasstabela zawiera tylko identyfikatory i nie zawiera dodatkowych informacji, EF nie generuje jednostki dla tabeli łączącej. To jest właściwe zachowanie i tego właśnie oczekujesz.

Teraz, robiąc wstawki lub aktualizacje, staraj się myśleć kategoriami obiektów. Np. Jeśli chcesz wstawić klasę z dwoma uczniami, utwórz Classobiekt, Studentobiekty, dodaj uczniów do Studentskolekcji klas, dodaj Classobiekt do kontekstu i wywołaj SaveChanges:

using (var context = new YourContext())
{
    var mathClass = new Class { Name = "Math" };
    mathClass.Students.Add(new Student { Name = "Alice" });
    mathClass.Students.Add(new Student { Name = "Bob" });

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

Spowoduje to utworzenie wpisu w Classtabeli, dwóch wpisów w Studenttabeli i dwóch wpisów w StudentClasstabeli łączących je ze sobą.

Zasadniczo robisz to samo w przypadku aktualizacji. Wystarczy pobrać dane, zmodyfikować wykres, dodając i usuwając obiekty z kolekcji, zadzwoń SaveChanges. Sprawdź to podobne pytanie, aby uzyskać szczegółowe informacje.

Edycja :

Zgodnie z Twoim komentarzem musisz wstawić nowy Classi dodać Studentsdo niego dwa istniejące :

using (var context = new YourContext())
{
    var mathClass= new Class { Name = "Math" };
    Student student1 = context.Students.FirstOrDefault(s => s.Name == "Alice");
    Student student2 = context.Students.FirstOrDefault(s => s.Name == "Bob");
    mathClass.Students.Add(student1);
    mathClass.Students.Add(student2);

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

Ponieważ obaj uczniowie są już w bazie danych, nie zostaną wstawieni, ale ponieważ znajdują się teraz w Studentskolekcji Class, do StudentClasstabeli zostaną wstawione dwa wpisy .

Jakimycz
źródło
Cześć, dziękuję za odpowiedź. Mój scenariusz jest taki, że muszę wstawić 1 wpis do klasy i jak x twój przykład 2 wpisy do StudentClass i brak wpisu do Student. Nie jestem zdezorientowany, jak to robię
user9969
TO DZIAŁAŁO TRAKTAT. Jeśli chodzi o aktualizację. Czy jest coś specjalnego, co muszę zrobić? Np. Wystarczy zaktualizować nazwę klasy, na przykład.
user9969
3
Spowoduje to zwiększenie obciążenia pobierania z bazy danych elementów, które należy dodać. Metoda Attach może służyć do dodawania tylko relacji. Zobacz msdn.microsoft.com/en-us/data/jj592676.aspx, a także stackoverflow.com/questions/11355019/…
Gnomo
3
AddToClasses to DbSet for Class?
Jo Smo
1
Nie zapomnij zainicjować swoich kolekcji w konstruktorach klas i uczniów. Na przykład: public Class () {this.Students = new HashSet <Student> (); }
user1040323
40

Wypróbuj ten do aktualizacji:

[HttpPost]
public ActionResult Edit(Models.MathClass mathClassModel)
{
    //get current entry from db (db is context)
    var item = db.Entry<Models.MathClass>(mathClassModel);

    //change item state to modified
    item.State = System.Data.Entity.EntityState.Modified;

    //load existing items for ManyToMany collection
    item.Collection(i => i.Students).Load();

    //clear Student items          
    mathClassModel.Students.Clear();

    //add Toner items
    foreach (var studentId in mathClassModel.SelectedStudents)
    {
        var student = db.Student.Find(int.Parse(studentId));
        mathClassModel.Students.Add(student);
    }                

    if (ModelState.IsValid)
    {
       db.SaveChanges();
       return RedirectToAction("Index");
    }

    return View(mathClassModel);
}
Stritof
źródło
to uratowało mi dzień, dziękuję !!! kluczową częścią jest item.Collection (i => i.Students) .Load (); część
Gerrie Pretorius
Na marginesie, miałem wyjątek InvalidOperationException, kiedy to robiłem, coś takiego jak moja jednostka nie istniało w kontekście. Po prostu zadzwoniłem do context.MyEntityCollection.Attach (myEntity), aby sobie z tym poradzić.
Kilazur
Nigdy bym tego nie domyślił. Dzięki za tonę.
Jared Beach
1
Pozwólcie, że dodam tutaj moje podziękowania. Napisałem i przetestowałem ponad 130 linii kodu w ciągu ostatnich 4 dni, próbując wykonać to zadanie, ale nie mogłem tego zrobić. Próbowałem tego i wszystko działało idealnie. Chociaż nie rozumiem celu ustanowienia zmiennej item , a następnie w ogóle jej nie używam, po prostu cieszę się, że działa! Dzięki milionowi bajlionów !!!! :)
Dude-Dastic
Biorąc pod uwagę, że pytanie brzmi: Wstaw I Aktualizuj, ta odpowiedź uzupełnia pytanie i zaakceptowaną odpowiedź. Dziękuję Ci bardzo!
Vahx,
5

Chciałem dodać do tego swoje doświadczenie. Rzeczywiście, EF, po dodaniu obiektu do kontekstu, zmienia stan wszystkich elementów podrzędnych i powiązanych jednostek na Added. Chociaż w tej regule występuje mały wyjątek: jeśli jednostki podrzędne / powiązane są śledzone przez ten sam kontekst, EF rozumie, że te jednostki istnieją i nie dodaje ich. Problem występuje, gdy na przykład ładujesz elementy podrzędne / powiązane z innym kontekstem lub interfejsem sieciowym itp., A następnie tak, EF nie wie nic o tych jednostkach i dodaje je wszystkie. Aby tego uniknąć, po prostu zdobądź klucze encji i znajdź je (np. context.Students.FirstOrDefault(s => s.Name == "Alice"))W tym samym kontekście, w którym chcesz wykonać dodanie.

Athina
źródło
3

Używam następującego sposobu do obsługi relacji wiele-do-wielu, w których biorą udział tylko klucze obce.

Więc do wstawienia :

public void InsertStudentClass (long studentId, long classId)
{
    using (var context = new DatabaseContext())
    {
        Student student = new Student { StudentID = studentId };
        context.Students.Add(student);
        context.Students.Attach(student);

        Class class = new Class { ClassID = classId };
        context.Classes.Add(class);
        context.Classes.Attach(class);

        student.Classes = new List<Class>();
        student.Classes.Add(class);

        context.SaveChanges();
    }
}

Do usunięcia ,

public void DeleteStudentClass(long studentId, long classId)
{
    Student student = context.Students.Include(x => x.Classes).Single(x => x.StudentID == studentId);

    using (var context = new DatabaseContext())
    {
        context.Students.Attach(student);
        Class classToDelete = student.Classes.Find(x => x.ClassID == classId);
        if (classToDelete != null)
        {
            student.Classes.Remove(classToDelete);
            context.SaveChanges();
        }
    }
}
lenglei
źródło
1

W ramach jednostki, gdy obiekt jest dodawany do kontekstu, jego stan zmienia się na Dodano. EF zmienia również stan każdego obiektu na dodany w drzewie obiektów, a zatem otrzymujesz błąd naruszenia klucza podstawowego lub zduplikowane rekordy są dodawane do tabeli.

Nilesh Hirapra
źródło
2
Przeprowadź edycję, aby podać wystarczająco dużo szczegółów, aby użytkownik nie musiał odwiedzać Twojego bloga, aby uzyskać odpowiedź.