Sql-Server
為什麼使用外連接選擇 rowversion 永遠不會返回 null
使用 SQL Server 2014。
給定帶有rowversion列的表,要加入的其他表和這樣的選擇:
CREATE TABLE dbo.FooTable( [Id] [int] IDENTITY(1,1) NOT NULL, [RowVersion] [rowversion] NULL, CONSTRAINT [PK_FooTable] PRIMARY KEY CLUSTERED ( [Id] ASC )) CREATE TABLE dbo.BarTable( [Id] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_BarTable] PRIMARY KEY CLUSTERED ( [Id] ASC )) Insert into BarTable default values GO SELECT * FROM FooTable ft FULL OUTER JOIN BarTable bt on ft.Id = Bt.Id
選擇的結果是:
Id RowVersion Id NULL 0x 1
在與 BarTable 不匹配的地方,RowVersion 列不為空,而是 0x。
這讓我們用來反序列化應用伺服器上的結果的 Dapper 感到困惑,認為它應該為行的 FooTable 部分構造一個對象——一個無效的對象,只包含一個空字節數組。
這種相當出乎意料的行為有什麼原因嗎?
有沒有比在 sql 語句中使用 CASE 將值轉換回 null 或在應用程式碼中處理它更智能的解決方案?
這似乎是 .NET 的 SqlClient 中的一個錯誤。該列被返回為空
byte[]
,即使它null
在結果中被標記為。例如
using System; using System.Data.Odbc; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Threading; using System.Threading.Tasks; using System.Transactions; namespace ConsoleApp14 { class Program { static void Main(string[] args) { using (var con = new SqlConnection("Server=localhost;database=tempdb;Integrated Security=true")) { con.Open(); var cmd = con.CreateCommand(); cmd.CommandText = "select cast(null as rowversion) rv"; using (var rdr = cmd.ExecuteReader()) { rdr.Read(); var allowDbNull = rdr.GetColumnSchema()[0].AllowDBNull; var isNull = rdr.IsDBNull(0); var val = rdr[0]; Console.WriteLine($"SqlClient: AllowDbNull {allowDbNull} IsDbNull: {isNull} {val.GetType().Name} {val}"); } } using (var con = new SqlConnection("Server=localhost;database=tempdb;Integrated Security=true")) { con.Open(); var cmd = con.CreateCommand(); cmd.CommandText = "select @val = cast(null as rowversion) "; var p = cmd.Parameters.Add(new SqlParameter("@val", System.Data.SqlDbType.Timestamp)); p.Direction = System.Data.ParameterDirection.Output; cmd.ExecuteNonQuery(); { SqlBinary val = (SqlBinary) p.SqlValue; Console.WriteLine($"SqlClient (parameter): IsDbNull: {val.IsNull} {val.GetType().Name} {val}"); } } using (var con = new OdbcConnection("Driver={ODBC Driver 17 for SQL Server};Server=localhost;Trusted_Connection=yes")) { con.Open(); var cmd = con.CreateCommand(); cmd.CommandText = "select cast(null as rowversion) rv"; using (var rdr = cmd.ExecuteReader()) { rdr.Read(); var allowDbNull = rdr.GetSchemaTable().Rows[0]["AllowDBNull"]; var isNull = rdr.IsDBNull(0); var val = rdr[0]; Console.WriteLine($"ODBC: AllowDbNull {allowDbNull} IsDbNull: {isNull} {val.GetType().Name} {val}"); } } } } }
輸出
SqlClient: AllowDbNull True IsDbNull: False Byte[] System.Byte[] SqlClient (parameter): IsDbNull: True SqlBinary Null ODBC: AllowDbNull True IsDbNull: True DBNull
我在https://github.com/dotnet/SqlClient/issues/255打開了一個問題, 但它很可能會作為 WontFix 關閉。根據來源中的註釋
// Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change
該問題已被提出,但並未作為重大更改進行修復。它可能會在 .NET Core 中得到修復,無論如何它都充滿了重大變化,並在 .NET Framework 中保持原樣。