【问题标题】:Entity framework ObjectContext share - pros and cons实体框架 ObjectContext 分享——优缺点
【发布时间】:2025-11-30 18:10:01
【问题描述】:

在我的项目中,我使用实体框架 4.0 作为 ORM 将数据保存在 SQL Server 中。

我的项目是来自应用程序的功能区,主窗体中有网格视图和导航树,顶部有功能区面板。我的应用基本上是一个 CRUD UI,业务逻辑很少。

作为第一次使用 EF,我通过在编排表单(主表单或向用户显示的应用程序)中创建并保存 objectContext 的实例作为成员变量并将查询绑定到网格来开发此项目看法。

对于各种事件,如功能区面板按钮点击、网格视图行点击等,我打开另一个窗口窗体。在那个窗口窗体中,我创建了另一个对象上下文并存储在该窗体类的成员变量中。

我已经阅读了一些博客和问题,例如:

  1. How to decide on a lifetime for your objectcontext
  2. Entity Framework and ObjectContext n-tier architecture

一组作者建议共享对象上下文,而另一组建议短期和非共享。

我达到了这种混乱状态,因为我现在处于这样一种状态,即我在其中一个子窗体中对 objectContext 所做的更改并未反映显示它的父窗体。我试图刷新,但仍然没有任何用处。只是为了做个实验,通过构造函数注入的方式分享了我最先在最父类中创建的objectContext,解决了我的变化反射问题。

将所有子表单转换为共享 objectContext 对我来说是一项艰巨的工作。但如果值得,我已经准备好了。我不确定分享它的潜在问题是什么?

我可能会选择 objectContext 的静态实例,因为我没有将它用于 Web,也没有计划用于多线程场景。如果需要,我可以成为单身人士。

我的问题:

  1. 针对我的情况分享还是不分享 ObjectContext?
  2. 如果不共享,我该如何解决我目前的问题,即用另一个 objectContext 所做的更改来更新一个 objectContext?
  3. 如果要分享 - 哪种方式更好?静态还是单例或其他?

项目及环境详情如下:

  • Winforms
  • C#
  • VS 2012
  • EF 4.0,使用数据优先方法创建的模型。

我在搜索和阅读了许多问题和博客文章后发布了这篇文章。我读的越多,它变得越混乱 :) 如果我要让某人假设要回答的问题,请多多包涵。如果通过 cmets 提出此类澄清,我将尝试更新问题。

【问题讨论】:

    标签: entity-framework-4 objectcontext


    【解决方案1】:

    您的问题

    针对我的情况分享还是不分享 ObjectContext? 不要分享你的上下文。 EntityFramework 上下文应该遵循 UnitOfWork 模式。您的对象上下文应尽可能短,而无需不必要地创建/销毁太多上下文。这通常会转化为应用程序中的单个“操作”作为工作单元。对于 Web 应用程序/api,这可能是每个 HttpWebRequest,或者您可以按逻辑数据操作(对于您实施的每个“业务逻辑”部分)进行。

    例如:

    • LoadBusinssObjects() 将创建一个上下文,加载您的数据列表以及您想要的任何相关数据,然后处理该上下文。
    • CreateBusinessObject() 将创建一个上下文,创建某个实体的实例,用数据填充它,将其附加到上下文中的集合,保存更改,然后释放上下文。
    • UpdateBusinessObject() 会从上下文中读取一些对象,更新它,保存更改,然后释放上下文。
    • DeleteBusinessObject() 会在上下文中找到一个业务对象,将其从上下文的集合中移除,保存更改并释放上下文。

    如果不分享,我该如何解决我目前的问题,即用另一个 objectContext 所做的更改来更新一个 objectContext? 这是pub/sub architecture 的工作。对于您在上面实现的每个操作,这可以像在对象上的几个静态事件处理程序一样简单。然后在每个业务操作的代码中,触发相应的事件。

    如果要分享 - 哪种方式更好?静态或单例或其他? 这是不正确的。 EF 上下文的内存占用量将继续增长,因为上下文的状态管理器会为您在应用程序中进行的每一次交互不断收集缓存的对象(包括附加的和未附加的)。上下文不是这样设计的。

    除了资源使用之外,EF 上下文不是线程安全的。例如,如果您想让您的编辑器表单之一在树列表加载一些新数据的同时保存一些更改怎么办?使用一个静态实例,您最好确保这一切都在 UI 线程上运行或与信号量同步(呸呸呸——不好的做法)。

    示例

    根据您的帖子,这是一个使用 C# 和代码优先方法的示例。请注意,我没有解决诸如数据并发或线程之类的问题以保持示例简短。同样在实际应用程序中,这个概念是通过泛型和反射实现的,因此我们所有的模型都有用于创建、更新、删除的基本事件。

    public class MyCodeFirstEntityChangedArgs : EventArgs
    {
        /// <summary>
        /// The primary key of the entity being changed.
        /// </summary>
        public int Id {get;set;}
    
        /// <summary>
        /// You probably want to make this an ENUM for Added/Modified/Removed
        /// </summary>
        public string ChangeReason {get;set;}
    }
    
    public class MyCodeFirstEntity
    {
        public int Id {get;set;}
        public string SomeProperty {get;set;}
    
        /// <summary>
        /// Occurs when an instance of this entity model has been changed.
        /// </summary>
        public static event EventHandler<MyCodeFirstEntityChangedArgs> EntityChanged;
    }
    
    public class MyBusinessLogic
    {
        public static void UpdateMyCodeFirstEntity(int entityId, MyCodeFirstEntity newEntityData)
        {
                using(var context = new MyEFContext())
                {
                    // Find the existing record in the database
                    var existingRecord = context.MyCodeFirstEntityDbSet.Find(entityId);
    
                    // Copy over some changes (in real life we have a 
                    // generic reflection based object copying method)
                    existingRecord.Name = newEntityData.Name;
    
                    // Save our changes via EF
                    context.SaveChanges();
    
                    // Fire our event handler so that other UI components 
                    // subscribed to this event know to refresh/update their views.
                    // ----
                    // NOTE: If SaveChanges() threw an exception, you won't get here.
                    MyCodeFirstEntity.EntityChanged(null, new MyCodeFirstEntityChangedArgs()
                    {
                        Id = existingRecord.Id,
                        ChangeReason = "Updated"
                    });
                }
        }
    }
    

    现在您可以将事件处理程序从任何地方(它是一个静态事件处理程序)附加到您的模型,如下所示:

    MyCodeFirstEntity.EntityChanged += new EventHandler&lt;MyCodeFirstEntityChangedArgs&gt;(MyCodeFirstEntity_LocalEventHandler);

    然后在每个视图中都有一个处理程序,该处理程序将在触发此事件时刷新本地 UI 视图:

    static void MyCodeFirstEntity_LocalEventHandler(object sender, MyCodeFirstEntityChangedArgs e)
    {
        // Something somewhere changed a record! I better refresh some local UI view.
    }
    

    现在,您拥有的每个 UI 组件都可以订阅对其重要的事件。如果您有一个树列表,然后是一些编辑器表单,则树列表将订阅任何更改以添加/更新/删除节点(或简单的方法 - 只需刷新整个树列表)。

    应用程序之间的更新

    如果您想更进一步,甚至在连接的环境中链接应用程序的单独实例,您可以使用WebSync - a comet implementation for the Microsoft Technology Stack 之类的东西通过网络实现发布/订阅事件系统。 WebSync 具有内置的所有内容,可以将事件分成逻辑“通道”,用于您要订阅或发布的每个实体/事件。是的,我为制作 WebSync 的软件公司工作——他们正在为我写这篇文章的时间付费。 :-)

    但是,如果您不想为商业实现付费,您可以编写自己的 TCP 套接字客户端/服务器,在实体更改时为上述事件分发通知。然后,当订阅应用程序通过网络收到通知时,它可以触发其本地事件处理程序,其方式与刷新本地视图的方式相同。您不能使用架构不佳的数据上下文静态实例来执行此操作(您将只能运行一个应用程序实例)。通过早期的一些良好设置,您可以在以后轻松添加分布式发布-订阅系统,该系统可以同时跨多个本地应用程序和 Web 应用程序实例运行!这变得非常强大。

    【讨论】: