我有一个多线程服务,它使用SMO读取表名和列名,也可能改变数据库。我们目前正在研究Azure并继续在那里尝试代码。它已经使用SQL Server很长一段时间了。 在Azure SQL中,当只使用一个线程时,一切正常。当第二个线程在第一个线程仍在执行时开始读取时,会抛出异常:
System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader()
at Microsoft.SqlServer.Management.Common.ConnectionManager.ExecuteTSql(ExecuteTSqlAction action, Object execObject, DataSet fillDataSet, Boolean catchException)
at Microsoft.SqlServer.Management.Common.ServerConnection.GetExecuteReader(SqlCommand command)
at Microsoft.SqlServer.Management.Smo.ExecuteSql.GetDataReader(String query, SqlCommand& command)
at Microsoft.SqlServer.Management.Smo.DataProvider.SetConnectionAndQuery(ExecuteSql execSql, String query)
at Microsoft.SqlServer.Management.Smo.ExecuteSql.GetDataProvider(StringCollection query, Object con, StatementBuilder sb, RetriveMode rm)
at Microsoft.SqlServer.Management.Smo.SqlObjectBase.FillData(ResultType resultType, StringCollection sql, Object connectionInfo, StatementBuilder sb)
at Microsoft.SqlServer.Management.Smo.SqlObjectBase.FillDataWithUseFailure(SqlEnumResult sqlresult, ResultType resultType)
at Microsoft.SqlServer.Management.Smo.SqlObjectBase.BuildResult(EnumResult result)
at Microsoft.SqlServer.Management.Sdk.Sfc.Environment.GetData()
at Microsoft.SqlServer.Management.Sdk.Sfc.Environment.GetData(Request req, Object ci)
at Microsoft.SqlServer.Management.Sdk.Sfc.Enumerator.GetData(Object connectionInfo, Request request)
at Microsoft.SqlServer.Management.Smo.ExecutionManager.GetEnumeratorDataReader(Request req)
at Microsoft.SqlServer.Management.Smo.SqlSmoObject.GetInitDataReader(String[] fields, OrderBy[] orderby)
at Microsoft.SqlServer.Management.Smo.SqlSmoObject.ImplInitialize(String[] fields, OrderBy[] orderby)
at Microsoft.SqlServer.Management.Smo.SqlSmoObject.Initialize(Boolean allProperties)
at Microsoft.SqlServer.Management.Smo.SmoCollectionBase.InitializeChildObject(ObjectKeyBase key)
at Microsoft.SqlServer.Management.Smo.SmoCollectionBase.GetObjectByKey(ObjectKeyBase key)
at Microsoft.SqlServer.Management.Smo.TableCollection.get_Item(String name)
激烈的谷歌搜索没有产生任何可行的东西,使用SMO应该没问题,如果你不在线程之间分享任何东西。我已经更新到微软新的官方SMO 2017 nuget包,它没有帮助。第一个线程完成后,第二个线程运行正常,因此只有一个线程可以读取数据库。 非常感谢所有帮助。
我用来读取数据库的代码如下:
private Database GetDataBase()
{
//do not have to get this from DB every time on same thread.
if (_db != null) return _db;
//GetConnection just returns new SqlConnection
using (var conn = _executor.GetConnection() as SqlConnection)
{
conn.Open();
var sConn = new ServerConnection(conn);
var server = new Server(sConn);
_db = server.Databases[sConn.DatabaseName];
_db.DefaultSchema = "dbo";
//Problem with Azure:
//Enumerate these to ensure immediate crash, when 2nd thread kicks in
//and 1st thread is still executing.
//Works fine with SQL Server
foreach (Table t in _db.Tables)
{
foreach (Column c in t.Columns)
{
}
}
}
return _db;
}