【问题标题】:How to manage ObjectContext lifetime correctly in multi-tier application using Entity Framework?如何使用实体框架在多层应用程序中正确管理 ObjectContext 生命周期?
【发布时间】:2012-08-14 04:16:39
【问题描述】:

我见过很多在 MVC3 应用程序中使用实体框架的例子,它们都是非常简单的演示,只有一个 mvc3 Web 项目,里面有 edmx。

因此,他们可以通过“使用”语句使用打开和关闭连接的最佳实践:

using(var context = new SchoolEntities())
{
    // do some query and return View with result.
}

而且,它可以在“使用”语句中正确使用延迟加载(导航属性),因为上下文还没有 处置:

foreach(var item in student.Course)
{
    // do something with the navigation property Course
}

在成为 n 层应用程序之前,一切似乎都很完美。

我创建了 DAL、BLL 和 MVC3 UI。

DAL 里面有 edmx,以及像 SchoolDA.cs:

这样的操作符类
public class StudentDA()
{
    public Student FindStudent(int studentId)
    {
        using(var context = new SchoolContext())
        {
            // do query, return a student object.
        }
    }
}

那么,在 BLL 中,如果我使用:

var student = studentDa.FindStudent(103);

然后调用它的导航属性

student.Course

我会得到一个错误(当然):

ObjectContext 实例已被释放,不能再用于需要连接的操作。

所以,我必须像这样更改 StudentDA.cs:

public class StudentDA() : IDisposable
{
    private SchoolEntites context;

    public StudentDA()
    {
        context = new SchoolEntities();
    }

    public void Dispose()
    {
        context.Dispose();
    }

    public Student FindStudent(int studentId)
    {
        // do query, return a student object.
    }
}

然后,BLL 会变成这样:

public Student FindStudent(int id)
{
    using(var studentDa = new StudentDA())
    {
        // this can access navigation properties without error, and close the connection correctly.
        return studentDa.FindStudent(id);
    }
}

在遇到 Update() 方法之前,一切似乎又完美了。

现在,如果我想更新从 BLL.FindStudent() 获取的学生对象,context.SaveChanges() 将返回 0,因为上下文已经在 BLL.FindStudent(),数据库不会更新任何内容。

var optStudent = new StudentBO();
var student = optStudent.FindStudent(103);
student.Name = "NewValue";
optStudent.Update(student);

有人知道如何在 3 轮胎应用程序中使用 EntityFramework 吗?或者我怎样才能正确管理上下文。我会经常在 web 层使用导航属性,但我不能总是保持连接打开来消耗服务器内存。

【问题讨论】:

  • 搜索控制反转依赖注入
  • @Eranga 还确保限制上下文的范围,使用 ninject 的 .InRequestScope 或 .InTransientScope,这可以阻止串扰并附加问题

标签: entity-framework database-connection


【解决方案1】:

有多种方法可以处理 EF 上下文的生命周期。在 Web 应用程序中,通常上下文对于 HttpRequest 是唯一的。例如,如果您想在 Web 应用程序中手动处理此问题并拥有每个 Thread/HttpRequest EF 上下文,您可以使用以下代码(从 http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management 复制的代码):

internal static class DbContextManager
{
    public static DbContext Current
    {
        get
        {
            var key = "MyDb_" + HttpContext.Current.GetHashCode().ToString("x")
                      + Thread.CurrentContext.ContextID.ToString();
            var context = HttpContext.Current.Items[key] as MyDbContext;

            if (context == null)
            {
                context = new MyDbContext();
                HttpContext.Current.Items[key] = context;
            }
            return context;
        }
    }
}  

然后你就可以轻松使用了:

var ctx = DbContextManager.Current

但我建议您将生命周期管理留给 IoC 框架,例如 AutofacCastle WindsorNinject,它会自动处理您注册对象的创建/处置以及许多其他功能。

【讨论】:

  • 如果我使用 var key = "foo"; 会有什么不同?而不是那个长字符串? var context = HttpContext.Current.Items["foo"] 不是本地的上下文吗?
  • 如果您只是处理每个上下文的生命周期管理,那么您的代码就足够了(显然,也更易于阅读)。我使用了从 Rick strahl 的博客west-wind.com/weblog/posts/2008/Feb/05/… 获得的这段代码,他在其中谈到了每个上下文/线程生命周期管理。
  • 看起来很优雅,因为您使用Current 似乎会话在请求结束时被清除。谢谢,测试一下!
【解决方案2】:

感谢您的回答卡米亚尔。我在寻找一种无需使用 IoC 框架来管理 ObjectContext 生命周期的简单策略时遇到了这个问题,这对于我的需求来说似乎有点矫枉过正。

我还看到了您的另一篇帖子 here,用于在请求结束时处理上下文。

认为这可能对遇到此问题的其他人有用,所以只需在此处发布我的代码实现:

上下文管理器类 -

internal static class MyDBContextManager
    {
        //Unique context key per request and thread
        private static string Key
        {
            get
            { 
                return string.Format("MyDb_{0}{1}", arg0: HttpContext.Current.GetHashCode().ToString("x"),
                    arg1: Thread.CurrentContext.ContextID);
            }
        }

        //Get and set request context
        private static MyDBContext Context
        {
            get { return HttpContext.Current.Items[Key] as MyDBContext; }
            set { HttpContext.Current.Items[Key] = value; }
        }

        //Context per request
        public static MyDBContext Current
        {
            get
            {
                //if null, create new context 
                if (Context == null)
                {
                    Context = new MyDBContext();
                    HttpContext.Current.Items[Key] = Context;
                }
                return Context;
            }
        }

        //Dispose any created context at the end of a request - called from Global.asax
        public static void Dispose()
        {
            if (Context != null)
            {
                Context.Dispose();
            }
    }
}

Global.asax (MVC) -

    public override void Init()
    {
        base.Init();
        EndRequest +=MvcApplication_EndRequest; 
    }

    private void MvcApplication_EndRequest(object sender, EventArgs e)
    {
        MyDBContextManager.Dispose();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多