2011-12-12 99 views
4

我遇到了一些我們正在移植到Mono的應用程序的問題。Mono是否將.NET以不同的方式處理System.Data.SqlCommands?

(爲了參考,.NET運行時是4.0,和單版本是2.6.7。)

編輯:在Mono 2.10.2

此問題仍然存在作爲啓動的一部分的應用程序,它將數據讀入內存。對於這部分,我只是使用內聯SQL命令,但出於某種原因,我看到Linux/Mono(當整個事情在Windows/.NET中工作時)的行爲不一致。

我有一個查詢,在某些情況下工作正常,但在其他情況下沒有。

這個特殊的例子不工作:

var cmd = new SqlCommand("SELECT ID, Name, VATTerritoryID, NativeCurrencyID FROM PricingZones", conn); 
var reader = cmd.ExecuteReader(); 

var objectToLoad = new SomeObjectType(); 

while (reader.Read()) 
{ 
    objectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("ID")); 
    objectToLoad.Property2 = reader.GetString(row.GetOrdinal("Name")); 
    objectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("VATTerritoryID")); 
    objectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("NativeCurrencyID")); 
} 

編輯:爲了便於比較,這裏有一個,它的工作

var cmd = new SqlCommand("SELECT VATTerritoryID, ProductDescriptionID, VATBandID FROM VATTerritoryBandExceptions", conn); 
var reader = cmd.ExecuteReader(); 

var someOtherObjectToLoad = new SomeOtherObjectType(); 

while (reader.Read()) 
{ 
    someOtherObjectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("VATTerritoryID")); 
    someOtherObjectToLoad.Property2 = reader.GetString(row.GetOrdinal("ProductDescriptionID")); 
    someOtherObjectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("VATBandID")); 
} 

我有可能懷疑有向下的差異:

  • 外殼(因爲我知道這是不同的Windows/Linux版),但把一切爲小寫一直沒有解決的問題
  • 列名(也許單更在乎的保留字?),但似乎與[名]或「名稱」更換名稱並沒有做任何的不同

我曾在第一種情況下錯誤是:

[IndexOutOfRangeException: Array index is out of range.] 
at System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i) 

提示有沒有「列1」,在返回的結果集。

(編輯:更新這部分有點爲清楚起見)

奇怪的是,如果我這樣做:

var cmd = new SqlCommand("SELECT ID, Name, VATTerritoryID, NativeCurrencyID FROM PricingZones", conn); 
var reader = cmd.ExecuteReader(); 

var objectToLoad = new SomeObjectType(); 

while (reader.Read()) 
{ 
    Console.WriteLine("First row, first column is " + row.GetValue(0)); 
    Console.WriteLine("First row, second column is " + row.GetValue(1)); 
    Console.WriteLine("First row, third column is " + row.GetValue(2)); 
    Console.WriteLine("First row, fourth column is " + row.GetValue(3)); 
} 

輸出是:

First row, first column is 0 
First row, second column is New 
Array index is out of range. 

我認爲一些奇怪的事情在這種情況下Mono框架正在發生,但是我找不到相關的錯誤報告,而且我無法確定爲什麼這隻發生在某些情況下但不是其他人!其他人有過類似的經歷嗎?

編輯:我已經改變了一些報表,以匹配失敗的查詢完全,萬一有與保留字或類似的問題。請注意,我在第二種情況下發出的查詢確實需要四列,看起來只有兩個非常奇怪的查詢返回(0 | New)。

+0

你有*混凝土*它失敗的例子? –

+0

如果您使用'(Int32)reader [「column1」]' –

+0

如果您確定在這兩種情況下使用的是相同的MS SQL Server數據庫,該怎麼辦? – LukeH

回答

2

好夥伴我設法重現你的問題。你有一個線程問題。這是我唯一的想法,可能是這個問題的原因,我設法重現它。

爲了解決這個問題,你需要做到以下幾點:

  1. 確保每位讀者都有解析 數據後reader.Close()調用。

  2. 使用下面的代碼做線程安全的呼叫:

    Object executeLock = new Object(); 
    
    private IDataReader ExecuteThreadSafe(IDbCommand sqlCommand) 
    { 
        lock (executeLock) 
        { 
         return sqlCommand.ExecuteReader(CommandBehavior.CloseConnection); 
        } 
    } 
    

看起來像單有不同的實現比.NET SQL對象。確實,我無法在Windows上重現它!

+0

好吧,這個作品!謝謝 – simple

0

單聲道不是.NET,特別是與早期版本有很大的區別。連接到SQL的根方法是在C#中使用TDS(表格數據流)實現,對於早期版本的Mono(和TDS),可能會導致很多問題。幾乎所有關於SQL和數據的基本類都有差異,可能會導致異常。也許值得使用Mono 2.10+,因爲Mono團隊不斷改進整個項目。

+0

我沒有完全控制我正在使用的盒子,所以它不是最簡單的選擇,但是如果調查證明沒有結果,我肯定會考慮升級單聲道版本 - 謝謝 – simple

+0

不幸的是,更改單聲道版本hasn沒有改變任何東西 - 無論如何感謝 – simple

+0

太糟糕了。抱歉。如果你找到時間,下載源代碼會很有趣,看看並執行代碼分析。 – 2011-12-13 13:57:19

1

我會開始試圖弄清楚究竟在哪裏存在差異。

首先,你有下面的代碼:

objectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("column1")); 
objectToLoad.Property2 = reader.GetString(row.GetOrdinal("column2")); 
objectToLoad.Property3 = reader.GetString(row.GetOrdinal("column ")); //Is the space on the end intended? 

我相信你說的前兩行的工作,而第三線炸燬。我們首先需要弄清楚這在哪裏爆炸。我想嘗試:

int test = row.GetOrdinal("column "); 

是否test等於有效的列索引?如果沒有,那是你的問題。確保這是確切的列名,嘗試不同的外殼等。如果指數有效,嘗試:

object foo = reader[test]; 
Console.WriteLine(foo.GetType().Name); 

弄清楚此列的數據類型是什麼。也許有鑄造問題。

如果失敗了,我會嘗試將讀取器加載到DataSet對象中,以便您可以更仔細地查看確切的模式。

如果您發現Mono和.NET Framework之間的行爲有所不同,Mono團隊通常非常願意解決這些問題。我強烈建議將此作爲錯誤提交。

希望這會有所幫助!

+0

我原來的問題並不清楚 - 我相信這是GetOrdinal失敗了。我要重試這個縮小範圍。 – simple

+0

請參閱上文,以更好地解釋我的問題 - GetValue調用表明我收回的數據集的確很奇怪。 – simple

0

嘗試訪問你的讀者以這種方式:

reader["column1isastring"].ToString(); 
(Int32)reader["column2isInt32"]; 

此外,作爲一個側面說明,請確保您使用的是「使用」指令一次性的對象。我不確定莫諾是否實現了這一點,但值得一試。使用指令的作用是一旦完成使用即清理一次性對象。它非常方便,可以用於清晰的代碼。簡單的例子:

using (MySqlCommand command = new MySqlCommand("SELECT column1, column2, column FROM tablename", conn)) 
{ 
    try 
    { 
     conn.Open(); 
     using (MySqlDataReader reader = command.ExecuteReader()) 
     { 
       reader.Read(); 
       var someString = reader["column1isastring"].ToString(); 
       var whatever = (Int32)reader["column2isInt32"]; 
     } //reader closes and disposes here 
    } 
    catch (Exception ex) 
    { 
     //log this exception 
    } 
    finally 
    { 
     conn.Close(); 
    } 
} //conn gets closed and disposed of here 

另外,如果你要,可直接到達你的命令的用戶輸入,請確保您使用了MySQLParameter類來防止惡意參數掉落表,例如。

+0

我會給它一個機會,但我的印象是集合[index]符號只是語法上的糖,並且正在調用與collection.GetValue(index)相同的引擎。 – simple

+0

collection [index]表示法不能解決問題。 – simple

0

我使用的是Mono 4.0,而且我有類似的問題。我的研究表明,這個問題最可能的原因是Mono中連接池的實現存在缺陷。至少,如果我關閉池,問題就會消失。

要關閉連接池,您需要將以下字符串添加到您的連接字符串:Pooling=false;

之後,連接對象的創建應該看起來像:

var conn = new SqlConnection("Server=localhost; Database=somedb; user=user1; password=qwerty; Pooling=false;"); 
相關問題