实体框架代码第一个子导航属性null

Ala*_*ald 6 .net c# asp.net-mvc entity-framework

我有一个项目使用实体框架代码第一版本6与懒惰加载.在模型级别,它有一个包含模块的课程.课程类声明如下:

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; }
}
Run Code Online (Sandbox Code Playgroud)

我的Module类声明如下:

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


}
Run Code Online (Sandbox Code Playgroud)

它们都继承的BaseEntity类只是为了减少常见应用程序属性的代码重复.它声明所有实体的主键如下:

[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Int64 Id { get; set; }
Run Code Online (Sandbox Code Playgroud)

如果相关,我可以在其中包含更多细节,但我无法看到它们如何干扰,并且那里有一堆非主题框架的东西.

当我加载视图以编辑数据库中已存在的课程时,即使应该有记录,当在MVC视图中使用时,Course的Modules属性也始终为null .例如,尽管有几个模块的课程ID为3,但编号为3的编辑课程的模块属性为null.

然而,关系的反向导航确实有效.如果我转到视图来编辑模块ID 5,它的Course属性按预期设置,我可以访问课程值.此模块链接到课程ID 3.

我的实体框架代码优先使用默认配置,即启用了延迟加载.我通过检查DBContext的调试器值来验证这一点,但这也解释了为什么Course属性自动从Module运行到Course.

从模块到课程而不是从课程到模块集合的关系的可能原因是什么?

正如您从Course类中的注释代码中看到的那样,我之前通过确保Modules属性永远不为null来处理null情况,但这只在没有模块的情况下才重要,因为这里的情况并非如此我尝试将其更改回基本ICollection属性以将其排除为问题.

我也在调配DBContext时进行调试,并且肯定是在视图代码运行之后,因此在访问Collection之前不存在DBContext被丢弃的危险.

我对显式使用Include语句不感兴趣.我想使用延迟加载和EF来自动获取数据,我知道它对性能的影响等.

理想情况下,除了使用上述属性之外,我不想描述与实体框架的关系.如果我必须,但请解释为什么我确信我已成功完成这个简单的父母 - 孩子,1 - 很多关系之前没有问题.

更新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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果我在以下行之后调试并检查属性,它会按预期自动填充.

currentModel = GetID(id);
Run Code Online (Sandbox Code Playgroud)

正如@Zaphod所评论的那样,在视图开始渲染之前,数据库连接似乎已关闭.我不明白为什么会发生这种情况,因为我们仍然是服务器端,还没有将标记返回给浏览器.有没有办法在视图中启用延迟加载,只在关闭控制器时关闭连接?

更新2

实际的GetID代码:

 protected virtual Model GetID(long id = 0)
 {
        return DbSet.Find(id);
 }
Run Code Online (Sandbox Code Playgroud)

在Controller的构造函数中相应地设置DbSet以指向DbContext上的DbSet:

public CourseController()
        : base()
    {
        this.DbSet = DB.Courses;
    }
Run Code Online (Sandbox Code Playgroud)

我无法看到DbSet如何在调用视图的控制器操作的最后一行与开始运行的视图之间以某种方式中断查询.

更新3

我应该包含在继承的控制器构造函数中创建DbContext的位置的详细信息:

public ApplicationCRUDController()
        : base()
    {
       this.DB = eLearn.Models.DbContext.CreateContext(Session);
    }
Run Code Online (Sandbox Code Playgroud)

这仅在继承的控制器的Dispose方法中处理一次,该方法在视图呈现之后才被调用:

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

        base.Dispose(disposing);
    }
Run Code Online (Sandbox Code Playgroud)

我仍然不明白为什么反向关系场景会正常工作,因为它应该在视图中延迟加载,因为它都使用相同的框架.

Ala*_*ald 0

在控制器框架的 OnActionExecuted 中,模型对象被恶意添加到 DbContext DbSet 中:

//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);
Run Code Online (Sandbox Code Playgroud)

执行此 Add 调用即可解决问题。我仍然不明白为什么它只在一个方向上破坏关系。另一个问题是,代码被放入来修复另一个问题,因此会再次被破坏,但我不知道那是什么,所以只能顺其自然!