4

I am writing unit test to verify detection of exception caused by database overload.

But I cannot find constructor of SqlException. Why I cannot see constructor in metadata?

Following code is just to help understand why I am looking for constructor.

#region Is Timeout
[TestMethod]
public void TestTimeOutWin32ExceptionTimeOut()
{
    Win32Exception ex = new Win32Exception("The wait operation timed out");
    Assert.IsTrue(ExceptionHelper.IsDatabaseTimeOut(ex));
}

[TestMethod]
public void TestTimeOutSqlExceptionTimeOut()
{
    SqlException ex = new SqlException("Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.");
    Assert.IsTrue(ExceptionHelper.IsDatabaseTimeOut(ex));
}

[TestMethod]
public void TestTimeOutEntityCommandExecutionException()
{
    SqlException innerException = new SqlException("Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.");
    EntityCommandExecutionException ex = new EntityCommandExecutionException("anything", innerException);
    Assert.IsTrue(ExceptionHelper.IsDatabaseTimeOut(ex));
}

#endregion

#region Is NOT Timeout
[TestMethod]
public void TestTimeOutWin32ExceptionEmpty()
{
    Win32Exception ex = new Win32Exception("");
    Assert.IsFalse(ExceptionHelper.IsDatabaseTimeOut(ex));
}

[TestMethod]
public void TestTimeOutArgumenException()
{
    ArgumentException ex = new ArgumentException("invalid path");
    Assert.IsFalse(ExceptionHelper.IsDatabaseTimeOut(ex));
}

[TestMethod]
public void TestTimeOutArgumenNullException()
{
    ArgumentNullException ex = new ArgumentNullException("empty path");
    Assert.IsFalse(ExceptionHelper.IsDatabaseTimeOut(ex));
}

[TestMethod]
public void TestTimeOutException()
{
    Exception ex = new Exception("custom string");
    Assert.IsFalse(ExceptionHelper.IsDatabaseTimeOut(ex));
}
#endregion
Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74
Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
  • All of its constructors are private, see [the source](http://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlException.cs,fc412bb451a92848). – Lee Nov 22 '15 at 20:19
  • When did SQL Server start throwing exceptions when it's 'overloaded'? – Randy Minder Nov 22 '15 at 20:20
  • SqlException class has two constructors, (one deserialization constructor) and both are private. SqlException class also has two static methods to create instances, again, both private. That's why you can't see any ctor in the meta data – Oguz Ozgul Nov 22 '15 at 20:26
  • @Randy Minder: Yes, some view are too complicated and if some queries with certain transaction meet, timeout occurs. I am going to redesign database structure and caches little bit. But I want to be able to detect and measure the number of overload and provide 503 HTTP code int that case. – Tomas Kubes Nov 22 '15 at 20:33
  • 1
    @qub1n - TImeouts do not indicated an overoaded database. A timeout can result from a number of things, totally unrelated to SQL Server being 'overloaded'. – Randy Minder Nov 22 '15 at 20:35
  • 1
    Someone else struggled with that too as far as I can see. [Check this link](http://pietschsoft.com/post/2012/03/02/Unit-Testing-with-SqlException-in-NET-Only-with-help-from-Reflection) – Oguz Ozgul Nov 22 '15 at 20:38
  • And where these timeouts can come from? According to stacktrace, these timeout come very often from fulltext queries joined with another tables and according my measurement these queries can really takes few seconds. So I assume that if multiple fulltext search requests occurred at once, timeout can occur. – Tomas Kubes Nov 22 '15 at 20:39

3 Answers3

4

SqlException uses an internal factory method (CreateException) to internally create instances through private constructors. There are no public methods that allow you to create one, probably because it is specific to the SQL Data Provider and not intended for you to create your own exceptions.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
1

I don't know why they have not provided a public constructor but you can create an instance using FormatterServices.GetUninitializedObject

var sqlex = FormatterServices.GetUninitializedObject(typeof(SqlException)) as SqlException;

after that you can use FormatterServices.PopulateObjectMembers to populate the members you want.

Hint: be careful!

Edit Base on hvd comment:

But why do it yourself? Force a SQL Server timeout once, serialise the exception, store the serialised exception in the test data, deserialise it when you need that specific exception.

Community
  • 1
  • 1
Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74
  • 1
    Okay: firstly, this is a horrible idea in general, classes (including `SqlException`) break badly if their constructor doesn't run, pretty much all property accessors and member functions rely on the type's fields initialised by the constructor. Secondly, even if `SqlException`'s properties and methods are by pure chance fine with it in the current implementation of .NET Framework, this won't help the OP fill in the exception details to proper values in order to pretend that a timeout happened. –  Nov 22 '15 at 20:27
  • 2
    With your edit, you're actually getting somewhere that might allows for a reliable answer. When you add `PopulateObjectMembers` to the mix, you're getting quite some way to deserialisation. But why do it yourself? Force a SQL Server timeout once, serialise the exception, store the serialised exception in the test data, deserialise it when you need that specific exception. –  Nov 22 '15 at 20:59
  • hvd, yes that is good, serialize it binary on logging and use it in unit test exactly as it is... – Tomas Kubes Nov 22 '15 at 21:20
0

Yes, constructor is private:

private SqlException CreateSqlException(string message)
{
        var collectionConstructor = typeof(SqlErrorCollection).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, //visibility
            null, //binder
            new Type[0],
            null);          

        var errorCollection = (SqlErrorCollection)collectionConstructor.Invoke(null);

        var constructor = typeof(SqlException).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, //visibility
            null, //binder
            new[] { typeof(string), typeof(SqlErrorCollection), typeof(Exception), typeof(Guid) },
            null); //param modifiers

        return (SqlException)constructor.Invoke(new object[] { message, errorCollection, new DataException(), Guid.NewGuid() });
}

Taken from jgauffin's coding den

Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
  • It isn't constructor. It is a class method – Igor Bendrup Nov 22 '15 at 20:30
  • @IgorBendrup The `SqlException` class has a private constructor, and an internal helper method that calls that constructor. If you're going to use reflection to bypass accessibility anyway, it doesn't really matter whether you call the constructor or the helper method. –  Nov 22 '15 at 20:31
  • This seems like a less bad approach than dotctor's, but remember that the fact that the constructor is private means that Microsoft may change implementation details without any notice. Every update to the .NET Framework may potentially cause this code to no longer work. –  Nov 22 '15 at 20:32
  • Yes, it is only unit test, it does not run on server. And in that case, I can easily find out any change in .NET Framework. – Tomas Kubes Nov 22 '15 at 20:35
  • @hvd Ofcourse, SqlException class has private constructor. But the code above doesn't contain it – Igor Bendrup Nov 22 '15 at 20:38
  • I really don't like hacking into the internals of software in general, like this. Why bother for private fields then, while they actually never are private? – Oguz Ozgul Nov 22 '15 at 20:44
  • @IgorBendrup Do you understand what the code is doing? It's using reflection to access that private constructor. The code indeed doesn't contain the constructor, but it's not supposed to contain it, it's supposed to invoke it. –  Nov 22 '15 at 20:54