具有持久HTTP连接的IDbConnection生命周期管理

时间:2012-11-08 12:10:53

标签: asp.net-mvc structuremap signalr lifetime idbconnection

当我的ASP.NET MVC应用程序中存在持久的HTTP连接(如SignalR集线器)时,我在管理开放数据库连接的生命周期时遇到问题,其中StructureMap的作用域为HttpContext

我的DI容器StructureMap将打开的IDbConnection注入到多个服务中。为确保关闭并妥善处理这些数据库连接,我在ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects()事件上致电EndRequest

这对MVC控制器非常有用,直到需要数据库连接的服务注入到SignalR集线器中,从而为每个客户端保持持久的HTTP连接,并最终使连接池饱和。

如果我将IDbConnection范围限定为单个,则每个应用程序只打开一个连接,并且池不会饱和,但如果连接被锁定或超时,则为a bad idea

那么也许有一种方法可以自定义SignalR集线器的数据库连接范围?我尝试在每个Hub方法中解析一个服务实例,但这仍然在HttpContext范围内实例化数据库连接,并在调用客户端的集线器连接期间保持打开状态。

当存在持久的HTTP连接时,如何在HTTP范围的上下文中管理使用StructureMap的数据库连接的生命周期?

示例代码

典型服务

public class MyService
{
    private IDbConnection _con;
    public MyService(IDbConnection con)
    {
        _con = con;
    }

    public IEnumerable<string> GetStuff()
    {
        return _con.Select<string>("SELECT someString FROM SomeTable").ToList();
    }
}

典型的SignalR Hub

public class MyHub : Hub
{
    private MyService _service;
    public MyHub(MyService service)
    {
        _service = service; // Oh Noes! This will open a database connection
                            // for each Client because of HttpContext scope
    }

    public Task AddMessage()
    {
        var result = _service.GetStuff();
        // ...
    }
}

StructureMap配置

For<IDbConnection>()
    .HybridHttpOrThreadLocalScoped()
    .Use(() => BaseController.GetOpenConnection(MyConnectionString));

的Global.asax.cs

public class GlobalApplication : System.Web.HttpApplication
{
    public GlobalApplication()
    {
        EndRequest += delegate
        {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        };
    }
    // ...
 }

2 个答案:

答案 0 :(得分:1)

在SignalR 1.0.0 Alpha中,Hub的工具IDisposable。与Hub不同,SignalR HttpContext实例是短暂的,因此如果您使用IDbConnection的{​​{1}}方法关闭Hub,则不应该不必要地饱和你的连接池。

答案 1 :(得分:1)

使用瞬态数据库连接和嵌套StructureMap容器​​的解决方案

首先,在StructureMap中配置一个命名的瞬态数据库连接实例:

For<IDbConnection>()
    .Transient() // scope
    .Add(x => BaseController.GetOpenConnection(connectionString, IsDebugging()))
    .Named("Transient");

确保在默认实例之前配置此,否则它将覆盖默认实例。

其次,将IContainer注入SignalR中心,以便构建嵌套的StructureMap容器​​:

public class JobHub : Hub
{
    private readonly IContainer _container;

    public JobHub(IContainer container)
    {
        _container = container;
    }

    public Task DoStuff(string input)
    {
        // ...

在SignalR方法中实例化嵌套容器并解析命名的瞬态数据库连接:

        using (var httpRequestScope = _container.GetNestedContainer())
        {
            var transientConnection =
                    httpRequestScope.GetInstance<IDbConnection>("Transient");

使用.With<IDbConnection>(transientConnection)确保嵌套容器实例化的服务和存储库使用此连接:

            var myService = httpRequestScope
                .With<IDbConnection>(transientConnection)
                .GetInstance<MyService>();

            var result = myService.DoStuff(input);

            return Clients.addResult(result);
        }
    }
}

最后,作用域using (...)语句将确保嵌套容器自行清理,包括数据库连接。

这里的缺点是你为每个SignalR方法调用打开和关闭数据库连接,但由于连接是合并的,因此提前释放可能不会那么糟糕。您的里程数应取决于您的SignalR请求量。

您可能会抛弃嵌套容器,只是向DependencyResolver.Current询问命名连接实例,但是您可能必须记住明确关闭每个连接以防止泄漏。