簡體   English   中英

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

[英]IDbConnection lifecycle management with persistent HTTP connections

當我的ASP.NET MVC應用程序(例如SignalR集線器)中存在持久的HTTP連接時,我無法管理范圍為HttpContext StructureMap的開放數據庫連接的生命周期。

我的DI容器StructureMap將一個開放的IDbConnection注入幾個服務中。 為了確保這些數據庫連接已關閉並正確處理,我在EndRequest事件上調用ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects()

這對於MVC控制器非常有用,直到需要將數據庫連接的服務注入到SignalR集線器中為止,SignalR集線器將為每個客戶端保持持久的HTTP連接打開,並最終使連接池飽和。

如果我將IDbConnection作用域IDbConnection為一個單例,則每個應用程序只能打開一個連接,並且該池不會飽和,但這是一個壞主意 ,以防連接被鎖定或超時。

因此,也許有一種方法可以自定義SignalR集線器的數據庫連接范圍? 我嘗試在每個Hub方法中解析服務實例,但這仍然在HttpContext范圍內實例化數據庫連接,並在調用客戶端的Hub連接期間將其保持打開狀態。

當周圍存在持久的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集線器

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();
        };
    }
    // ...
 }

在SignalR 1.0.0 Alpha中, Hub的實現IDisposable SignalR Hub實例是短暫的,與HttpContext不同,因此,如果在HubDispose方法中關閉IDbConnection ,則不必不必要地使連接池飽和。

使用臨時數據庫連接和嵌套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作為命名的連接實例,但是您可能必須記住顯式關閉每個連接以防止泄漏。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM