c# - Linq to Sql : Change Database for each connection

标签 c# asp.net asp.net-mvc linq connection-pooling

我正在开发一个 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“实例”,而不是在每次请求它时创建一个新实例,而不是使用缓存/共享实例并将连接字符串注入(inject) setter/getter 。

但是 - 我很确定这对您的合并问题没有帮助。 SQL Server 驱动程序根据不同的连接字符串值来缓存连接——因为这些值仍在变化,所以您又回到了连接字符串缓存中有大量事件连接的状态,这可能会导致大量缓存未命中,从而导致连接速度变慢。

关于c# - Linq to Sql : Change Database for each connection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21789814/

相关文章:

c# - Specflow/Gherkin 测试 XML 文档

C#减慢随机数GUI显示

c# - 部分用户控制

asp.net-mvc - ASP.NET MVC [RegularExpression] 属性在整个字符串匹配时不起作用

asp.net-mvc - IIS 7.5 上的 MVC5 路由错误 (404.0) 错误

asp.net-mvc - @Html.Raw 到一般的 javascript 函数调用错误,如果变量带有空格

C#:为什么 .ToString() 将文本更快地附加到转换为字符串的 int 中?

C# 在发布版本中不起作用

asp.net - VS2008中点击停止调试后asp.net代码仍然执行

asp.net - asp.net、javascript 和 silverlight 的共享本地化?