1

我正在努力使用 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)每次离开“使用(上下文)”块时,我都可以手动分离()每个实体对象(及其子实体和父实体)。每次我输入一个新的“使用(上下文)”块时,我都可以附加()它们。导致很多努力。(我认为一个框架应该减少而不是增加它。)

3)每次我输入一个新的“使用(上下文)”块时,重新加载然后丢弃每个实体。导致 SQL Server 上的更多(不必要的)负载并且还浪费内存。

4)我可以在应用程序启动时加载所有详细信息和所有引用以及所有引用的所有引用以及这些引用的引用。(不讨论,这真的很傻!)

5) ...(我忘记了一个选项吗?)

现在你的大问题:我应该选择哪种方式?还有另一种我没有看到的方法吗?还是我完全误解了框架的精神?

提前致谢

更新:这是一个 Windows 窗体应用程序。

4

3 回答 3

2

我会做第二个选择。只是不需要分离每个实体,您可以将查询设置为 NoTracking 并且实体不会首先附加到上下文,并且您不会失去关系(如果已加载)。

 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。如果它是基于网络的,我建议使用单例模式,其中将为每个请求创建上下文。

 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 的书 Programming Entity Framework, 1st Edition。

于 2009-09-08T12:30:48.197 回答
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;
}

(但它不会解决你的问题)

于 2009-09-09T06:52:03.450 回答
0

所以,在阅读了 Julia Lerman 的“Programming Entity Framework”几乎一整天之后,我终于找到了一个提示:在第 20 章中,她谈到了“Using a Long-Running Object Context”。这部分涵盖了我的选项 1 和 3:只要用户停留在同一视图上,上下文就会保持打开状态。每次 UI 切换到新视图时都会释放上下文。在新视图中,将创建一个新上下文,并且必须重新加载从最近视图重用的所有实体。

就我而言,我可以接受这个解决方案,尽管这对我来说意味着很多重构......

于 2009-09-09T15:43:22.540 回答