MVC:EF6连接池和SQL Server CONTEXT_INFO
本文关键字:Server CONTEXT INFO SQL EF6 连接 MVC | 更新日期: 2023-09-27 17:59:06
在ASP.NET MVC应用程序中,我试图使用SQL Server的CONTEXT_INFO
来传递当前登录的用户,因此我的审核触发器不仅记录web服务器登录,还记录网站的登录。
不过,我很难确定当前用户将始终被输入数据库服务器上下文。
在后端,我已经设置好了一切,一个设置上下文的存储过程,一个拉取它的函数和DML触发器来记录,没问题。
应用端的参与度要高一些。我订阅了Database.Connection.StateChange
事件,这样我就可以捕获每个新打开的连接并相应地设置此上下文。
此外,为了能够在数据层中检索MVC站点的当前登录ID(它无法访问web项目),我向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上下文实例可能跨越多个登录用户,因为它是IIS应用程序池的一部分,而不是每个HttpContext
我对连接池以及如何打开/重新打开连接的了解还不够,因为我知道每次运行StateChange
处理程序时,我实际上都会从委托中检索新的UserID。
换句话说:单个连接是否可能在两个独立的HttpContext
实例的跨度上打开并使用?我相信是的,因为没有什么可以强制执行的(至少我不知道)。
我可以做些什么来确保每个连接都获得当前的HttpContext
?
(可能需要注意的是:EF本身之外没有UoW/Repository模式,数据上下文通常每个控制器实例化一次)
我看到:每个控制器一个上下文通常是不正确的。相反,我应该为每个请求使用一个上下文,这(除了其他优点之外)可以确保我的场景也能正确运行。
我找到了这个答案,它解释了背后的原因:每个web请求一个DbContext。。。为什么?
我找到了这个答案,它非常简洁地解释了如何通过BeginRequest
和EndRequest
实现:在ASP.NET MVC(没有IOC容器)中,每个请求一个DbContext
(第二个答案的代码粘贴在下面以防止链接腐烂)
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();
}
在EntityContext类中。。。
public class EntityContext
{
public static EntityContext Current
{
get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
}
}