c# – Linq to Sql:为每个连接更改数据库

我正在开发一个ASP.NET MVC应用程序,它使用
Linq to SQL连接到大约2000个数据库之一.我们在分析工具中注意到应用程序花了很多时间连接到数据库,我怀疑这部分是由于连接池碎片如下所述:
http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx

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 to Sql尝试运行查询时更改数据库.有没有办法在不重构整个应用程序的情况下实现这一目标?目前,我们只是为每个请求创建一个数据上下文,并且该数据上下文可以打开和关闭许多连接.每次打开连接时,我都需要告诉它使用哪个数据库.

我当前的解决方案或多或少像this one – 它将一个SqlConnection对象包装在一个继承自DbConnection的类中.这允许我覆盖Open()方法并在打开连接时更改数据库.它适用于大多数情况,但在进行许多更新的请求中,我收到此错误:

System.InvalidOperationException: Transaction does not match
connection

我的想法是,然后我将以与我对SqlConnection所做的类似的方式包装DbTransaction对象,并确保其连接属性将指向包装的连接对象.这解决了上面的错误,但引入了一个新的错误,其中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());
            }
        }
    }
}

}

最佳答案 我认为这不会影响单个共享连接.

LINQ to SQL最适用于工作单元类型连接 – 创建连接,执行原子分组工作并尽快关闭连接并重新打开以执行下一个任务.如果你这样做,那么传入连接字符串(或使用只传递表名的自定义构造函数)非常简单.

如果将应用程序分解为一个问题,则可以使用getter来操作缓存的DataContext’实例’,而是在每次请求时创建一个新实例,而不是使用缓存/共享实例并在Getter中注入连接字符串.

但是 – 我很确定这对你的汇集问题没有帮助. SQL Server驱动程序根据不同的连接字符串值缓存连接 – 由于值仍在变化,您可以直接在连接字符串缓存中激活大量连接,这可能会导致大量缓存未命中,从而导致连接速度变慢.

点赞