6

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

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

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

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

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

  1. 如何决定对象上下文的生命周期
  2. Entity Framework 和 ObjectContext n 层架构等。

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

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

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

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

我的问题:

  1. 为我的情况分享或不分享 ObjectContext?
  2. 如果不分享,我该如何解决我目前使用另一个 objectContext 所做的更改来更新一个 objectContext 的问题?
  3. 如果分享 - 哪个会更好?静态或单例或其他?

项目和环境的详细信息如下:

  • 窗体
  • C#
  • 对比 2012
  • EF 4.0,使用数据优先方法创建的模型。

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

4

1 回答 1

6

你的问题

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

例如:

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

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

如果分享 - 哪个会更好?静态或单例或其他? 这是不正确的。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<MyCodeFirstEntityChangedArgs>(MyCodeFirstEntity_LocalEventHandler);

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

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

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

应用程序之间的更新

If you want to go a step further and even link separate instances of your app in a connected environment you can implement a pub/sub eventing system over the network using something like WebSync - a comet implementation for the Microsoft Technology Stack. WebSync has all the stuff built in to separate events into logical "channels" for each entity/event you want to subscribe to or publish to. And yes, I work for the software company who makes WebSync - they're paying for my time as I write this. :-)

But if you didn't want to pay for a commercial implementation, you could write your own TCP socket client/server that distributes notifications for the above events when an entity changes. Then when the subscribing app gets the notification over the network, it can fire its local event handlers the same way which will cause local views to refresh. You can't do this with a poorly architected static instance of your data context (you'd be bound to only ever have one instance of your app running). With some good setup early on, you can easily tack on a distributed pub-sub system later that works across multiple instances of native apps and web apps all at the same time! That gets very powerful.

于 2013-03-11T19:50:59.267 回答