每个ASP.NET会话锁定

问题描述:

确定一些背景。我有一些与此类似:每个ASP.NET会话锁定

class ConnectionFactory 
{ 
    public IConnection Connect() 
    { 
     if (User.IsAuthenticated) { 
      return InternalConnect(User.Username, null); 
     } 
     return null; 
    } 
    public IConnection Connect(string username, string password) 
    { 
     return InternalConnect(username, password); 
    } 
    private IConnection InternalConnect(string username, string password) 
    { 
     IConnection connection; 
     var cacheKey = Session[CacheKeySessionKey] as string; 

     if (!string.IsNullOrEmpty(cacheKey)) { 
      connection = HttpCache[cacheKey] as IConnection; 
     } 

     if (!IsGoodConnection(connection) { 
      connection = MakeConnection(username, password); // very costly 
      cacheKey = Session[CacheKeySessionKey] = // some key 
      HttpCache[cacheKey] = connection; 
     } 

     return connection; 
    } 
    private bool IsGoodConnection(IConnection conn) 
    { 
     return conn != null && conn.IsConnected; 
    } 
} 

目前,我正在运行到一个并发问题,其中即Connect()被称为多次,每次请求创建多个IConnection秒。我只需要一个。它正在使用IoC容器注入各种实例。 MakeConnnection因为它增加了一个WCF频道而成本很高。

我的问题是:怎样才能锁定每个会话的InternalConnect电话?我不认为每个请求锁定是正确的方式,因为每个用户可能会发生多个请求。我当然不想锁定每一个电话,因为这会导致糟糕的表现。

我认为这样做是一个坏主意:

lock(Session.SessionID) 
{ 
    // Implementation of InternalConnect 
} 

注:用户名和密码超载就是我所说的只登录。

+0

isgoodconnection是什么意思? – Beatles1692 2012-02-16 13:17:10

+0

@ Beatles1692 - 它只是一个方法来检查它不是null或者它是否被断开。 – 2012-02-16 13:18:40

+0

您使用的是什么IoC容器?你可以展示你的IoC配置吗? – 2012-02-16 13:23:02

这只是未经测试的代码,从我的头顶,但它可能工作?

// globally declare a map of session id to mutexes 
static ConcurrentDictionary<string, object> mutexMap = new ConcurrentDictionary(); 

// now you can aquire a lock per session as follows 
object mutex = mutexMap.GetOrAdd(session.SessionId, key => new object()); 
lock(mutex) 
{ 
    // Do stuff with the connection 
} 

您将需要找到一种方法来清除旧会话出mutexMap的,但应该不会太困难。

+0

我非常喜欢这种模式。谢谢! – 2012-02-16 16:36:06

+0

你可不只是将锁对象添加到用户会话? – rdans 2015-09-28 10:13:12

+0

@rdans,这将只适用于内存会话存储,因为其他会话提供程序将序列化对象作为保存过程的一部分。 – 2015-09-29 10:57:06

我将ninject创建类作为一个单身人士,然后将连接存储在工厂类本身。

当您拨打到InternalConnect的电话时,请检查_connection是否为空。如果是的话,新的一个新的IConnect并将其分配给_connection

+0

这就是我基本上在做什么。 'ConnectionFactory'是唯一一个如何知道连接是否为空的连接。 – 2012-02-16 13:31:51

这里有一个建议: 有连接制造商对象,它具有MakeConnection的逻辑,并且它以通常的方式锁定整个过程。当会话开始时,它将一个连接制造商存储在其中并在内部连接方法中调用此方法。

这里就是我的意思:

public class ConnectionMaker 
{ 
private object _lock=new object(); 

public IConnection MakeConnection() 
{ 
lock(_lock) 
{ 
// 
} 
} 
} 

,然后在在session_start你可以有:

Session["ConnectionMaker"]=new ConnectionMaker(); 

,然后在内部连接:

if(! IsGoodConnection(connection)) 
{ 
var connectionMaker=Session["ConnectionMaker"] as ConnectionMaker; 
connection=connectionMaker.MakeConnection(); 
.... 
} 
+0

我试图保持这global.asax,因为这全是在图书馆。所以'Session_Start'不适用。 – 2012-02-16 13:37:02

+0

好吧,您可以在第一次将图书馆添加到会话中,例如在您的登录过程中 – Beatles1692 2012-02-16 13:41:43

另一种选择是直接在每个用户会话中存储一个对象。

锁应该是这样的:

lock (Session["SessionLock"]) { 
    // DoStuff 
} 

,你可以创建当每个会话启动

protected void Session_Start(object sender, EventArgs e) 
{ 
    Session["SessionLock"] = new object(); 
} 

做这种方式在Global.asax对象是指锁定对象会话结束后自动删除。