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

ArgumentNullException on SqlDataRecord.GetValue when using Udt data type #2445

Closed
LimeyChris opened this issue Apr 4, 2024 · 1 comment · Fixed by #2448
Closed

ArgumentNullException on SqlDataRecord.GetValue when using Udt data type #2445

LimeyChris opened this issue Apr 4, 2024 · 1 comment · Fixed by #2448
Labels
✔️ Repro Available Issues that are reproducible with repro provided.

Comments

@LimeyChris
Copy link

Describe the bug

ArgumentNullException is thrown when calling GetValue on a SqlDataRecord for a column type defined as SqlMetaData("geog", SqlDbTypeUdt, typeof(SqlGeography))

Exception message: System.ArgumentNullException: Value cannot be null. (Parameter 'key')
Stack trace:
System.Collections.Concurrent.dll!System.ThrowHelper.ThrowArgumentNullException(string name) Line 14
System.Collections.Concurrent.dll!System.Collections.Concurrent.ConcurrentDictionary<System.Type, Microsoft.Data.SqlClient.Server.Serializer>.TryGetValue(System.Type key, out Microsoft.Data.SqlClient.Server.Serializer value) Line 476
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.SerializationHelperSql9.GetSerializer(System.Type t) Line 77
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.SerializationHelperSql9.Deserialize(System.IO.Stream s, System.Type resultType) Line 53
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.ValueUtilsSmi.GetUdt_LengthChecked(Microsoft.Data.SqlClient.Server.SmiEventSink_Default sink, Microsoft.Data.SqlClient.Server.ITypedGettersV3 getters, int ordinal, Microsoft.Data.SqlClient.Server.SmiMetaData metaData) Line 2263
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.ValueUtilsSmi.GetValue(Microsoft.Data.SqlClient.Server.SmiEventSink_Default sink, Microsoft.Data.SqlClient.Server.ITypedGettersV3 getters, int ordinal, Microsoft.Data.SqlClient.Server.SmiMetaData metaData, object context) Line 1033
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.ValueUtilsSmi.GetValue200(Microsoft.Data.SqlClient.Server.SmiEventSink_Default sink, Microsoft.Data.SqlClient.Server.SmiTypedGetterSetter getters, int ordinal, Microsoft.Data.SqlClient.Server.SmiMetaData metaData, object context) Line 926
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.SqlDataRecord.GetValueFrameworkSpecific(int ordinal) Line 23
Microsoft.Data.SqlClient.dll!Microsoft.Data.SqlClient.Server.SqlDataRecord.GetValue(int ordinal) Line 61
SqlGeographyTest.dll!SqlGeographyTest.Program.Main(string[] args) Line 20

To reproduce

The following code can be used to reproduce the issue.

using Microsoft.Data.SqlClient.Server;
using Microsoft.SqlServer.Types;

using System.Data;

namespace SqlGeographyTest
{
	class Program
	{
		static void Main(string[] args)
		{
			SqlGeography geog = SqlGeography.Point(43, -81, 4326);

			SqlMetaData[] metadata = new SqlMetaData[] { new SqlMetaData("geog", SqlDbType.Udt, typeof(SqlGeography), "Geography") };

			SqlDataRecord record = new SqlDataRecord(metadata);

			record.SetValue(0, geog);

			var checkValue = record.GetValue(0);

		}
	}
}

After debugging, the issue appears to arise from the following code in the SqlMetaDataToSmiExtendedMetaData method in MetaDataUtilsSmi (Microsoft.Data.SqlClient.Server).

            return new SmiExtendedMetaData(source.SqlDbType,
                                            source.MaxLength,
                                            source.Precision,
                                            source.Scale,
                                            source.LocaleId,
                                            source.CompareOptions,
#if NETFRAMEWORK
                                            source.Type,
#else
                                            null,
#endif
                                            source.Name,
                                            typeSpecificNamePart1,
                                            typeSpecificNamePart2,
                                            typeSpecificNamePart3);

In .NET 8, the type is set to null instead of source.Type due to the #if NETFRAMEWORK directive.

Then when SqlDataRecord.GetValue is called, the Deserialize method throws an exception because it tries to use a null type.

See GetUdt_LengthChecked method in ValueUtilsSmi (Microsoft.Data.SqlClient.Server). metaData.Type is null.

Stream stream = new SmiGettersStream(sink, getters, ordinal, metaData);
result = SerializationHelperSql9.Deserialize(stream, metaData.Type);

Expected behavior

The call to GetValue should return the value that was set in the call to SetValue.

Further technical details

Microsoft.Data.SqlClient version: 5.2.0
Microsoft.SqlServer.Server: 1.0.0
Microsoft.SqlServer.Types: 160.1000.6
.NET target: .NET 8
Operating system: Windows 11

Additional context

@ErikEJ
Copy link
Contributor

ErikEJ commented Apr 5, 2024

@LimeyChris Thanks for a very good repro, I am working on a fix - caused by half-baked introduction of support for Micrsoft.SqlServer.Types for .NET (Core)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✔️ Repro Available Issues that are reproducible with repro provided.
Projects
Development

Successfully merging a pull request may close this issue.

3 participants