dotnet / SqlClient

Microsoft.Data.SqlClient provides database connectivity to SQL Server for .NET applications.
MIT License
851 stars 286 forks source link

Improve Exception Messages #834

Open israelaece opened 3 years ago

israelaece commented 3 years ago

Note in the follow snippet that there isn't a column index 4 in the query. When we run the code, we got a IndexOutOfRangeException. But when the query and object initializer is more complex, is difficult to locate the problem.

My suggestion here is to include in exception message what's the column index that not exists.

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array. at Microsoft.Data.SqlClient.SqlDataReader.CheckDataIsReady(...)

        using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Test;Integrated Security=True"))
        {
            using (var cmd = new SqlCommand("SELECT Id, Name, Created, Token FROM People", conn))
            {
                conn.Open();

                using (var dr = cmd.ExecuteReader())
                {
                    while (dr.Read())
                    {
                        people.Add(new Person()
                        {
                            Id = dr.GetInt32(0),
                            Name = dr.GetString(1),
                            Created = dr.GetDateTime(2),
                            Token = dr.GetGuid(3),
                            Dummy = dr.GetString(4)
                        });
                    }
                }
            }
        }

The same approach could be used when the problem is casting InvalidCastException:

Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.String'. at Microsoft.Data.SqlClient.SqlBuffer.get_String()

JRahnama commented 3 years ago

@israelaece in .NETCore 3.1 you can apply the column name. for example in your code: Name = dr.GetString("FirstName"), if the column name is FirstName, will get you the column value and if does not exists it will state: Unhandled exception. System.IndexOutOfRangeException: FirstName.

in .NETFramework or older versions of .NETCore this is not applicable. However, you can try: Name = dr.GetString(dr.GetOrdinal("FirtstName")) and if that does not exists you will get an error message stating the column name.

JRahnama commented 3 years ago

For the InvalidCastException the exception does states the problematic casting. Do you need column name or index number to be exactly sated on the Exception?

israelaece commented 3 years ago

Hi @JRahnama. Yes, but I'm looking for performance, and AFAIK, GetOrdinal has an overhead associated to lookup names in the hashtable. GetOrdinal allow to use name to find the index, and I want to work in a "short-circuit" way.

When I need more readability, I can created constants (const int Name = 1) instead of to work with "magic numbers" (Name = dr.GetString(Name)).

In general, I'd like know what's the index that "is out of range" or what's index where "casting is invalid", and I suggested to include this value in exception messages.

israelaece commented 3 years ago

Is in pt-BR, but we can check how hard is if an IndexOfRange or InvalidCast is throwed. If message display the problematic index, I can find the line more quickly.

                    while (dr.Read())
                    {
                        var notaFiscal = new NotaFiscal()
                        {
                            Cliente = new Cliente()
                            {
                                DataDeCadastro = dr.GetDateTime(0),
                                PessoaFisica = new PessoaFisica()
                                {
                                    Cpf = dr.GetString(1),
                                    Nome = dr.GetString(2),
                                    Endereco = new Endereco()
                                    {
                                        Localidade = dr.GetString(3),
                                        Cep = dr.GetString(4)
                                    }
                                }
                            },
                            Transportador = new Transportador()
                            {
                                Tipo = dr.GetString(5),
                                Empresa = new Empresa()
                                {
                                    Cnpj = dr.GetString(6),
                                    Localidade = dr.GetString(7),
                                    Estado = dr.GetString(8),
                                    Nome = dr.GetString(9)
                                }
                            },
                            Itens = new[]
                            {
                                new Item()
                                {
                                    Produto = new Produto()
                                    {
                                        Descricao = dr.GetString(10),
                                        Codigo = dr.GetGuid(11),
                                        Valor = dr.GetDecimal(12)
                                    },
                                    Quantidade = dr.GetInt32(13),
                                    Total = dr.GetDecimal(14)
                                }
                            }
                        };
                    }