1

So I have a method that looks something like the following:

private static DataSet GetData()
{
    DataSet returnValue = new DataSet();

    try
    { 
        //get all relevant tables here, add them to returnValue
    }
    catch (ArgumentException e)
    {
        //if a table isn't whitelisted, trying to grab it will throw an ArugmentException. 
    }

    return returnValue;
}

Now, I want to pass along the caught exceptions. However, if for example 2 tables are whitelisted, but 1 isn't, I still want those two tables to be returned in the DataSet. I've been thinking I should do something like:

DataSet returnValue = new DataSet();
//TablesToFetch == string list or something containing tablenames you want to fetch
foreach (string tableName in tablesToFetch)
{
    try
    {
        //get table tableName and add it to returnValue
    }
    catch (ArgumentException e)
    {
        //handle exception
    }
}
return returnValue;

However, the problem here is that I can't just throw the exceptions I find, because then the DataSet won't be returned. The first solution I can think of is to "bundle" the exceptions and throw them one by one later, outside of the method*, but it kind of strikes me as a bit of a messy solution. Anyone have any tips on how to handle this, or should I just go ahead with the solution I just proposed?

*I could just wrap the method in another method, which calls a "handle all exceptions"-method after calling GetData()

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
user2875994
  • 195
  • 4
  • 13
  • 1
    What are you hoping to do with the exceptions? Could you also check if the table is whitelisted before trying to get it? – R Day Oct 23 '15 at 10:40
  • 1
    Could you use an out/ref parameter of type string[] to hold the names of the tables not in the whitelist? Might not need the exceptions at all... – Clay Ver Valen Oct 23 '15 at 10:45
  • @RDay That's actually kinda what I'm doing. The try runs a "GetTable" method, and in there I test if the table is whitelisted before I fetch it. But if the table isn't whitelisted, I throw an ArgumentException containing the message "table isn't whitelisted". Check my comment below to Jeroen regarding exactly what I want to do with it. – user2875994 Oct 23 '15 at 10:45
  • What am I supposed to do when I feel like both answers address my question appropriately? I'd feel bad just marking one... – user2875994 Oct 23 '15 at 11:26

2 Answers2

2

If you can't solve 'the problem' within the method, you should throw an Exception. You shouldn't return a DataSet when an exception occurred (you didn't handled). So either you return an Exception or you return a DataSet and handle the exception within the method.


It is possible to Aggregate the exceptions like:

private DataSet GetData(IEnumerable<string> tablesToFetch)
{
    var exceptions = new List<Exception>();

    DataSet returnValue = new DataSet();
    //TablesToFetch == string list or something containing tablenames you want to fetch
    foreach (string tableName in tablesToFetch)
    {
        try
        {
            //get table tableName and add it to returnValue
        }
        catch (ArgumentException e)
        {
            //handle exception
            exceptions.Add(e);
        }
    }

    if (exceptions.Count > 0)
        throw new AggregateException(exceptions);

    return returnValue;
}

Another appoach is return a class with results:

public class GetDataResult
{
    public GetDataResult(DataSet dataSet, string[] missingTables)
    {
        DataSet = dataSet;
        MissingTables = missingTables;
    }

    public string[] MissingTables { get; private set; }
    public DataSet DataSet { get; private set; }
}

private GetDataResult GetData(IEnumerable<string> tablesToFetch)
{
    List<string> missingTables = new List<string>();

    DataSet returnValue = new DataSet();
    //TablesToFetch == string list or something containing tablenames you want to fetch
    foreach (string tableName in tablesToFetch)
    {
        try
        {
            //get table tableName and add it to returnValue

        }
        catch (ArgumentException e)
        {
            //handle exception
            missingTables.Add(tableName);
        }
    }

    return new GetDataResult(returnValue, missingTables.ToArray());
}

usage:

var result = GetData(new[] { "MyTable1", "MyTable2" });

if(result.MissingTables.Count > 0)
{
    Trace.WriteLine("Missing tables: " + string.Join(", ", result.MissingTables));
}

// do something with result.DataSet

update from comments

I don't know much about the structure you're using, so this is pseudo code

// PSEUDO!
private DataTable GetTable(string tableName)
{
    // if table isn't found return null
    if(<table is found>)
        return table;
    else
        return null;
}

private GetDataResult GetData(IEnumerable<string> tablesToFetch)
{
    List<string> missingTables = new List<string>();

    DataSet returnValue = new DataSet();
    //TablesToFetch == string list or something containing tablenames you want to fetch
    foreach (string tableName in tablesToFetch)
    {
        var table = GetTable(tableName);

        if(table == null)
        {
            missingTables.Add(tableName);
            continue;
        }  

        // do something with the table.
    }

    return new GetDataResult(returnValue, missingTables.ToArray());
}
Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57
  • 1
    The problem is it handles many tables. One can throw an exception, but I still want to handle the tables who didn't throw exceptions. The reason I want to pass along the exception is that I just want to log the exception message (table isn't whitelisted) so the table either can be added to the whitelist, or removed from the list of tables that are being fetched later. – user2875994 Oct 23 '15 at 10:41
  • 1
    You should check before it can raise an exception. Exceptions are very expensive. Try to avoid them. Only use exceptions exceptional. – Jeroen van Langen Oct 23 '15 at 10:45
  • I actually haven't used exceptions a lot and I'm trying to get into them because it seems like an industry standard to be using. Normally I'd be doing the kind of things you're mentioning above, like storing the errors in a string and just logging that. What's the point of exceptions if they are expensive, then? – user2875994 Oct 23 '15 at 10:48
  • You can read this: http://stackoverflow.com/questions/6184676/exceptions-when-to-use-timing-overall-use – Jeroen van Langen Oct 23 '15 at 10:53
  • So if I understand correctly... throw exceptions only when there is a complicated stack trace you want to keep for debugging purposes (and you don't expect it to happen often)? – user2875994 Oct 23 '15 at 10:57
  • No: in short, Only use exception when something happend you didn't handle/thought about it. So, if you know you'll get an exception when checking a table that doesn't exists. Try to check if the table is present before you use it. Another example: Converting a `string` to an `int`. You could use a `Convert.ToInt32()` for it, but it raise an exception if the content isn't an `Int`. If you use a `int.TryParse()` it will give a boolean as result. (which in my perspective is a better choice) – Jeroen van Langen Oct 23 '15 at 11:04
  • Right. My code currently DOES check if the table is whitelisted before trying to fetch it, though. It's just that if it's not it throws an exception to "let the program know" so I can log the issue. – user2875994 Oct 23 '15 at 11:07
  • I think the "Another appoach is return a class with results:" would fits your needs. – Jeroen van Langen Oct 23 '15 at 11:10
  • Indeed. That was a very nice solution you edited in. My only question would then be... if exceptions are expensive, wouldn't it be better to simply have GetData run a "verify if the table is in the whitelist" before actually running the GetTable method, and then create the wrapper class if that's the case? Currently I'm testing if the table is on the whitelist inside the GetTable method itself (which is why it throws an exception). It seems to be desirable to avoid throwing exceptions whenever possible - which makes me question why they exist in the first place (outside of fatal exceptions). – user2875994 Oct 23 '15 at 11:19
  • Instead of throwing an `Exception`, you could return `null` (i'll update my solution) – Jeroen van Langen Oct 23 '15 at 14:12
  • I ended up using a wrapper like you suggested. The wrapper holds an array containing the classes I'm constructing with the data from the database, along with another array containing another kind of wrapper holding if (and how) the import failed (an enum), and the name of the table. This gives me all the information I need for my purposes outside of the database API class. Like I said in a comment to the question at the top, though, I'm kinda unsure about which question to mark. Both answered my question :S I did end up scrapping all use of exception throwing due to what you said, though – user2875994 Oct 27 '15 at 11:14
2

This depends very much on the circumstances... I like an approach like this:

public returnType MyMethod([... parameters ...], out string ErrorMessage){
    ErrorMessage=null;
    try{
        doSomething();
        return something;
    }
    catch(Exception exp){
        ErrorMessage=exp.Message;
        return null; //
    }
}

Instead of the out string you could create your own supi-dupi-ErrorInformation class. You just call your routine and check, wheter the ErrorMessage is null. If not, you can react on the out passed values. Maybe you want just to pass the exception out directly...

Shnugo
  • 66,100
  • 9
  • 53
  • 114
  • One more hint: I use this approach, where I deal with a large database update to handle a big transaction in the calling routine. I want to make sure, that the calling routine won't step into later steps if an error occured and rolls back the entire data manipulation... – Shnugo Oct 23 '15 at 10:45
  • That makes sense. It's what I'd normally be doing, but like I mentioned to Jeroen I'm trying to understand and use exceptions more because I have very limited knowledge on the subject. – user2875994 Oct 23 '15 at 10:50
  • An exception is "falling" down the call stack until it is handled somewhere. If you want to react on an exception somewhere else, you could define your own error types (derived from Exception). On Creation you can pass the existing Exception as the "InnerException" and throw yours. Outside you'll be able to have your very granular error handling. The approach I used in my answer is much more "old school" but I think it is better for maintenance... Personal opinion... – Shnugo Oct 23 '15 at 10:59