在我的项目中,我使用实体框架4.0作为ORM来保存SQL Server中的数据。
我的项目是来自应用程序的功能区,其中包含网格视图和主窗体中的导航树,其顶部带有功能区面板。我的应用程序基本上是一个CRUD UI,只有很少的业务逻辑。
第一次使用EF时,我通过在协调表单(主表单或显示为用户应用程序的表单)中创建并保存objectContext实例作为成员变量并将查询绑定到网格来开发此项目视图。
对于各种事件,如功能区面板按钮单击,网格视图行单击等,我打开另一个窗体表单。在该窗口中,我创建了另一个对象上下文,并存储在该表单类的成员变量中。
我读过很少的博客和问题,如:
一组作者建议分享对象上下文,而另一组则建议短暂和非共享。
我达到了这种混乱状态,因为我现在处于这样一种状态,即我在其中一个子窗体中对objectContext所做的更改没有反映显示它的父窗体。我试图刷新但仍然没有用。仅仅是为了一个实验,我通过构造函数注入分享了我最初在大多数父类中创建的objectContext,并解决了我的更改反射问题。
将我所有的子表单转换为共享objectContext对我来说是一项巨大的工作。但如果值得,我准备好了。我不确定分享它的潜在问题是什么?
我可能会选择objectContext的静态实例,因为我没有将它用于Web而不是规划多线程场景。如果需要,我可以成为一个单身人士。
我的问题:
项目和环境的详细信息如下:
我在搜索和阅读许多问题和博客文章后发布这个帖子。我读的越多,它就越容易混淆:)如果我要离开某人想要回答的话,请耐心等待。如果通过评论提出这样的澄清,我会尝试更新问题。
答案 0 :(得分:6)
根据我的情况共享或不共享ObjectContext?
不要分享你的背景。 EntityFramework上下文应遵循UnitOfWork模式。您的对象上下文应该尽可能短,而不会不必要地创建/销毁太多的上下文。这通常会转换为应用中作为工作单元的单个“操作”。对于Web应用程序/ api,这可能是每HttpWebRequest
,或者您可以按逻辑数据操作执行此操作(对于每个已实现的“业务逻辑”部分)。
例如:
LoadBusinssObjects()
会创建一个上下文,加载您的数据列表以及您想要的任何相关数据,然后处理上下文。CreateBusinessObject()
将创建一个上下文,创建一个实体的实例,用数据填充它,将其附加到上下文中的collect,保存更改然后处理上下文。UpdateBusinessObject()
会从上下文中读取一些对象,更新它,保存更改并处理上下文。DeleteBusinessObject()
会在上下文中找到一个业务对象,将其从上下文中的集合中删除,保存更改并处理上下文。如果不分享,我该如何解决目前使用其他更改更新一个objectContext的问题? 这是pub/sub architecture的工作。对于上面实现的每个操作,这可以像对象上的一些静态事件处理程序一样简单。然后在每个业务操作的代码中,触发相应的事件。
如果要分享 - 这会更好吗?静态或单身或其他什么? 这是不正确的。当上下文的状态管理器不断收集您在应用程序中执行的每次交互的缓存对象(连接和未连接)时,EF上下文将继续增加内存占用量。上下文的设计不是这样的。
除资源使用外,EF上下文不是线程安全的。例如,如果您希望允许其中一个编辑器表单在树列表加载一些新数据的同时保存一些更改,该怎么办?使用一个静态实例,您最好确保这一切都在UI线程上运行或与信号量同步(yuck,yuck - bad practices)。
以下是根据您的帖子使用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组件都可以订阅哪些事件对它很重要。如果您有一个树列表,然后是一些编辑器表单,树列表将订阅任何更改以添加/更新/删除节点(或简单的方法 - 只刷新整个树列表)。
如果您想更进一步,甚至在连接环境中链接应用程序的单独实例,您可以使用类似WebSync - a comet implementation for the Microsoft Technology Stack的内容在网络上实现发布/订阅事件系统。 WebSync具有内置的所有内容,可以将事件分离为您要订阅或发布到的每个实体/事件的逻辑“通道”。是的,我为制作WebSync的软件公司工作 - 他们在写这篇文章的时候付出了我的时间。 : - )
但是如果您不想为商业实现付费,您可以编写自己的TCP套接字客户端/服务器,在实体更改时分发上述事件的通知。然后,当订阅应用程序通过网络获得通知时,它可以以相同的方式触发其本地事件处理程序,这将导致本地视图刷新。您不能使用数据上下文的架构不佳的静态实例来执行此操作(您必须只运行一个应用程序实例)。通过早期的一些良好设置,您可以轻松地在以后的分布式pub-sub系统上使用,同时可以跨本机应用程序和Web应用程序的多个实例运行!这变得非常强大。