2012-02-16 52 views
7

確定一些背景。我有一些與此類似:每個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

回答

10

這只是未經測試的代碼,從我的頭頂,但它可能工作?

// 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

0

我將ninject創建類作爲一個單身人士,然後將連接存儲在工廠類本身。

當您撥打到InternalConnect的電話時,請檢查_connection是否爲空。如果是的話,新的一個新的IConnect並將其分配給_connection

+0

這就是我基本上在做什麼。 'ConnectionFactory'是唯一一個如何知道連接是否爲空的連接。 – 2012-02-16 13:31:51

0

這裏有一個建議: 有連接製造商對象,它具有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

0

另一種選擇是直接在每個用戶會話中存儲一個對象。

鎖應該是這樣的:

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

,你可以創建當每個會話啓動

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

做這種方式在Global.asax對象是指鎖定對象會話結束後自動刪除。