Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

C# Question: Any Way to Ignore/Suppress an Error without try/catch???

Status
Not open for further replies.

calamus

Programmer
Jan 30, 2004
14
US
Okay, in my database, users have a many-to-many relationship with the mailing list categories, so that each user can pick any number of categories that they want to receive Newsletters on. When viewing the list of categories that they can select, I do an OUTER JOIN in my SQL query so that I can retrieve ALL categories AND their "subscription" if they have one for that category.

Now, in my data access layer, I have a function that receives the recordset and assigns the values to the "strongly typed" model of the database...e.g.

Code:
oCategory.Name = oRset["catName"].ToString();

Since I am using an OUTER JOIN, sometimes, the column oRset["subscrID"] exists, and other times it is equal to DBNull.Value, and still other times, when grabbing categories without the need to link it to user subscriptions, it's completely NULL and it throws the error System.IndexOutOfRangeException. It is the last error which is causing my only problem. In order to avoid it, I do this, which is (IMHO) bad programming practice because I have an empty catch:

Code:
try {
  if( oRset["subscrID"] != DBNull.Value ) {
    oCategory.RecipientRegistered = true;
  }
}
catch( Exception e ) {
  // do nothing, bad programming
}

Now, here I will state that I am normally a PHP developer, and if I were having a similar "Notice" error in PHP, I would simply prefix the reference oRset["subscrID"] with the @ symbol, because an "IndexOutOfRangeException" is not a problem in this case and I don't want it to end execution of the page...but neither do I want to do bad programming practices and get into the habit of writing empty catches...so anyone have a better solution???

Kevin
 
I'm assuming that what you are getting at is that "oRset["subscrID"]" does not exist on some occasions.

You are right... you don't want to use a try-catch block. especially when looping through data and you know it is going to happen often. When it throws an exception you set off a chain of events that takes time and resources and thus delays the processing... significantly.

So, I can tell you that in VB you are going to have to test for it by using something like oRset.Count > X or Not oRset Is Nothing -- prior to testing your "oRset["subscrID"]".

TIP: C# probably also has a function like VB.NET does with a "AndAlso" which would allow you to put both tests into the same line and if the first test Fails then it never tests the succeeding tests.

ie: If Not X Is Nothing AndAlso X.Property = True Then


Senior Software Developer
 
Before I even messed with try/catch, I tried doing:

Code:
if( oRset["subscrID"] != null && oRset["subscrID"] != DBNull.Value ) {
  // execute code
}

For everything else I've done in C#, .NET, testing a variable against null in an if statement almost always bypassed the error. However, for some reason, that doesn't seem to work with arrays.

However, I just thought about the fact that you can do:

Code:
if( String.IsNullOrEmpty( strVar ) )

Perhaps there is a similar method that you can use with arrays...??? I typed in "Array." into Visual Web Developer, and it brought up:

Code:
Exists<T>( T[] array, Predicate<T> match)

I'm not sure how to work with the <T> stuff...looks like the List<Object>, but not sure how that would tie into an array...so I'll have to look it up, but maybe that will work for my purposes...

In the meantime, if you have any additional info, I'd be glad to hear it.

Thanks,

Kevin
 
The are too many freaking data types in .NET.

Anyway, Array.Exists looks like it's only to find values, not an index...so SOL there...and probably wouldn't have worked anyway since the IDataReader isn't just an array, but some other type of collection of who knows what data type (or at least I don't know). So, back to the drawing board.

Kevin
 
Code:
DbDataReader reader = ...;
for( int i = 0; i < reader.FieldCount; i++ )
    switch (reader.GetName(i))
    {
        case "Whatever":
            string s = (string)reader[i];
            break;
        case "WhateverElse":
            bool b = (bool)reader[i];
            break;
        default:
            //ignore?
            break;
    }

MCP, MCTS - .NET Framework 2.0 Web Applications
 
Of course, you put the for loop inside a:

Code:
while( reader.Read() )

...loop, but you get the idea.

MCP, MCTS - .NET Framework 2.0 Web Applications
 
What type of object is oRset? I know it is some type of record set, but I can't find it in the System.Data namespace. I don't use record sets anymore, because the DataSet and DataTable objects work so much better.

I think the last time I used a Record Set was probably 6 or 7 years ago. Maybe the right answer is to get away from the record set and start using the DataSet???


Senior Software Developer
 
Hey SiriusBlackOp,

Sorry for the confusion...oRset is an IDataReader. The name is a result of habit from old ASP, and that name has simply stuck with me as I always think of them as record-sets regardless of what type of object it is. I should probably just call it oData, but old habits die hard.

BoulderBum,

Thanks...I actually considered doing a switch, but it wasn't worthwhile to me to add the overhead of an extra loop for one variable that may or may not exist. I doubt I need to worry about the speed of the application, but it's still bad practice to write a loop that I don't actually need, so I can't justify doing that.

Last night, after 3 hours or more of searching the web for any possible answer, I came to the conclusion that there was no "good" way to do this except to add an additional boolean parameter LinkToUser. The full code for all four functions that this pertains to is below, one is an overload function for when it needs to find if a recipient is linked to that category already (my Data Access Layer, which these functions are a part of inherits from a Connection class that allows me to do less typing when creating connections, which is the startConnection and getRecordset stuff (as I said, old habits die hard, which is why it's called getRecordset instead of getDataReader:

Code:
public List<ListCategory> getDistrictCategories(Int16 District, int Recipient) {
    SqlCommand oCmd   = this.startConnection();

    oCmd.Parameters.Add("@Recipient", SqlDbType.Int).Value     = Recipient;
    oCmd.Parameters.Add("@District", SqlDbType.SmallInt).Value = District;
    oCmd.Parameters.Add("@CanSend", SqlDbType.Bit).Value       = true;

    IDataReader oRset = this.getRecordset(oCmd, "SELECT C.*,S.subscrID FROM [css_Categories] C " +
        "LEFT OUTER JOIN [css_Subscriptions] S ON S.catID=C.catID AND S.listID=@Recipient AND S.subscrCanSend=@CanSend " +
        "WHERE C.distID=@District");

    return this.getCategoriesFromReader(oRset,true);
}

public List<ListCategory> getDistrictCategories( Int16 District ) {
    SqlCommand oCmd = this.startConnection();

    oCmd.Parameters.Add("@District", SqlDbType.SmallInt).Value = District;

    IDataReader oRset = this.getRecordset(oCmd, "SELECT * FROM [css_Categories] WHERE distID=@District");

    return this.getCategoriesFromReader(oRset,false);
}

public List<ListCategory> getCategoriesFromReader( IDataReader oRset, bool LinkToUser ) {
    List<ListCategory> rows = new List<ListCategory>();
    while(oRset.Read()) {
        rows.Add(getCategoryFromReader(oRset, LinkToUser));
    }
    return rows;
}

public ListCategory getCategoryFromReader( IDataReader oRset, bool LinkToUser ) {
    ListCategory row = new ListCategory();
    row.ID           = (int) oRset["catID"];
    row.Name         = oRset["catName"].ToString();
    row.Description  = oRset["catDescr"].ToString();
    row.IsPublic     = (bool) oRset["catIsPublic"];
    row.District     = (Int16) oRset["distID"];

    if( LinkToUser && oRset["subscrID"] != DBNull.Value ) {
        row.RecipientRegistered = true;
    }
    return( row );
}

Now, that's my solution for the time being. However, I still don't consider this ideal, because--in the long run--I think it would be beneficial for me to know of a way to make the system ignore errors so that I don't have to write any extra code just for C# to ignore an undefined index that I WANT undefined at certain times...However, I've given up hope that there is a way to do this in C#...though, I would gladly be proven wrong if anyone can give me a way to suppress errors.

Most likely, I've just been spoiled by PHP, since you could easily just put @ before the variable to get it to be ignored: @oRset["subscrID"]...maybe Microsoft just hasn't thought that that could ever possibly be valuable and would rather people write TWO functions (one for each query so that there is never an undefined index), or add a parameter as I did. Personally, I try to never write the same code twice, so that changes later are easier...thus, being able to ignore errors in some cases is beneficial to me...but anyway, I'm rabbit trailing.

Thanks for the help, and if anyone knows of a solution to ignore errors, I don't need it for that code any longer, but I'd still like to know if it's possible.

Thanks,

Kevin
 
Apparently, my rant for the day is the use of these datareaders and record sets. LOL!

Have you tried a DataAdapter's Fill method to load a DataSet, and then loop through the rows of the dataSets's table?

You might have better luck using those objects.
Code:
    Private Function GetData(ByVal cmd As SqlClient.SqlCommand) As DataTable
        Try
            Dim conn As New SqlClient.SqlConnection("Your Connection String Here.")
            cmd.Connection = conn
            Dim da As New SqlClient.SqlDataAdapter(cmd)
            Dim dsRet As New DataSet

            da.Fill(dsRet)
            Return dsRet.Tables(0)
        Catch ex As Exception
            'Handle any errors here, or just throw them back.
            Throw ex
        End Try
    End Function

Then this would be my first attempt to solve that issue you are having in VB.NET
Code:
        Dim dt As DataTable = GetData(oCmd)
        For Each dr As DataRow In dt.Rows
            If Not dr Is Nothing AndAlso Not dr("subscrID") Is DBNull.Value Then
                row.RecipientRegistered = True
            End If
        Next

Senior Software Developer
 
Above in C#
Code:
    private DataTable GetData(SqlClient.SqlCommand cmd) {
        try {
            SqlClient.SqlConnection conn = new SqlClient.SqlConnection("Your Connection String Here.");
            cmd.Connection = conn;
            SqlClient.SqlDataAdapter da = new SqlClient.SqlDataAdapter(cmd);
            DataSet dsRet = new DataSet();
            da.Fill(dsRet);
            return dsRet.Tables[0];
        }
        catch (Exception ex) {
            // Handle any errors here, or just throw them back.
            throw ex;
        }
    }

Code:
DataTable dt = GetData(oCmd);
foreach (DataRow dr in dt.Rows) {
    if ((!(dr == null) 
                && !(dr("subscrID") == DBNull.Value))) {
        row.RecipientRegistered = true;
    }
}

Senior Software Developer
 
I probably won't go back to the drawing board on this project when it comes to how I read the data, but if I do any future .NET projects, I'll make sure to try out the DataSet to see if I can get away with undefined indexes.

Mostly, though, my question was not as much about possible solutions, because I knew of ways around it...but I was really hoping that .NET has a similar method to PHP's @ symbol. In PHP, I would be able to do it all like this:

$row->RecipientRegistered = @$oRset["subscrID"] > 0 ? true : false;

To me, that's the most sensible way of doing it (even though many people hate the ?: and think its wrong to use them, I love them, because it makes things succinct and short, and thus, I can scan code more quickly than if there are a lot of if/else statements, but as I said, I'm fairly spoiled by PHP's way of doing things.

Kevin
 
I just had one more thought about what Might help you. The dr object also has a IsNull Method that could help, but could also be the same result as "IS DBNull.Value". See Below to test it against your data...

Code:
        For Each dr As DataRow In dt.Rows
            If Not dr Is Nothing AndAlso Not dr.IsNull("subscrID") AndAlso Not ("subscrID") Is DBNull.Value Then
                row.RecipientRegistered = True
            End If
        Next
Code:
foreach (DataRow dr in dt.Rows) {
    if ((!(dr == null) 
                && (!dr.IsNull("subscrID") 
                && !("subscrID" == DBNull.Value)))) {
        row.RecipientRegistered = true;
    }
}

Senior Software Developer
 
Well... you can to this in VB.NET "On Error Resume Next"

BUT... It does not work in C#.

"On Error Resume Next" is a carry over from VB6 which is why it is not in C#


This works in C#, but then you are right back to where you where:
try {
}
catch (System.Exception continue) {
}

Senior Software Developer
 
Spoiled by PHP" huh? If you mean you think PHP is better, I would argue with ya. PHP is just script like old ASP(vb-script) was. It lets you get away with more slop like the use of "@" as a hack in place of doing things properly. You should never have to write code that intentionally creates and ignores exceptions, thus slowing your code execution. That's my opinion anyway.

Senior Software Developer
 
it wasn't worthwhile to me to add the overhead of an extra loop for one variable that may or may not exist. I doubt I need to worry about the speed of the application, but it's still bad practice to write a loop that I don't actually need, so I can't justify doing that.

This is a bit misguided. The operations you're worrying about are some of the least expensive operations in the .NET Framework. You should try running this code as an experiment:

Code:
DbDataReader reader = cmd.ExecuteReader();

DateTime start = DateTime.Now;
if( reader.Read() )
{
    for( int i = 0; i < reader.FieldCount; i++ )
        switch (reader.GetName(i))
        {
            case "Field1":
                string s = (string)reader[i];
                break;
            case "Field2":
                bool b = (bool)reader[i];
                break;
            case "Field3":
                int f = (int)reader[i];
                break;
            default:
                //ignore?
                break;
        }
}
DateTime finish = DateTime.Now;
TimeSpan totalRunningTime = finish - start;

Put a breakpoint on the line that initializes the TimeSpan (not before or your time spent in the debugger will register as part of the "total running time").

What I suspect you'll find is that the looping and switch statement takes less than a millisecond (not even enough time for the TimeSpan to account for). This might be a problem if your code gets hit a million times a second, but if that's the case, you've got bigger issues since the performance bottleneck is going to be the database and the data flying around your network, not that snippet of code.

If your code only gets hit a few times a second (which I suspect is the case even if it's the most frequently used component of your system), then you shouldn't worry. Your three hours of extra effort probably didn't even save a millionth of a second.



MCP, MCTS - .NET Framework 2.0 Web Applications
 
Sirius,

In truth, with PHP, I can always use empty($var) or isset($var) and get a true/false from that the "correct" way without using @. I almost never use @. The point is that I CAN. Yes, someone who wants to do bad programming can use @ all over the place, use autoglobals and open the door for hacking...and they can forget to do mysql_escape_string() before putting data into the database with string concatenation...but then people can do a lot of bad programming in ANY language.

ASP.NET has done a lot to make the "default methods" that people use more secure...among other things, parameterizing SQL statements to avoid SQL injection is a big one...checking for HTML in posted data is another one...though it's one that should probably always be overridden and validated manually so that you can give the user a readable error or just strip the HTML and not give an error...but at least for the less informed (or in too much of a hurry) developers, it keeps them from opening up doors for people to insert JavaScript.

ASP.NET has also made many things that were previously more difficult with other languages much easier...like uploading a file, which you can do in something like 1-2 lines now...ASP.NET is definitely the most advanced language out there. I never said "PHP is BETTER". I said that I was spoiled by PHP...PHP is object-oriented only as an after-thought...it's not truly an object-oriented language, and I do not like that aspect of PHP.

I will say one thing though, ASP.NET's methods for paging and sorting SUCK! You have to have SQL Server 2005 AND use an ObjectDataSource just to be able to page data efficiently for large data sets...and the sorting algorithm leaves much to be desired...e.g. a way to specify default sorting so that if default is Name ASC and someone clicks to sort by name it doesn't do "Name ASC" again and you have to click it a second time to do DESC...they should also make the ability for you to specify an up and down arrow so that you can see the direction of the sort...and they should allow you to specify multilpe columns to sort by so that when someone sorts by album you can have it sort by track number as the secondary sorting...That is my biggest complaint with ASP.NET. Well, that and the fact that while many hard things are much easier with the .NET library...many easy things have become much harder.

Anyway, IMHO, there are times where it is beneficial to be able to suppress an error. The problem is that with C#, and in this particular scenario with the IDataReader, there is no empty()/IsNull() type method to use other than the DBIsNull, and there is NOTHING aside from BoulderBum's loop/switch or using my additional parameter that I can find to be able to avoid an exception being thrown...unless I switch data types as you suggest, which may or may not work, so I'm not willing to do at this point since I have a solution already and I'm too far along in the project to go back to the drawing board for an error that I can work around...and I like my code to be consistent across the board. However, I will be keeping the DataSet in mind on any future projects.

To me, this is a fault with the way that the IDataReader was built...maybe I should hop on your soap box and preach against the IDataReader...and if on some other project, I find that another reader or the DataSet will allow me to have non-existent values, then I will definitely never use the IDataReader again, since it simply can't handle a column not existing...it can handle it being NULL in the database, but it can't handle it not even getting a result from the database.

Having the IDataReader throw an error is very handy in the event that you do a typo for a column name and it doesn't exist and you have to fix it...but IMHO, in this particular scenario...since there is no IsNull() method that works, a way to supress the error other than try/catch would be beneficial. Heck, if C# can have the all-time world's worst programming method that every book author since C++ first came out tells you to never, never, never use (as the first thing they tell you after their Hello World example)...then I think that the language can handle a way to suppress errors no matter how much of a "bad practice" it is...because simply put, in cases like this with the IDataReader, the authors of that class simply didn't imagine the scenario that I am in and so they didn't provide a useful method to work with it properly...so error suppression is the next best bet.

IMHO, that's not bad practice...it's just making up for what the IDataReader coders failed to account for. Though, you are free to disagree, as I do see your point as well...errors are there for a reason, and so you should work to keep your code error free. Personally, I just happen to think that there are exceptions to those rules that are valid.

Kevin
 
BoulderBum,

Yes, I know that we're talking microseconds, which is why I said "I doubt I need to worry about the speed of the application"...but I still avoid writing loops when they are not needed...especially loops within loops, which is what that is.

If the switch had been the easiest solution, though, I still might have done it...e.g. if I had references to that function all throughout my code and didn't want to try finding and changing them all to add the correct parameter (though I believe Visual Studio would do most of the work for me)...but, anyway, since I was able to do the same thing with a single LinkToUser parameter for 3 functions and no additional loop, I think that's the best thing to do in this case.

Kevin
 
Going back to the beginning, you say:
I do an OUTER JOIN in my SQL query so that I can retrieve ALL categories AND their "subscription" if they have one for that category.
other times, when grabbing categories without the need to link it to user subscriptions, it's completely NULL
To me, that sounds like different sets of data, so why in your class or whichever layer is using it, don't you know which one it will be? Isn't there some logic you can apply to know whether you will be searching for a record that will not be linked to a subscription?

If there really isn't any logic that you can apply in the current state (and perhaps there should be, but that's really up to you), you could always query the metadata of the underlying IDataReader to see if the column exists by looking through the returned DataTable from the GetSchemaTable method.


-------------------------------------------------------

Mark,
[URL unfurl="true"]http://aspnetlibrary.com[/url]
[URL unfurl="true"]http://mdssolutions.co.uk[/url] - Delivering professional ASP.NET solutions
[URL unfurl="true"]http://weblogs.asp.net/marksmith[/url]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top