-1

I have some code where I want to throw two exceptions; however, the exception are basically the same but with different values. I would like to know an elegant generic way to determine which of these errors occurred.

I know I can do two try catches or that I can set a Boolean to determine the success of query. I am also aware that this can be done in 1 query; however, I need to be able to determine if the company key was wrong or if PA id was wrong. I also know that I can create my own exception and add an additional field to it. Unfortunately, I do not believe that any of these are the optimal solution and this has been bothering me for quite some time.

Any information as to best practice would be appreciated.

using (var ora = new OracleConnection(Data.ConnectionString))
{
    String sqlGetCompanyId = "SELECT COMPANY_ID FROM companies WHERE key = :key";
    String sqlValidateDelete = "select * from pa where PA_ID = :paid AND COMPANY_ID = :cid";

    ora.Open();
    int CompanyId = 0;

    using (var Command = ora.CreateCommand())
    {
        Command.CommandText = sqlGetCompanyId;
        Command.Parameters.Add(":key", OracleDbType.Varchar2).Value = cKey;

        using (var reader = Command.ExecuteReader(CommandBehavior.SingleRow))
        {
            if (reader.Read())
               CompanyId = unchecked((int)((long)reader["COMPANY_ID"]));
            else
               throw new ArgumentException("Invalid Company Key");
        }
     }

     using (var Command = ora.CreateCommand())
     {
         Command.CommandText = sqlValidateDelete;
         Command.Parameters.Add(":cid", OracleDbType.Int32).Value = CompanyId;
         Command.Parameters.Add(":paid", OracleDbType.Int32).Value = PAID;

         using (var reader = Command.ExecuteReader(CommandBehavior.SingleRow))
         {
             if (!reader.Read())
                throw new ArgumentException("Price Agreement Id for this company does not exist");
             rv = unchecked((int)((long)reader["ROW_VERSION"]));
         }
      }
   }
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Aelphaeis
  • 2,593
  • 3
  • 24
  • 42
  • Why are you using a reader to determine if a row exists? Count(*) is more direct. And why use exceptions? Just return a byte return code. – paparazzo Aug 19 '13 at 18:25
  • @Blam The reason I've done this is because I actually require information from the reader in addition to knowing if it exist. i.e CompanyId = unchecked((int)((long)reader["COMPANY_ID"])); – Aelphaeis Aug 19 '13 at 19:04
  • OK then use reader.HasRows or IsDBNull rather than unchecked. And still why exception rather than a return status? – paparazzo Aug 19 '13 at 19:12
  • The reason is because oracle reader starts before the first row and returns "true if there are more rows; otherwise, false." as documented [here](http://msdn.microsoft.com/en-us/library/system.data.oracleclient.oracledatareader.read.aspx) Furthermore, even if I did reader.hasRows, I'd need to do that in addition to reader.read() because I actually use the data immediately after I check for it its existence. Lastly, this is only part of a method. I did not design the way the system is suppose to work nor am I at liberty to change it. This also has nothing to do with the question at hand. – Aelphaeis Aug 20 '13 at 12:24
  • I know how rdr works. Checking for HasRows is faster than making it throw an exception and you don't need unchecked. – paparazzo Aug 20 '13 at 12:48

3 Answers3

1

How about defining your own exception

public class MyException : ArgumentException
{
    public MyException(string s) : base(s)
    {
    }
    public int MyValue { set; get; }
}

and use it like throw new MyException("Some Message") { MyValue = 666 };

EZI
  • 15,209
  • 2
  • 27
  • 33
1

I'd suggest creating a custom exception for each:

public class InvalidCompanyKeyException : ArgumentException {
   public InvalidCompanyKeyException() : base() {}
   public InvalidCompanyKeyException(string message) : base(message) {}
   public InvalidCompanyKeyException(string message, Exception inner) : base(message, inner) {}
}

public class PriceAgreementIdNotFoundException : ArgumentException {
   public PriceAgreementIdNotFoundException() : base() {}
   public PriceAgreementIdNotFoundException(string message) : base(message) {}
   public PriceAgreementIdNotFoundException(string message, Exception inner) : base(message, inner) {}
}

You can catch these separately, or you can catch either one as ArgumentException. I've included three separate constructors for each exception because that's what Microsoft recommends. They also recommend making the exception serializable if they'll be used in certain scenarios, but your example doesn't seem to require serialization.

Ed Gibbs
  • 25,924
  • 4
  • 46
  • 69
  • Its a good thing you included that information as this is actually used in a web service which means its a very good idea for it to be serializable. – Aelphaeis Aug 19 '13 at 19:07
  • Just lucky then :) You need an additional constructor when the exception is serializable. The link in my answer covers this but doesn't show an actual example; I'm sure one could be Googled up. – Ed Gibbs Aug 19 '13 at 19:21
1

You could just specify the ParamName when you create the ArgumentException:

throw new ArgumentException("Invalid Company Key", "cKey");
...
throw new ArgumentException("Price Agreement Id for this company does not exist",
                            "PAID");

And then read it when you catch it:

catch (ArgumentException ex)
{
    if (ex.ParamName == "cKey")
        // something
    else if (ex.ParamName == "PAID")
        // something else
    else
        throw; // something else went wrong, rethrow the error
}
Tim S.
  • 55,448
  • 7
  • 96
  • 122