11月12日,驚喜地發現SqlClient(System.Data.SqlClient.dll)跨平臺了(對應的nuget包包是runtime.unix.System.Data.SqlClient),終于可以在Linux上基于.NET Core運行ASP.NET 5程序訪問SQL Server數據庫了。
于是,立馬更新dnx至rc2,用之前已經寫好的、用EF7訪問SQL Server數據庫的ASP.NET 5示例程序,分別在2臺Linux服務器上進行測試。但測試時遇到了一個非常奇怪的問題:其中1臺Linux服務器上可以正常訪問SQL Server數據庫,而另外1臺Linux服務器上運行時總是出現這樣的錯誤:
DllNotFoundException: Unable to load DLL 'api-ms-win-core-localization-obsolete-l1-2-0.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E) System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
這2臺Linux服務器分別訪問的是2臺不同的SQL Server,能正常訪問的服務器用的是自己在純英文操作系統上安裝的SQL Server,不能正常訪問的服務器用的是阿里云RDS。
當時以為是這2臺Linux服務器的系統環境不一樣引起的,于是分別重裝操作系統,重新安裝dnx,問題依舊。。。折騰了幾天,實在找不出原因,就將問題放之一邊。
今天(11月19日),微軟正式發布了ASP.NET 5 RC1,于是又基于ASP.NET 5 RC1測試了一下,問題還是依舊。
但是今天在測試時,進行了一個之前遺漏的測試,在出問題的服務器上訪問不出問題的服務器所用的SQL Server,結果問題立馬消失。
太奇怪了!怎么會與SQL Server有關?于是將阿里云RDS換成了另外1臺自己安裝的SQL Server,但也是同樣的問題。現在問題變成了:同樣的應用程序,訪問1臺SQL Server正常,訪問另一臺就出錯。于是將解決問題的焦點放到了比較這2臺SQL Server的不同之處,但通過SQL Profiler進行跟蹤,未發現有任何不同。
后來,用EF遷移命令訪問數據庫:
dnx ef database update
也是同樣的錯誤:
System.DllNotFoundException: Unable to load DLL 'api-ms-win-core-localization-obsolete-l1-2-0.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E) at System.Data.LocaleInterop.LCIDToLocaleName(UInt32 Locale, StringBuilder lpName, Int32 cchName, Int32 dwFlags) at System.Data.LocaleInterop.LcidToLocaleNameInternal(Int32 lcid) at System.Data.LocaleInterop.GetDetailsInternal(Int32 lcid) at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory) at System.Data.SqlClient.TdsParser.GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) at System.Data.SqlClient.TdsParser.TryProcessEnvChange(Int32 tokenLength, TdsParserStateObject stateObj, SqlEnvChange[]& sqlEnvChange)
但是調用棧的信息不一樣,當看到 TdsParser.GetCodePage 方法,突然想到是不是與Codepage有關?但是SQL Profiler看不到任何與Codepage的信息。。。
是不是漏掉了什么?是不是在SqlClient與SQL Server交互時,還有一些信息被漏掉了?得要看到它們之間的所有交互信息,怎么看呢?
看網絡交互信息,最有效的方法非網絡抓包莫屬(所以說抓包是程序員的基本功之一)。于是在SQL Server服務器上用Wireshark抓包。。。
果然逮著了Codepage相關的東西,在SqlClient登錄至SQL Server之后,SQL Server響應給客戶端的內容中有這樣的信息:
看!Codepage: 2052,它表示的是中文(Chinese - China),問題很可能與這里返回的Codepage有關。
但SQL Server中什么設置會影響到這里返回的Codepage值呢?搜索"windows change sql server codepage",找到了線索,原來就是Database Collation的設置。
比較了一下這2臺SQL Server中對應數據庫的Collation設置,沒出問題的SQL Server設置的是SQL_Latin1_General_CP1_CI_AS,出問題的SQL Server設置的是Chinese_PRC_CI_AS。
于是將Collation由Chinese_PRC_CI_AS改為SQL_Latin1_General_CP1_CI_AS,問題立馬解決!
當然,問題的根源不是SQL Server的Collation設置,而是跨平臺的System.Data.SqlClient.dll不能正確處理Collation為Chinese_PRC_CI_AS的情況,這算是corefx中System.Data.SqlClient實現的一個bug。
不管怎么樣,總算找到了問題的真正原因,暫時也有臨時解決方法,在Linux服務器上基于.NET Core運行ASP.NET 5程序訪問SQL Server已經成為現實。
【相關博文】
.NET跨平臺之旅:升級至ASP.NET 5 RC1,Linux上訪問SQL Server數據庫
【更新】
該問題已被修復,詳見 dotnet/corefx/pull/4958
文章列表