ganders
ganders

Reputation: 7451

Find catch statements re-thrown that don't have the inner exception set via RegEx

I'm trying to find all of our code that re-throws exceptions where the new exception being thrown does not contain the original exception as the inner exception. Here's an example:

            catch(DBApplicationException dbEx) 
        {
            BaseApplicationException bEx = new BaseApplicationException(dbEx.Message, dbEx);
            bEx.MethodName = System.Reflection.Assembly.GetExecutingAssembly().FullName + "::" + System.Reflection.MethodBase.GetCurrentMethod().Name.ToString();
            bEx.Severity = ExceptionSeverityLevel;
            PublishingManager.Publish(bEx,"");
            throw bEx;
        }
        catch(Exception e) 
        {
            BaseApplicationException bEx = new BaseApplicationException(e.Message);
            bEx.MethodName = System.Reflection.Assembly.GetExecutingAssembly().FullName + "::" + System.Reflection.MethodBase.GetCurrentMethod().Name.ToString();
            bEx.Severity = ExceptionSeverityLevel;
            PublishingManager.Publish(bEx,"");
            throw bEx;
        }

The first catch (catch (DBApplicationException dbEx) gets re-thrown as BaseApplicationException, but as you can see, it sets the message as dbEx.Message, and then specifies the InnerException as dbEx, but the second catch segment re-throws without the InnerException, it only contains the e.Message.

So for my regex pattern, I only want to find the entire catch block that doesn't contain the inner exception, right now, the pattern I'm using returns both of these two catch blocks together.

Here's my regex pattern:

catch((.|\n|\r)*){((.|\n|\r)*)Exception\(((?!,).)+\);((.|\n|\r)*)}

Here's my method block to test this scenario:

public static DataSet SearchUserSessions(string username, string first, string last, string startDate, string endDate) 
    {
        DataSet ds = null;
        try 
        {
            SqlParameter [] arParms = new SqlParameter[]
            {
                new SqlParameter("@UserName", username),
                new SqlParameter("@FirstName", first),
                new SqlParameter("@LastName", last),
                new SqlParameter("@SessionStart", startDate),
                new SqlParameter("@SessionEnd", endDate)
            };

            DB db = new DB();
            ds = db.ExecuteDataset(SecurityConfig.ConnectionString, CommandType.StoredProcedure, 
                SPSearchUserSessions, (DB.Provider)SecurityConfig.ConnectionProviderType, 
                arParms); 
        }
        catch(DBApplicationException dbEx) 
        {
            BaseApplicationException bEx = new BaseApplicationException(dbEx.Message, dbEx);
            bEx.MethodName = System.Reflection.Assembly.GetExecutingAssembly().FullName + "::" + System.Reflection.MethodBase.GetCurrentMethod().Name.ToString();
            bEx.Severity = ExceptionSeverityLevel;
            PublishingManager.Publish(bEx,"");
            throw bEx;
        }
        catch(Exception e) 
        {
            BaseApplicationException bEx = new BaseApplicationException(e.Message);
            bEx.MethodName = System.Reflection.Assembly.GetExecutingAssembly().FullName + "::" + System.Reflection.MethodBase.GetCurrentMethod().Name.ToString();
            bEx.Severity = ExceptionSeverityLevel;
            PublishingManager.Publish(bEx,"");
            throw bEx;
        }
        return ds;
    }

Upvotes: 0

Views: 230

Answers (3)

Justin Morgan
Justin Morgan

Reputation: 30700

If I understand what you're trying to do, this is going to be extremely difficult. The main reason is that you're trying to return the entire catch block, which means you have to (at least partially) parse the C#. This is because catch blocks are denoted by {...} structures, and they can have other {...} blocks nested inside them:

catch (Exception e)
{
    if (condition)
    {
        doSomething();
    }    
}

For a regex to identify the end of the catch block, it has to balance the { and } symbols to make sure they match. This is possible in .NET regex with balancing groups, but it's going to be more complicated than you bargained for.

I also notice that your regex is extremely permissive. You've used (.|\n|\r) where you don't really want to match every character, and all your quantifiers are greedy. This bit here...

{((.|\n|\r)*)Exception\(((?!,).)+\);((.|\n|\r)*)}

...will actually match everything between the first instance of the word catch until the last } in the file, as long as it finds something like Exception(blah) anywhere. In fact, blah could be anything without a comma in it, even another statement. Exception(); DoStuff(); would theoretically match it!

Your best option, IMO, is to use Visual Studio's Find Usages feature on your BaseApplicationException class. You could find all usages of the entire class, then perhaps compare that to all usages of BaseApplicationException.InnerException. If you must use a regex, this should at least get 99% of catch blocks without nested {...} blocks (expanded for clarity):

^\s*                          #Beginning of a line, possibly followed by whitespace
catch[\s\n]*                  #catch, possibly followed by whitespace
\(\w*Exception\s+\w+\)[\s\n]* #Some type of Exception declaration, possibly followed by whitespace
\{                            #An opening brace
(:?(?!\.InnerException)[^}])* #Anything except a closing brace, without the term `.InnerException` appearing anywhere
\}                            #The closing brace of the block

As I mentioned above, this will choke on nested {...} blocks. You can solve that with balancing groups, but I wanted to keep this simple.

Upvotes: 2

Joanna Derks
Joanna Derks

Reputation: 4063

This works against your example:

catch\((([^)]|\n|\r)*)\)\s*{(([^}]|\n|\r)*?)Exception\(([^,](?!\n))+?\);(([^}]|\n|\r)*)}

But I agree that it's not a maintainable way of tackling this - unless that's a one-off check.

Upvotes: 2

Ian Horwill
Ian Horwill

Reputation: 3025

Sounds fiendishly difficult and hard to maintain. Can you enforce in your project that BaseApplicationException is only thrown as a "wrapper" exception? Then you can remove the constructor that takes just a string, and test the inner exception for null in the one remaining constructor.

Upvotes: 0

Related Questions