2014-02-14 49 views
4

我正在使用Linq to SQL連接到大約2000個數據庫之一的ASP.NET MVC應用程序。我們注意到,該應用程序花費大量時間到數據庫的連接我們的分析工具,而且我懷疑這是部分原因是由於連接池碎片如下所述:http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspxLINQ to Sql:更改每個連接的數據庫

Many Internet service providers host several Web sites on a single server. They may use a single database to confirm a Forms authentication login and then open a connection to a specific database for that user or group of users. The connection to the authentication database is pooled and used by everyone. However, there is a separate pool of connections to each database, which increase the number of connections to the server.

There is a relatively simple way to avoid this side effect without compromising security when you connect to SQL Server. Instead of connecting to a separate database for each user or group, connect to the same database on the server and then execute the Transact-SQL USE statement to change to the desired database.

我想實現這個解決方案在Linq to Sql中,所以我們有更少的開放連接,所以當我們需要時,更有可能在池中有可用的連接。爲此,我需要在Linq每次嘗試運行查詢時更改數據庫。有沒有什麼辦法可以在不重構整個應用程序的情況下完成這個任務?目前,我們只是爲每個請求創建一個數據上下文,並且該數據上下文可能會打開和關閉多個連接。每次打開連接時,我都需要告訴它要使用哪個數據庫。

我當前的解決方案或多或少像this one - 它將SqlConnection對象封裝在繼承自DbConnection的類中。這允許我重寫Open()方法並在打開連接時更改數據庫。它適用於大多數情況下確定的,但在這使得許多更新的請求,我得到這個錯誤:

System.InvalidOperationException: Transaction does not match connection

我的想法是,我會再以同樣的方式裹DbTransaction對象是什麼我的SqlConnection一樣,並確保其連接屬性將指向包裝的連接對象。修正了上述錯誤,但引入了一個新的DbCommand無法將我的包裝連接強制轉換爲SqlConnection對象的新特性。於是我也包裝了DbCommand,現在我得到了關於未初始化的DbCommand對象事務的新的令人興奮的錯誤。

總之,我覺得我追逐的是具體的錯誤,而不是真正理解正在發生的事情。我是否在這個包裝戰略的正確軌道上,還是有更好的解決方案,我錯過了?

這裏是我的三個包裝類的更有趣的部分:

public class ScaledSqlConnection : DbConnection 
{ 
    private string _dbName; 
    private SqlConnection _sc; 
    public override void Open() 
    { 
     //open the connection, change the database to the one that was passed in 
     _sc.Open(); 
     if (this._dbName != null) 
      this.ChangeDatabase(this._dbName); 
    } 
    protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) 
    { 
     return new ScaledSqlTransaction(this, _sc.BeginTransaction(isolationLevel)); 
    } 

    protected override DbCommand CreateDbCommand() 
    { 
     return new ScaledSqlCommand(_sc.CreateCommand(), this); 
    } 
} 

public class ScaledSqlTransaction : DbTransaction 
{ 
    private SqlTransaction _sqlTransaction = null; 
    private ScaledSqlConnection _conn = null; 

    protected override DbConnection DbConnection 
    { 
     get { return _conn; } 
    } 
} 

public class ScaledSqlCommand : DbCommand 
{ 
    private SqlCommand _cmd; 
    private ScaledSqlConnection _conn; 
    private ScaledSqlTransaction _transaction; 
    public ScaledSqlCommand(SqlCommand cmd, ScaledSqlConnection conn) 
    { 
     this._cmd = cmd; 
     this._conn = conn; 
    } 
    protected override DbConnection DbConnection 
    { 
     get 
     { 
      return _conn; 
     } 
     set 
     { 
      if (value is ScaledSqlConnection) 
       _conn = (ScaledSqlConnection)value; 
      else 
       throw new Exception("Only ScaledSqlConnections can be connections here."); 
     } 
    } 

    protected override DbTransaction DbTransaction 
    { 
     get 
     { 
      if (_transaction == null && _cmd.Transaction != null) 
       _transaction = new ScaledSqlTransaction(this._conn, _cmd.Transaction); 
      return _transaction; 
     } 
     set 
     { 
      if (value == null) 
      { 
       _transaction = null; 
       _cmd.Transaction = null; 
      } 
      else 
      { 
       if (value is ScaledSqlTransaction) 
        _transaction = (ScaledSqlTransaction)value; 
       else 
        throw new Exception("Don't set the transaction of a ScaledDbCommand with " + value.ToString()); 
      } 
     } 
    } 
} 

}

回答

0

我想我找到了適合我的情況的解決方案。我沒有包裝SqlConnection並重寫Open()來更改數據庫,而是將DBContext傳遞給一個新的SqlConnection並訂閱連接的StateChanged事件。當狀態改變時,我檢查連接是否剛剛打開。如果是這樣,我調用SqlConnection.ChangeDatabase()將其指向正確的數據庫。我測試了這個解決方案,它似乎工作 - 我看到所有數據庫只有一個連接池,而不是每個已訪問數據庫的池。我認爲這不是理想應用中的理想解決方案,但是對於這個應用程序的結構,我認爲它應該在相對較少的成本下做出體面的改進。

2

我不認爲這會工作過一個單一的共享連接。

LINQ to SQL最適合於工作單元類型連接 - 創建連接,完成原子分組工作並儘快關閉連接並重新打開下一個任務。如果你這樣做,然後傳遞一個連接字符串(或使用只傳遞表名的自定義構造函數)是非常簡單的。

如果考慮到您的應用程序存在問題,您可以使用getter來操縱緩存的DataContext'實例',而是每次請求時創建一個新實例,而不是使用緩存/共享實例,並將連接字符串插入Getter。

但 - 我敢肯定,這不會幫助您的池問題。 SQL Server驅動程序基於不同的連接字符串值來緩存連接 - 由於值仍然在變化,因此您很快就會在連接字符串緩存中激活大量連接,這可能會導致大量緩存未命中,從而導致連接速度變慢。