MVC:EF6连接池和SQL Server CONTEXT_INFO

时间:2016-07-29 01:42:15

标签: c# asp.net-mvc entity-framework-6 connection-pooling

在ASP.NET MVC应用程序中,我尝试使用SQL Server的CONTEXT_INFO来传递当前登录的用户,以便我的审计触发器不仅记录Web服务器登录,而且也是网站的登录。

我无法确定当前用户是否总会被送入数据库服务器上下文。

在后端我设置了一切,设置上下文的sproc,拉动它的函数和DML触发器进行记录,没问题。

应用程序结束更多参与。我订阅了Database.Connection.StateChange事件,因此我可以捕获每个新打开的连接并相应地设置此上下文。

此外,为了能够在数据层(无法访问Web项目)中检索MVC站点的当前登录ID,我向EF构造函数提供了一个委托,该构造函数将返回用户ID。这也意味着我设置的任何其他外围项目也需要这种依赖,并且它在Web开发期间保留了大部分实现细节:

public class CoreContext : DbContext
{

    Func<int> _uidObtainer;

    public CoreContext(Func<int> uidObtainer) : base(nameof(CoreContext)) { construct(uidObtainer); }
    public CoreContext(Func<int> uidObtainer, string connection) : base(connection) { construct(uidObtainer); }

    void construct(Func<int> uidObtainer) {

        // disallow updates of the db from our models
        Database.SetInitializer<CoreContext>(null);

        // catch the connection change so we can update for our userID
        _uidObtainer = uidObtainer;
        Database.Connection.StateChange += connectionStateChanged;

    }

    private void connectionStateChanged(object sender, System.Data.StateChangeEventArgs e) {

        // set our context info for logging

        if (e.OriginalState == System.Data.ConnectionState.Open || 
            e.CurrentState != System.Data.ConnectionState.Open) {
            return;
        }

        int uid = _uidObtainer();

        var conn = ((System.Data.Entity.Core.EntityClient.EntityConnection)sender).StoreConnection;
        var cmd = conn.CreateCommand();
        cmd.CommandText = "audit.SetContext";
        cmd.CommandType = System.Data.CommandType.StoredProcedure;
        cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@DomainUserID", uid));
        cmd.ExecuteNonQuery();

    }

    // etc etc...

在我的MVC项目中,我将拥有如下代码:

context = new Data.CoreContext(() => AppService.UserID());

(利用易于访问的方法作为代理传递,而代理又从HttpContext.Current.User读取)

这一切都很好,除了一个未知的:

我知道EF Context实例可以跨多个登录用户,因为它是IIS应用程序池的一部分,而不是每个HttpContext

我不知道关于连接池以及如何打开/重新打开连接是安全的,因为我知道每次StateChange处理程序运行时,我实际上都是从委托中检索新的UserID。

不同地说:单个连接是否可以打开并在两个单独的HttpContext实例的范围内使用?我相信是的,看到如何执行其他任何事情(至少不是我所知道的)。

我该怎么做才能确保每个连接都获得当前的HttpContext

(可能相关的说明:EF本身之外没有UoW / Repository模式,数据上下文通常每个控制器实例化一次)

1 个答案:

答案 0 :(得分:0)

我看到:每个控制器的一个上下文通常是不正确的。相反,我应该为每个请求使用一个上下文,除了其他优点之外,还确保我的场景也能正常运行。

我找到了这个答案,解释了其背后的原因:One DbContext per web request... why?

我找到了这个答案,它简明扼要地解释了如何通过mainBeginRequest来实现:One DbContext per request in ASP.NET MVC (without IOC container)

(下面粘贴第二个答案的代码以防止链接)

EndRequest

在你的EntityContext类中......

protected virtual void Application_BeginRequest()
{
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}

protected virtual void Application_EndRequest()
{
    var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
    if (entityContext != null)
        entityContext.Dispose();
}