Mike Nichols - Son of Nun Technology

SqlException Creator for .NET 2.0

I wanted to test what would happen with different SQL messages in my app. Based on their error codes, I'd wrap a friendlier message around them and send em on up.

Problem is, SqlException is sealed AND has no public constructors. Great. How the heck do you test that?

One might try something snappy like this :

 

            try

            {

                SqlConnection conn = new SqlConnection("server=.;database=YOURDB;Integrated Security=true;");

                conn.Open();

 

                SqlCommand cmd = new SqlCommand("raiserror(2601,16,1)",conn);

                cmd.ExecuteNonQuery();

 

            }

            catch (SqlException ex)

            {

                sql = ex;

            }

            Assert.AreEqual(2601, ((SqlException) sql).Number);

This fails.

You'd soon find that throwing native errors from sql doesn't work.

I found a SqlExceptionCreator here but it only works in .NET 1.1 . Basically the .ctor for SqlException has an extra parameter and SerializationInfo is more uptight, requiring all the public properties to be set by .AddValue. I adapted this to work on .NET 2.0 and I can resume my testing.  BTW...dont' change the "AddValue" values below...HResult has to be 1 and ClassName can't be null. Otherwise you get a SerializationException. Can I just say that Lutz Rueder's Reflector is better than sliced bread? I can now resume my testing...

 

using System;

using System.Data.SqlClient;

using System.Reflection;

using System.Runtime.Serialization;

 

public class SqlExceptionCreator

{

    public static SqlException CreateSqlException(string errorMessage, int errorNumber)

    {

        SqlErrorCollection collection = GetErrorCollection();

        SqlError error = GetError(errorNumber, errorMessage);

 

        MethodInfo addMethod = collection.GetType().GetMethod("Add"BindingFlags.NonPublic | BindingFlags.Instance);

        addMethod.Invoke(collection, new object[] { error });

 

        SerializationInfo info = new SerializationInfo(typeof(SqlException), new FormatterConverter());

        info.AddValue("Errors", collection, typeof(SqlErrorCollection));

        info.AddValue("ClassName",typeof(SqlException).ToString());

        info.AddValue("Message", errorMessage);

        info.AddValue("InnerException", null);

        info.AddValue("LineNumber",1);

        info.AddValue("Procedure","procedure");

        info.AddValue("Server","server");

        info.AddValue("Source","src");

        info.AddValue("State",0);

        info.AddValue("HelpURL",null);

        info.AddValue("StackTraceString",null);

        info.AddValue("RemoteStackTraceString", null);

        info.AddValue("RemoteStackIndex", 0);

        info.AddValue("ExceptionMethod", null);

        info.AddValue("HResult", 1);

        info.AssemblyName = "System.Data";

 

 

        Type[] types = new Type[] { typeof(SerializationInfo), typeof(StreamingContext) };

        ConstructorInfo constructor = typeof(SqlException).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);

        object[] objects = new object[] { info, null };

 

        SqlException exception = (SqlException)constructor.Invoke(objects);

 

        return exception;

    }

 

    private static SqlError GetError(int errorCode, string message)

    {

        object[] parameters = new object[] { errorCode, (byte)0, (byte)10,"server", message, "procedure", 0 };

        Type[] types = new Type[] { typeof(int), typeof(byte), typeof(byte), typeof(string), typeof(string), typeof(string), typeof(int) };

 

        ConstructorInfo constructor = typeof(SqlError).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);

        SqlError error = (SqlError)constructor.Invoke(parameters);

        return error;

    }

 

    private static SqlErrorCollection GetErrorCollection()

    {

        ConstructorInfo constructor = typeof(SqlErrorCollection).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);

        SqlErrorCollection collection = (SqlErrorCollection)constructor.Invoke(new object[] { });

        return collection;

    }

}