87

我目前正在使用DbContext与此类似的:

namespace Models
{
    public class ContextDB: DbContext
    {
              
        public DbSet<User> Users { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }

        public ContextDB()
        {
            
        }
    }
}

然后,我在需要访问数据库的所有控制器的顶部使用以下行。我还在我的 UserRepository 类中使​​用它,其中包含与用户相关的所有方法(例如获取活动用户,检查他拥有的角色等):

ContextDB _db = new ContextDB();

考虑到这一点,有时一个访问者可以激活多个 DbContext,例如,如果它正在访问使用 UserRepository 的控制器,这可能不是最好的想法。

我应该什么时候创建一个新的 DbContext?或者,我是否应该有一个在所有地方传递和重用的全局上下文?这会导致性能下降吗?也欢迎提出替代方法的建议。

4

6 回答 6

86

我使用了一个基本控制器,它公开了DataBase派生控制器可以访问的属性。

public abstract class BaseController : Controller
{
    public BaseController()
    {
        Database = new DatabaseContext();
    }

    protected DatabaseContext Database { get; set; }

    protected override void Dispose(bool disposing)
    {
        Database.Dispose();
        base.Dispose(disposing);
    }
}

我的应用程序中的所有控制器都源自BaseController并使用如下:

public class UserController : BaseController
{
    [HttpGet]
    public ActionResult Index()
    {
        return View(Database.Users.OrderBy(p => p.Name).ToList());
    }
}

现在回答你的问题:

我应该什么时候创建一个新的 DbContext / 我应该有一个可以传递的全局上下文?

应根据请求创建上下文。创建上下文,做你需要做的事情然后摆脱它。使用我使用的基类解决方案,您只需担心使用上下文。

不要尝试拥有全局上下文(这不是 Web 应用程序的工作方式)。

我可以拥有一个可以在所有地方重复使用的全局上下文吗?

不,如果你在它周围保留一个上下文,它将跟踪所有更新、添加、删除等,这会减慢你的应用程序速度,甚至可能导致一些非常微妙的错误出现在你的应用程序中。

您可能应该选择将存储库上下文公开给控制器,但不能同时公开。如果两个上下文对应用程序的当前状态有不同的想法,那么从同一个方法访问两个上下文将导致错误。

就个人而言,我更喜欢直接公开DbContext,因为我见过的大多数存储库示例最终都只是简单的包装DbContext

这会导致性能下降吗?

第一次DbContext创建 a 非常昂贵,但是一旦完成,就会缓存很多信息,以便后续实例化更快。与每次需要访问数据库时实例化一个上下文相比,您更有可能看到保持上下文的性能问题。

其他人是怎么做到的?

这取决于。

有些人喜欢使用依赖注入框架在创建时将其上下文的具体实例传递给他们的控制器。两种选择都很好。我的更适合小型应用程序,您知道正在使用的特定数据库不会改变。

有些人可能会争辩说您知道这一点,这就是依赖注入方法更好的原因,因为它使您的应用程序对更改更具弹性。我对此的看法是它可能不会改变(SQL 服务器和实体框架几乎不模糊),而且我的时间最好花在编写特定于我的应用程序的代码上。

于 2012-11-25T12:47:03.547 回答
13

我试着根据自己的经验来回答。

1. 我应该什么时候创建一个新的 DbContext / 我应该有一个可以传递的全局上下文?

Context 应该由依赖注入来注入,而不应该由你自己实例化。最佳实践是通过依赖注入将其创建为范围服务。(见我对问题 4 的回答)

还请考虑使用适当的分层应用程序结构,例如 Controller > BusinessLogic > Repository。在这种情况下,您的控制器不会接收 db-context 而是接收存储库。在控制器中注入/实例化 db-context 告诉我,您的应用程序架构在一个地方混合了许多职责,在任何情况下,我都不推荐。

2. 我可以拥有一个可以在所有地方重复使用的全局上下文吗?

是的,您可以拥有,但问题应该是“我应该拥有……”-> 否。Context 旨在用于每个请求以更改您的存储库,然后再次离开。

3. 这会导致性能下降吗?

是的,因为 DBContext 根本不是为了全局而设计的。它存储所有已输入或查询到的数据,直到它被销毁。这意味着全局上下文将变得越来越大,对其的操作将变得越来越慢,直到您遇到内存不足的异常或您因年龄增长而死亡,因为这一切都变慢了。

当多个线程同时访问同一个上下文时,您还会遇到异常和许多错误。

4. 其他人是怎么做到的?

工厂通过依赖注入注入的DBContext;范围:

services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));

我希望我的回答对哪里有帮助。

于 2019-05-17T13:03:43.207 回答
1

从性能的角度来看,DbContext应该在just实际需要时创建,例如,当您需要在业务层内拥有用户列表时,您可以从 DbContext 创建一个实例,immediately dispose并在您的工作完成时创建它

using(var context=new DbContex())
{
var users=context.Users.Where(x=>x.ClassId==4).ToList();

}

context实例将在离开UsingBlock 后被释放。

但是如果你不立即处理它会发生什么?
DbContext本质上是缓存,查询越多,占用的内存块就越多。
在向您的应用程序泛滥的情况下,它会更加明显concurrent requests,在这种情况下,您占用内存块的每一毫秒都至关重要,更不用说一秒钟了。
您越推迟处理不必要的对象,您的应用程序就越容易崩溃!

当然,在某些情况下,您需要保留您的DbContext实例并在代码的另一部分使用它,但在同一Request Context.

我向您推荐以下链接以获取有关管理的更多信息DbContext
dbcontext Scope

于 2021-04-13T14:18:12.023 回答
0

现在我正在尝试这种方法,它可以避免在调用不使用它的操作时实例化上下文。

public abstract class BaseController : Controller
{
    public BaseController() { }

    private DatabaseContext _database;
    protected DatabaseContext Database
    {
        get
        {
            if (_database == null)
                _database = new DatabaseContext();
            return _database;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (_database != null)
            _database.Dispose();
        base.Dispose(disposing);
    }
}
于 2015-12-05T21:00:44.397 回答
0

您应该在每次 Save() 操作后立即处理上下文。否则每次后续保存都需要更长的时间。我有一个项目在一个循环中创建和保存复杂的数据库实体。令我惊讶的是,在循环中移动“using (var ctx = new MyContext()){...}”后,操作速度提高了三倍。

于 2020-02-26T14:59:16.597 回答
-1

这显然是一个较老的问题,但如果您使用 DI,您可以执行类似的操作并将所有对象的范围限定为请求的生命周期

 public class UnitOfWorkAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.BeginTransaction();
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.CloseTransaction(actionContext.Exception);
        }
    }
于 2016-07-23T23:26:03.953 回答