n 层项目中的实体框架 ObjectContext 生命周期

Posted

技术标签:

【中文标题】n 层项目中的实体框架 ObjectContext 生命周期【英文标题】:Entity Framework ObjectContext lifetime in an n-tier project 【发布时间】:2015-06-30 16:36:34 【问题描述】:

我目前正在为客户构建一个大型解决方案,其中代码可重用性是关键字,因为不同类型的项目(即网站、WCF 服务、WPF 等)应该使用完全相同的业务逻辑。

现在,我的解决方案如下所示:

业务逻辑层(定义业务规则的所有逻辑都在这里添加) 接口 模型(ViewModel、DTO 等) 存储库(当前使用 Entity Framework 6。这是所有数据库事务所在的位置) Web 服务(WCF 服务) MVC 网站

关键是任何表示层(即 MVC 网站)都将使用业务逻辑层,然后使用存储库进行所需的任何数据库事务。

虽然这按我想要的方式工作,但我发现自己一直在与实体框架中的 ObjectContext 作斗争,这仅仅是因为在查询存储库中的数据时,实体不会转移到业务逻辑(这是合乎逻辑的,因为EF 延迟加载)我知道我可以使用.Include(x => x.MyOtherTable),但由于数据库相当大,这种方法很快就会变得臃肿,如果包含的表有很多记录,查询可能会相当大。

然后我创建了一个 DbContextManager 类,如下所示:

public static class DbContextManager

    //Unique context key per request and thread
    private static string Key
    
        get
        
            return string.Format("MyDb_01", HttpContext.Current.GetHashCode().ToString("x"), Thread.CurrentContext.ContextID);
        
    

    //Get and set request context
    private static MyEntities Context
    
        get  return HttpContext.Current.Items[Key] as MyEntities ; 
        set  HttpContext.Current.Items[Key] = value; 
    

    //Context per request
    public static MyEntities Current
    
        get
        
            //if null, create new context 
            if (Context == null)
            
                Context = new MyEntities ();
                HttpContext.Current.Items[Key] = Context;
            
            return Context;
        
    

    //Dispose any created context at the end of a request - called from Global.asax
    public static void Dispose()
    
        if (Context != null)
        
            Context.Dispose();
        
    

在我的 global.asax 中,我在请求结束时处理上下文:

private void Application_EndRequest(object sender, EventArgs e)
    
        DbContextManager.Dispose();
    

这很完美,我现在可以在存储库中进行初始数据库调用,然后在我的业务逻辑层中制定业务逻辑规则,这仅仅是因为 ObjectContext 为 http 请求而存在。

但是,当我需要从 WCF 服务项目(或即 WPF 项目)调用相同的业务逻辑方法时,我将无法使用我的 DbContextManager 类,因为它依赖于 HttpContext。

我觉得我现在这样做完全错误,我正在与实体框架进行一场不必要的战斗。我错过了什么? Entity Framework 是否适合这些类型的解决方案?现在肯定不是那种感觉:-)

非常感谢任何帮助或提示!

提前致谢。

【问题讨论】:

看到您的数据库上下文被部署在服务层中真是令人恐惧。为什么不直接在 DAL 中显式加载实体,而不是冒泡延迟查询?也许更有针对性、更小的服务将有助于缓解臃肿,并帮助您避免需要将数据库直接冒泡。 嗨,大卫,感谢您的意见。 “可怕”,这是一个大词;-) 在我的服务层?我不确定你是什么意思。当请求结束时,它会在 MVC 应用程序中释放。在我看来,那里没有服务层。您是否可以制作“显式加载实体”的代码 sn-p ?我不确定这到底是什么意思。 查看我的答案***.com/questions/29637516/…它可能会给你一些想法。 @BoMortensen 很抱歉造成混乱。我在全局 asax 中处理 db 上下文的问题是,您已将数据库完全绑定到 Web 项目 (MVC)。没有关注点分离,也没有控制反转。对我来说,在 n 层应用程序中这是一个非常可怕的地方。至于显式加载实体,每当您使用 ToList、ToArray 等解决查询时,您就是将查询的实体显式加载到内存中。这使您可以在离开 DAL 之前巧妙地处理您的上下文。 大卫:不用担心 :-) 我绝对明白你关于 global.asax 文件的意思。这正是我的问题:它与 DAL 的耦合太紧密了。然后我必须对急切加载进行一些修改,希望它会更有意义;-) 【参考方案1】:

我在这里很困惑。看起来您直接在视图层中使用实体层对象。

这通常是个坏主意。你的模型应该封装这些。您的业​​务逻辑层应该使用您的模型层来构建 UI 层可以生成/使用的 ViewModel 和 DTO 对象。这将允许您将 UI 层与数据库/实体层完全分离。

这将使在 Web 服务层中重用您的业务逻辑变得微不足道。它应该对您的数据库层一无所知。否则,在实体层中添加或更改字段将对所有 UI 层产生直接影响。

【讨论】:

您好,Rob,感谢您的意见。你是对的,我实际上已经制作了一些从实体映射的 DTO。比直接使用实体效果更好。我有点不确定 在哪里 我应该将实体映射到 DTO:在存储库类中还是在业务逻辑层中?你会带什么去? :-) 我通常在业务层做这个。如果您在存储库层执行此操作,则最终会在业务逻辑和视图层之间建立另一个映射层。有些人真的想要这种级别的解耦,理论上他们希望在不更改业务逻辑的情况下更改数据库层,但这很少得到回报(恕我直言)。我已经构建了在两层进行映射的系统,但通常最终感觉像是在浪费时间/精力,并且添加了更多可能出错的映射代码。 加入 DTO 层的另一个很好的副作用是您可以更轻松地支持诸如向后兼容性之类的东西。不将实体暴露给 UI 意味着您可以执行诸如添加具有智能默认值的附加字段之类的操作。这将使您的业务逻辑层随着时间的推移更具弹性。

以上是关于n 层项目中的实体框架 ObjectContext 生命周期的主要内容,如果未能解决你的问题,请参考以下文章

具有实体框架 6 的 ObjectContext 在现有相关实体上插入重复项

我应该如何监听从 ObjectContext 添加/删除的实体?

ObjectContext.SaveChanges()失败,SQL CE

实体框架存储过程表值参数

ObjectContext.Refresh()?

SpringBoot框架中的DAO层Entity层Service层Controller层