Entity Framework ObjectContext 的重复使用

13

我现在正在学习EF,关于对象上下文(ObjectContext)有一个问题:

当我访问数据库时,每个查询(函数)都应该创建ObjectContext的实例吗?

还是最好只创建一次(单例)并重复使用它?

在使用EF之前,我使用企业库数据访问块,并为DataAccess函数创建了一个数据访问实例...


在我看来 - 使用 DI 来解决 EF 对象上下文的实例化问题,并尝试调整生命周期,以便找到最佳结果。 - Sunny
6个回答

15

我认为最常见的方法是按请求使用它。在开始时创建它,进行所需操作(大多数情况下这些操作需要通用ObjectContext),在结束时释放它。大多数DI框架都支持此场景,但您还可以使用HttpModule创建上下文并将其放置在HttpContext.Current.Items中。这是一个简单的示例:

public class MyEntitiesHttpModule : IHttpModule
{
    public void Init(HttpApplication application)
    {
        application.BeginRequest += ApplicationBeginRequest;
        application.EndRequest += ApplicationEndRequest;
    }

    private void ApplicationEndRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.Items[@"MyEntities"] != null)
            ((MyEntities)HttpContext.Current.Items[@"MyEntities"]).Dispose();
    }

    private static void ApplicationBeginRequest(Object source, EventArgs e)
    {
        var context = new MyEntities();
        HttpContext.Current.Items[@"MyEntities"] = context;
    }
}

1
我认为这种解决方案不如在数据层中需要时简单地创建ObjectContext易于维护。我反对这种解决方案的原因是您会在UI层和数据层之间创建一个不必要的依赖关系。如何与数据库通信是数据层的内部问题。其次,虽然从一开始就不必担心延迟加载问题似乎很容易,但有一天您尝试跟踪错误并且无法确定导致错误的数据加载自哪个位置时,就会感到棘手(从经验而言...) - Ronald Wildenberg
1
@rwwilden:抱歉,请问它是如何在 UI 和数据层之间创建依赖关系的?我正在使用仓储模式,而且仓储必须共享上下文。上下文由 DI 容器注入。请求并不那么长,也没有太多的操作会导致这么多问题。如果您使用适当的模式,就不会有任何问题。 - LukLed
1
@rwwilden:关于ObjectContext的重复使用,Stack Overflow和其他页面上有无数问题。按请求使用似乎占主导地位,这不仅是我的观点。 - LukLed
@LukLed同意,保持ObjectContext太长时间是一个坏主意。 - Ronald Wildenberg
1
@SalvatorePreviti:在HTTP请求期间保持上下文是完全可以的,但你不应该使用它超过请求时间,因为你可能会使用过时的数据。 - LukLed
显示剩余7条评论

14

对于每个查询都需要使用它。ObjectContext是一个轻量级对象,因此每次需要时创建一个ObjectContext的成本不高。

此外,您保持ObjectContext存活的时间越长,它包含的缓存对象就越多,因为您针对它运行查询。这可能会导致内存问题。因此,将ObjectContext作为单例模式是一个特别糟糕的想法。随着您的应用程序的使用,您会在单例ObjectContext中加载越来越多的实体,直到最终您将整个数据库加载到内存中(除非您在不再需要它们时分离实体)。

然后还有一个可维护性问题。有一天,您试图跟踪错误,但无法确定导致它的数据是从哪里加载的。


3
如果只有单例和按需访问这两个选项,我会同意这个答案。但是,存在整个生命周期的一整个谱系,根据应用程序的不同,其中一些可能会提供更好的性能。上下文缓存是有原因的:) 按请求访问是除按需访问之外最“安全”的选项。 - Merlyn Morgan-Graham

1
不要使用单例模式..每个使用你的应用程序的人都会共享它,当该对象上下文跟踪实体时,会发生各种疯狂的事情。
我会将其添加为私有成员。

我指的是每个会话一个单例(每个用户都有自己的),并且我计划禁用跟踪。 - verror
我同意rwwilden的第一条回答。 - Raj Kaimal

1

就像Luke所说,这个问题在SO上已经被问了很多次。

对于Web应用程序来说,每个请求周期似乎是最好的选择。单例绝对是一个坏主意。

每个请求都可以很好地工作,因为一个Web页面有一个用户,可能有一些属于该用户的项目,可能有一些该用户的消息。您希望使用相同的ObjectContext,以便您可以转到User.Messages来获取它们,也许将一些消息标记为已读,也许添加一个项目,然后在页面周期完成时提交或放弃整个对象图。


1
静态单例不仅是一个糟糕的想法,而且也不是线程安全的!创建的上下文应该只在一个线程中使用。 - Salvatore Previti

0
public class DBModel {

        private const string _PREFIX = "ObjectContext";

        // DBModel.GetInstance<EntityObject>();
        public static ObjectContext GetInstance<T>() {
            var key = CreateKey<T>();
            HttpContext.Current.Items[key] = HttpContext.Current.Items[key] ?? Activator.CreateInstance<T>();
            return HttpContext.Current.Items[key] as ObjectContext;
        }

        private static string CreateKey<T>() {
            return string.Format("{0}_{1}", _PREFIX, typeof(T).Name);
        }
    }

0
7个月后才发帖。我目前正在我的应用程序中解决这个问题,我倾向于使用@LukLed的解决方案,即为我的HttpRequest创建一个单例ObjectContext。对于我的架构,我有几个控件用于构建页面,这些控件都有自己的数据问题,从EF层提取只读数据。它们每个都创建和使用自己的ObjectContext似乎是浪费的。此外,有一些情况下,一个控件可能会将数据拉入上下文中,其他控件可以重复使用该数据。例如,在我的主页中,页面顶部的标题栏具有用户信息,其他控件可以重复使用该信息。
我唯一担心的是,我可能会将实体拉入上下文中,这会影响其他控件的查询。我还没有看到过这种情况,但不知道是否会遇到麻烦。我想我们会看到的!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接