我正在努力使用ADO.NET实体框架。这是我的ER图:
--------------------- ----------------- ---------------
| GrandParentEntity |<-------| ParentEntity |<-------| ChildEntity |
+-------------------+ 1 N +---------------+ 1 N +-------------+
| Id | | Id | | Id |
--------------------- | GrandParentFk | | ParentFk |
----------------- ---------------
对对象上下文的生命周期有疑问。假设我按如下方式请求我的数据:
public static List<MyParentEntity> GetMyParentEntity()
{
using (var context = new MyObjectContext(blablabla...))
{
var resultSet = from e in context.MyParentEntitySet select e;
return resultSet.ToList();
}
}
我得到了所有父实体的列表。一段时间后(小时?),用户决定他要检查哪一个。 (同时,实体对象通过应用程序的几个层。)我现在必须加载记录的所有细节:
public static void LoadChildren(MyParentEntity pEntity)
{
using (var context = new MyObjectContext(blablabla...))
{
pEntity.GranParentEntityReference.Load();
pEntity.ChildEntites.Load();
}
}
这会导致ObjectDisposedException,因为MyParentEntity对象已失去与创建它的对象上下文的连接。我有几种可能性来处理这个问题:
1)我可以一直使用MyObjectContext的同一个实例,而不是关闭它。这会导致巨大的内存浪费。
2)每次离开“using(context)”块时,我都可以手动分离每个实体对象(及其子实体和父实体)。每次我输入一个新的“使用(上下文)”块时,我都可以附加()它们。需要付出很多努力。 (我认为框架应该减少努力而不是增加它。)
3)每次进入新的“使用(上下文)”块时,重新加载然后丢弃每个实体。导致SQL Server上出现更多(不必要的)负载,并浪费内存。
4)我可以在应用程序启动时加载所有引用的所有细节和所有引用以及所有引用的所有引用和这些引用的引用。 (没有讨论,这真的很愚蠢!)
5)......(我忘了一个选项吗?)
现在给你带来很大的问题:我应该选择哪种方式?还有另一种我没看到的方式吗?或者我是否完全误解了框架的精神?
提前致谢
更新:它是一个Windows窗体应用程序。
答案 0 :(得分:2)
我会做第二种选择。只是不需要分离每个实体,你可以将查询设置为NoTracking,并且entites首先不会附加到上下文中,并且你不会松开关系(如果已加载)。
public static List<MyParentEntity> GetMyParentEntity(){
using (var context = new MyObjectContext(blablabla...))
{
var resultSet = from e in context.MyParentEntitySet select e;
((ObjectQuery)resultSet).MergeOption = MergeOption.NoTracking;
return resultSet.ToList();
}}
其次,问题是您的应用程序是基于Web还是基于Windows。如果它是基于网络的,我建议使用Singleton模式,其中将为每个请求创建上下文。
public class Singleton
{
public static YourContext _context;
public static YourContext Context
{
get
{
//We are in a web app, use a request scope
if (HttpContext.Current != null)
{
YourContext current_context = (YourContext)HttpContext.Current.Items["_YourContext"];
if (current_context == null)
{
current_context = new YourContext();
HttpContext.Current.Items.Add("_YourContext", current_context);
}
return current_context;
}
else
{
if (_context == null)
_context = new YourContext();
return _context;
}
}
}
}
如果它不是网络应用程序,我不知道该做什么是最佳做法,所以把它放在静态字段可能不是好事。 这个框架有点复杂,阅读它如何深入工作将有助于更好地理解它并避免这种情况。我使用了Julia Lerman的书“编程实体框架”,第1版。
答案 1 :(得分:0)
我建议选择5:
public static MyParentEntity LoadChildren(MyParentEntity pEntity)
{
using (var context = new MyObjectContext(...))
{
pEntity = (from e in context.MyParentEntitySet.Include("GranParentEntityReference").Include("ChildEntites")
where e.Id == pEntity.Id
select e).FirstOrDefault();
return pEntity;
}
}
两个.Include在这里是为了确保查询包含它们(因为我不久前也发现它)。
我在linqpad中测试它现在正在工作,但是如果它对你不起作用(我从来没有遇到过这种情况),选项1对我来说似乎是更好的解决方案,除非你说的真的很大数据或最终用户终端可能是低规格的。
在一个完全不相关的注释中,您可以简化您的第一个查询(如果该查询中没有其他内容):
using (var context = new MyObjectContext(blablabla...))
{
var resultSet = context.MyParentEntitySet.ToList();
return resultSet;
}
(但它不能解决你的问题)
答案 2 :(得分:0)
因此,在Julia Lerman的“编程实体框架”中几乎整天阅读之后,我终于找到了一个提示:在第20章中,她谈到了“使用长期运行的对象上下文”。这部分涵盖了我的选项1和3:只要用户停留在同一视图上,上下文就会保持打开状态。每次UI切换到新视图时都会配置上下文。在新视图中,将创建一个新上下文,并且必须重新加载从最近视图中重用的所有实体。
我个人可以使用这个解决方案,虽然这对我来说意味着很多重构......