2015-03-04 17 views
5

Mam projekt przy użyciu Entity Framework Code First version 6 z Lazy loading. Na poziomie modelu ma Kurs, który ma Moduły. Klasa Oczywiście jest zadeklarowana następująco:Kod struktury Entity First Child Navigation Property null

public class Course : BaseEntity 
{ 
    public String Title { get; set; } 
    public String Description { get; set; } 
    public int Revision { get; set; } 

    //private IList<Module> _modules; 
    //public virtual IList<Module> Modules 
    //{ 
    // get { return _modules ?? (_modules = new List<Module>()); } 
    // set { _modules = value; } 
    //} 
    public virtual ICollection<Module> Modules { get; set; } 
} 

Moja klasa Module jest zadeklarowana następująco:

public class Module : BaseEntity 
{ 
    [ForeignKey("Course")] 
    public Int64 CourseID { get; set; } 
    public virtual Course Course { get; set; } 

    public String Title { get; set; } 
    public Int32 SequenceNo { get; set; } 

    public override string HumanDisplay 
    { 
     get { return Title; } 
    } 

    private IList<ModuleItem> _items; 
    public virtual IList<ModuleItem> Items 
    { 
     get { return _items ?? (_items = new List<ModuleItem>()); } 
     set { _items = value; } 
    } 


} 

klasa BaseEntity oboje dziedziczą tylko do zmniejszenia powielania kodu wspólnych właściwości aplikacyjnych. Deklaruje klucz podstawowy dla wszystkich podmiotów, takich jak:

[Key] 
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] 
public Int64 Id { get; set; } 

mogę zawierać więcej szczegółów na temat tego, czy istotne, ale nie mogę zobaczyć, jak oni ingerować i tam jest kilka off topic ramowej rzeczy tam.

Po załadowaniu widoku w celu edycji kursu, który już istnieje w bazie danych, właściwość Moduły kursu jest zawsze pusta, jeśli jest używana w widoku MVC , nawet jeśli powinny być zapisy. Na przykład kurs edycji z identyfikatorem 3 ma zerową właściwość Moduły, pomimo faktu, że istnieje kilka modułów o ID kursu równym 3.

Nawrotna nawigacja związku DOES jakkolwiek działa. Jeśli przejdę do widoku, aby edytować identyfikator modułu 5, właściwość kursu jest ustawiana zgodnie z oczekiwaniami i mogę uzyskać dostęp do wartości kursu. Ten moduł jest powiązany z ID kursu 3.

Mój kod źródłowy podmiotu Najpierw jest używana konfiguracja domyślna, np. Włączone jest ładowanie Lazy. Zweryfikowałem to, sprawdzając wartości debuggera w DBContext, ale także to wyjaśnia, dlaczego właściwość Course działa automatycznie z modułu do kursu.

Jakie są możliwe przyczyny relacji między modułem a kursem, ale nie z kolekcji Course to the Modules?

Jak widać na podstawie skomentowanego kodu w klasie Course, miałem do czynienia z wcześniejszą sytuacją zerową, upewniając się, że właściwość Modules nigdy nie była zerowa, ale ma to znaczenie tylko wtedy, gdy nie ma modułów dla kursu, który nie jest Przypadek tutaj, więc próbowałem zmienić to z powrotem do podstawowej właściwości ICollection, aby wykluczyć to jako problem.

Mam również debugowane, gdy DBContext jest usuwany i jest zdecydowanie po uruchomieniu kodu widoku, więc nie ma niebezpieczeństwa, że ​​DBContext zostanie usunięty przed uzyskaniem dostępu do Collection.

Nie jestem zainteresowany jawnym użyciem instrukcji Uwzględnij. Chcę użyć leniwego ładowania i EF, aby automatycznie uzyskać dane i jestem świadomy ich wpływu na wydajność itp.

Idealnie nie chcę opisywać związku z Entity Framework poza używaniem właściwości powyżej. Jeśli muszę, to mogę, ale proszę wyjaśnij dlaczego, ponieważ jestem pewien, że udało mi się zrobić to proste rodzic - Dziecko, 1 - wiele relacji wcześniej bez problemu.

UPDATE 1

kod działanie kontrolera jest następująca

public virtual ActionResult Edit(long id = 0) 
    { 
     if (Session.GetUser().GetCombinedPrivilegeForEntity(Entity).CanRead) 
     { 
      String saveSuccess = TempData["successMessage"] as String; 
      currentModel = GetID(id); 

      if (currentModel == null) 
       throw new RecordNotFoundException(id, typeof(Model).Name); 

      currentVM = Activator.CreateInstance<ViewModel>(); 

      currentVM.Model = currentModel; 
      currentVM.DB = DB; 
      currentVM.ViewMode = ViewMode.Edit; 
      currentVM.SuccessMessage = saveSuccess; 
      SetViewModelPermissions(); 

      //Cache.AddEntry(Session.SessionID, Entity, currentVM.Model.Id, currentVM); 
      if (currentModel == null) 
      { 
       return HttpNotFound(); 
      } 

      if (ForcePartial || Request.IsAjaxRequest()) 
       return PartialView(GetViewName("Edit"), currentVM); 

      else 
       return View(GetViewName("Edit"), MasterName, currentVM); 

     } 
     else 
     { 
      throw new PermissionException("read", Entity); 
     } 
    } 

Gdybym debugowania po następnej linii i inspekcję nieruchomości pobiera automically zaludnionych, jak oczekiwano.

currentModel = GetID(id); 

Jak komentuje @Zaphod wygląda na to, że połączenie DB jest zamknięte przed rozpoczęciem renderowania widoku. Nie rozumiem, dlaczego tak się dzieje, ponieważ nadal jesteśmy po stronie serwera i jeszcze nie zwróciliśmy znaczników do przeglądarki. Czy istnieje sposób na umożliwienie leniwego ładowania w widokach i zamykanie połączenia tylko wtedy, gdy kontroler jest usuwany?

Aktualizacja 2

Rzeczywisty kod WezID:

protected virtual Model GetID(long id = 0) 
{ 
     return DbSet.Find(id); 
} 

DbSet jest odpowiednio ustawiona w konstruktora sterownika, aby wskazać DbSet na DbContext:

public CourseController() 
     : base() 
    { 
     this.DbSet = DB.Courses; 
    } 

Nie widzę, jak DbSet mógłby w jakiś sposób złamać zapytania między ostatnim wierszem akcji kontrolera wywołującym widok a th e widok zaczyna biec.

Update 3

powinno mam włączone szczegóły gdzie DbContext jest tworzony, który znajduje się w odziedziczonej konstruktora kontrolera:

public ApplicationCRUDController() 
     : base() 
    { 
     this.DB = eLearn.Models.DbContext.CreateContext(Session); 
    } 

ta jest umieszczona tylko raz w sposobie dysponowania dziedziczone sterownika który nie jest wywoływana, dopóki widok nie zostanie wyświetlony:

protected override void Dispose(bool disposing) 
    { 
     if (DB != null) 
      DB.Dispose(); 

     base.Dispose(disposing); 
    } 

Wciąż d nie rozumiem, dlaczego scenariusz odwrotnej relacji działałby dobrze, ponieważ powinien być leniwy ładowanie w widoku dla niego, ponieważ wszystkie one używają tego samego schematu.

+1

Musisz pokazać kod, w jaki sposób ładujesz kurs. Korzystając z klas, które tu przygotowałeś, leniwy ładunek działa dla mnie w obu kierunkach. Proponuję napisać mały program testowy, aby pokazać, co nie działa. – Richard

+1

Te zajęcia zdecydowanie wyglądają tak, jakby były dla mnie w porządku. Twój problem jest gdzie indziej. – Pharylon

+0

Najprawdopodobniej właściwość nav nie została wypełniona przed zamknięciem połączenia (tj. Wysłaniem danych do widoku). Jak sugerował @Richard, opublikuj kod, w którym pobierzesz encję i przekaż ją do widoku (lub viewmodel) – Zaphod

Odpowiedz

0

Nie było nieuczciwe dodanie modelu obiektu do DbContext DbSet w ramach sterownika OnActionExecuted:

//We seem to need to add the model to the context because the context it was added on has been disposed....for some reason. 
DbSet.Add(vm.Model); 

Wykonanie tego Dodaj wywołanie rozwiązuje problem. Wciąż nie rozumiem, dlaczego łamie związek tylko w jednym kierunku. Drugi problem polega na tym, że kod został wprowadzony, aby naprawić inny problem, więc zostanie on przerwany, ale nie wiem, co to było, więc będzie musiał z nim pojechać!

0

Wygląda na to, że używasz ogólnego kontrolera, ale nie pokazuje sposobu załadowania encji. Jeśli połączenie zostanie usunięte przed uruchomieniem widoku, nie będzie można korzystać z leniwego ładowania. Rozważmy na przykład:

Course firstCourse; 
using (var context = new TestContext()) 
{ 
    firstCourse = context.Courses.First(); 
    Console.WriteLine("Course {0} has {1} modules", firstCourse.Title, 
     firstCourse.Modules.Count); 
} 

To działa dobrze. Przestawiać rzeczy chociaż ...

Course firstCourse; 
using (var context = new TestContext()) 
{ 
    firstCourse = context.Courses.First(); 
} 
Console.WriteLine("Course {0} has {1} modules", firstCourse.Title, 
    firstCourse.Modules.Count); // ObjectDisposedException when Modules tries to lazy load 

Aby uczynić tę pracę w MVC masz kilka możliwości:

  • Wykorzystanie obejmuje, ani wyraźnej leniwy załadunku do zapełnienia modułu
  • Upewnić się, że nie robi DbContext nie są usuwane do końca żądania (jeśli używasz IoC, coś w stylu RequestLifetimeScope w Autofac).
  • użyć modelu/podejście ViewModel gdzie ViewModel zostaną wypełnione podczas gdy połączenie jest otwarte
+0

dziękuję, ale nie wyrzucam kontekstu. Ustawię właściwość DbSet w modelu widoku dla zapytań LINQ, które mają być zapisane. Zobacz moją aktualizację 2 –

+0

Gdzie tworzysz kontekst ('DB')? Tylko dlatego, że nie jawnie dysponujesz nią, nie oznacza, że ​​nie jest ona usuwana przez ramy. Wykracza poza zakres i zostanie usunięty. – Richard

+0

Przepraszam, że dodałem kolejną aktualizację, aby to pokazać. Ponieważ mamy własne ramy dziedziczenia, dość trudno jest pokazać odpowiednie szczegóły –

Powiązane problemy