2015-03-04 2 views
5

У меня есть проект с использованием Entity Framework Code Первая версия 6 с Lazy loading. На уровне модели он имеет курс, который имеет модули. Класс курс объявляется следующим образом:Entity Framework Code First Child Navigation Свойство 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; } 
} 

Мой класс Модуль объявлен следующим образом:

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


} 

Класс BaseEntity оба они наследуют от всего, чтобы уменьшить дублирование кода для общих свойств приложения. Он объявляет первичный ключ для всех субъектов, например:

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

я могу включать более подробную информацию о нем, если отношении, но я не могу видеть, как они мешают, и есть куча не по теме рамочных вещей там.

Когда я загружаю представление для редактирования Курса, который уже существует в базе данных, свойство Модулей Курса всегда равно нулю при потреблении в представлении MVC , даже если должны быть записи. Например, курс редактирования с идентификатором 3 имеет свойство нулевых модулей, несмотря на то, что существует несколько модулей с идентификатором курса 3.

Обратное перемещение отношений DOES однако работа. Если я перейду к представлению, чтобы отредактировать Module id 5, свойство Course установлено как ожидалось, и я могу получить доступ к значениям курса. Этот модуль связан с идентификатором курса 3.

My Entity Framework Code Сначала используется конфигурация по умолчанию, то есть включена Lazy Loading. Я проверил это, проверив значения отладчика DBContext, а также объяснил, почему свойство Course автоматически работает с модулем на курс.

Каковы возможные причины отношений, связанных с модулем, к курсу, но не от коллекции курса к модулю?

Как вы можете видеть из прокомментированного кода в классе курса, у меня было это с обработкой нулевой ситуации ранее, если свойство Modules никогда не было нулевым, но это имеет значение только тогда, когда нет модулей для курса, который не является здесь, поэтому я попытался изменить это обратно на базовое свойство ICollection, чтобы исключить его как проблему.

Я также отлаживал при размещении DBC-контекста, и это определенно после того, как код представления запущен, поэтому нет опасности того, что DBC-текст будет удален до доступа к коллекции.

Я не заинтересован в явном использовании операторов Include. Я хочу использовать ленивую загрузку и EF для автоматического получения данных, и я знаю о последствиях, которые это имеет для производительности и т. Д.

В идеале я не хочу описывать связь с Entity Framework за пределами использования свойства выше. Если мне нужно, но, пожалуйста, объясните, почему, поскольку я уверен, что я успешно выполнил этот простой родительский подход - Child, 1 - many, без проблем.

UPDATE 1

код действия контроллера заключается в следующем

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

Если отладить после следующей строки и осмотреть имущество он получает automically населенную, как и ожидалось.

currentModel = GetID(id); 

Как заметил @Zaphod, похоже, что соединение DB закрыто до того, как представление начнет рендеринг. Я не понимаю, почему это происходит, поскольку мы все еще на стороне сервера и еще не вернули разметку в браузер. Есть ли способ включить ленивую загрузку в представлениях и только закрыть соединение, когда контроллер установлен?

Update 2

Фактический код GetID:

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

DbSet соответственно устанавливается в конструкторе контроллера, чтобы указать на DbSet на DbContext:

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

Я не вижу, как DbSet каким-то образом сломает запросы между последней строкой действия контроллера, вызывающей представление и vie w начинает работать.

Обновление 3

я должен был включать подробности, где DbContext создается что в унаследованном конструкторе контроллера:

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

Это только расположеннымы один раз в методе Dispose унаследованного контроллера, который не вызывается до тех пор, пока ПОСЛЕ просмотра представления не будет:

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

     base.Dispose(disposing); 
    } 

I sti не пойму, почему сценарий обратной зависимости будет работать нормально, поскольку он должен быть ленивой загрузкой в ​​представлении для него, поскольку все это использует ту же структуру.

+1

Вам нужно будет показать код того, как вы загружаете курс. Используя именно классы, которые вы здесь предоставили, ленивая загрузка работает для меня как для меня, так и для меня. Я бы предложил написать небольшую тестовую программу, чтобы продемонстрировать, что не работает. – Richard

+1

Эти классы определенно выглядят так, как будто они в порядке для меня. Ваша проблема в другом месте. – Pharylon

+0

Скорее всего, свойство nav не было заполнено до того, как вы закроете соединение (то есть отправьте данные в представление). Как предложил @Richard, отправьте код, в котором вы извлекаете объект и передаете его в представление (или viewmodel). – Zaphod

ответ

0

Был изгоев добавление модели объекта к DbContext DbSet в рамочном контроллера 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); 

Решение этой проблемы позволяет решить эту проблему. Я до сих пор не понимаю, почему это нарушает отношения только в одном направлении. Другая проблема заключается в том, что код был введен, чтобы исправить еще одну проблему, так что она будет сломана снова, но я не знаю, что это было, так что просто нужно с ней поработать!

0

Похоже, что вы используете общий контроллер, но он не показывает, как загружается сущность. Если ваше соединение установлено до того, как будет выполнено представление, вы не сможете использовать ленивую загрузку. Например, рассмотрите следующее:

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

Это прекрасно работает. Перестановка вещи, хотя ...

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 

Чтобы сделать эту работу в MVC у вас есть несколько вариантов:

  • Использования Включает или явную отложенную загрузка для заполнения модуля
  • Убедитесь DbContext Безразлично (если вы используете IoC, что-то вроде RequestLifetimeScope в Autofac).
  • Используйте модель/ViewModel подход, при котором будет заполняться ваш ViewModel в то время как соединение открыто
+0

спасибо, но я не распоряжаюсь контекстом. Я установил свойство DbSet в модели представления для запросов LINQ, которые должны быть написаны против. См. Мое обновление 2 –

+0

Где вы создаете контекст ('DB')? Просто потому, что вы явно не распоряжаетесь им, это не значит, что он не находится в ракурсе. Он выйдет из сферы действия и будет удален. – Richard

+0

Извините, я включил другое обновление, чтобы показать это. Поскольку у нас есть собственная структура наследования, довольно сложно показать соответствующие детали –

Смежные вопросы