Search code examples
c#.netsql-server.net-8.0sqlgeography

Microsoft.SqlServer.Types (106.1000.6) raising exception when attempting to retrieve values from a userdefined type


Attempting to migrate away from System.Data.SqlClient, we ran into a snag around operations using SqlGeography objects when dealing with user defined types. In short, I am able to setup and retrieve the ordinal value in a simple SqlDataRecord construct, but retrieving the value returns the following exception:

Value cannot be null. (Parameter 'key')

Stack Trace:

at System.ThrowHelper.ThrowArgumentNullException(String name)
at System.Collections.Concurrent.ConcurrentDictionary\`2.TryGetValue(TKey key, TValue& value)
at Microsoft.Data.SqlClient.Server.SerializationHelperSql9.GetSerializer(Type t)
at Microsoft.Data.SqlClient.Server.SerializationHelperSql9.Deserialize(Stream s, Type resultType)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.GetUdt_LengthChecked(SmiEventSink_Default sink, ITypedGettersV3 getters, Int32 ordinal, SmiMetaData metaData)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.GetValue(SmiEventSink_Default sink, ITypedGettersV3 getters, Int32 ordinal, SmiMetaData metaData, Object context)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.GetValue200(SmiEventSink_Default sink, SmiTypedGetterSetter getters, Int32 ordinal, SmiMetaData metaData, Object context)
at Microsoft.Data.SqlClient.Server.SqlDataRecord.GetValueFrameworkSpecific(Int32 ordinal)
at Microsoft.Data.SqlClient.Server.SqlDataRecord.GetValue(Int32 ordinal)
at SqlGeographyTest.Program.Main(String\[\] args) in C:\\source\\repos\\SqlServerTestError\\SqlServerTestError\\Program.cs:line 27

Any insight of where I'm going awry would be fantastic. Thank you.

Code example that reproduces 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 = CreateGeographyPoint(40, 90);
            var md1 = new SqlMetaData("ID", SqlDbType.Int);
            var md2 = new SqlMetaData("geog", SqlDbType.Udt, typeof(SqlGeography), "geography");

            SqlMetaData[] metadata = [md1, md2];

            SqlDataRecord record = new SqlDataRecord(metadata);

            record.SetValues(1, geog);
            var checkValue = record.GetValue(0);
            var checkValue2 = record.GetOrdinal("geog");
            try
            {
                var checkValue3 = record.GetValue(1);
            }
            catch(Exception ex)
            {
                string errorMessage = ex.Message;
            }
        }

        public static SqlGeography CreateGeographyPoint(double longitude, double latitude)
        {
            var text = string.Format("POINT({0} {1})", longitude, latitude);
            var ch = new System.Data.SqlTypes.SqlChars(text);
            return Microsoft.SqlServer.Types.SqlGeography.STPointFromText(ch, 4326);
        }
    }
}

I would have expected the SqlGeography object to be returned instead of an exception thrown.


Solution

  • Not sure why it does not work, possibly it is worth reporting to Microsoft, but you can try workaround with manually deserializing SqlBytes:

    var sqlBytes = record.GetSqlBytes(1);
    var deserialize = SqlGeography.Deserialize(sqlBytes);