Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect IsDBNull results on null rowversion when using ReadAsync #1228

Closed
roji opened this issue Aug 25, 2021 · 6 comments
Closed

Incorrect IsDBNull results on null rowversion when using ReadAsync #1228

roji opened this issue Aug 25, 2021 · 6 comments
Labels
🐛 Bug! Something isn't right !

Comments

@roji
Copy link
Member

roji commented Aug 25, 2021

When calling IsDBNull on a rowversion column after having called ReadAsync, false is return for a null value instead of true. This occurs on Microsoft.Data.SqlClient 3.0, and is likely related to #998 (/cc @Wraith2).

The below minimal repro uses a left join to produce a null rowversion. This was originally raised and repro'd on EF Core by @frankbuckley in dotnet/efcore#25074, thanks.

Repro
await using var conn = new SqlConnection("Server=localhost;Database=test;User=SA;Password=Abcd5678;Connect Timeout=60;ConnectRetryCount=0");
await conn.OpenAsync();

await using var cmd = new SqlCommand(@"
DROP TABLE IF EXISTS [Price];
DROP TABLE IF EXISTS [Occurrences];

CREATE TABLE [Occurrences] (
    [Id] int NOT NULL IDENTITY,
    CONSTRAINT [PK_Occurrences] PRIMARY KEY ([Id])
);

CREATE TABLE [Price] (
    [Id] int NOT NULL IDENTITY,
    [Timestamp] rowversion NOT NULL,
    [OccurrenceId] int NOT NULL,
    [Value] decimal(18,2) NOT NULL,
    CONSTRAINT [PK_Price] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Price_Occurrences_OccurrenceId] FOREIGN KEY ([OccurrenceId]) REFERENCES [Occurrences] ([Id]) ON DELETE CASCADE
);

INSERT INTO [Occurrences] DEFAULT VALUES;
INSERT INTO [Occurrences] DEFAULT VALUES;", conn);

await cmd.ExecuteNonQueryAsync();

cmd.CommandText = @"
SELECT [o].[Id], [p].[Id], [p].[OccurrenceId], [p].[Timestamp], [p].[Value]
FROM [Occurrences] AS [o]
LEFT JOIN [Price] AS [p] ON [o].[Id] = [p].[OccurrenceId]
ORDER BY [o].[Id]";

await using var reader = await cmd.ExecuteReaderAsync();

// Read one row with Read - works fine
reader.Read();
Console.WriteLine($"IsDBNull after Read: {reader.IsDBNull(3)}");

// Read another row with ReadAsync - bug
await reader.ReadAsync();
Console.WriteLine($"IsDBNull after ReadAsync: {reader.IsDBNull(3)}");
_ = reader.GetFieldValue<byte[]>(3); // Throws
@cheenamalhotra
Copy link
Member

cheenamalhotra commented Aug 25, 2021

Hi @roji

This issue has been fixed by PR #1182 and will be included in 4.0.0-preview1 release.

One thing to note:
_ = reader.GetFieldValue<byte[]>(3); // Throws will now throw below exception:

SqlNullValueException: Data is Null. This method or property cannot be called on Null values.

as we can't support cast on DBNull.

_ = reader.GetFieldValue<DBNull>(3); will be supported.

@roji
Copy link
Member Author

roji commented Aug 25, 2021

Thanks @cheenamalhotra, good to now!

Yes, it's definitely expected that GetFieldValue throw if IsDBNull returns false true, no problem there.

@roji
Copy link
Member Author

roji commented Aug 25, 2021

Duplicate of #1175

@roji roji marked this as a duplicate of #1175 Aug 25, 2021
@roji roji closed this as completed Aug 25, 2021
@cheenamalhotra
Copy link
Member

Yes, it's definitely expected that GetFieldValue throw if IsDBNull returns false, no problem there.

if IsDBNull returns 'true' :)

@roji
Copy link
Member Author

roji commented Aug 25, 2021

Yes :)

@roji
Copy link
Member Author

roji commented Aug 25, 2021

Just to confirm that the above indeed behaves correctly on 4.0.0-preview1 (IsDBNull return true after ReadAsync)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 Bug! Something isn't right !
Projects
None yet
Development

No branches or pull requests

2 participants