How to continue insert if one row fails - sql

I have a while loop where it fetches record from csv and inserts to sql table. Now csv may contain many rows.
What I want is if one row fails just log to a file and continue with next record. I was thinking of try and catch but that will exit the program. So, any suggestions?
while (csv.readnextline)
'assign csv columns to objects
try
'insert to db
Catch ex As Exception
'write to log file
End Try
I need the above code to continue after catching an exception.
Thanks

Try and catch do not exit the program, they just control the flow of the code in case something exceptional happens.
When an exception happens in the try block, the execution continues on the first line of the (corresponding) catch block. After the execution of the catch block, the code continues on the first line after the catch, which in your case could be the End While which will continue the loop.
So an construction like this
While dr.Read
Try
InsertRowIntoDataBase()
Catch ex As Exception
LogErrorToFile(ex)
End Try
End While
should work for you.
However, this is a bad design, as it will generate and log an exception, no matter what the problem is, whether the data is invalid, or the sql server is down, or even if there is an error in your code (e.g. some lurking NullReferenceException). You should limit the handling of exception to a specific case, e.g. to a problem with the database, like this:
While dr.Read
Try
InsertRowIntoDataBase()
Catch ex As SqlClient.SqlException
LogDataBaseErrorToFile(ex)
End Try
End While
Also, if there are known possible problems with the data (e.g. a string in the csv where an integer is expected) it's better to just check that than to use an exception mechanism, something along these lines:
While dr.Read
Try
If Not IsRowValid() Then
LogInvalidDataToFile()
Continue While
End If
InsertRowIntoDataBase()
Catch ex As SqlClient.SqlException
LogDataBaseErrorToFile()
Catch ex As Exception
LogGenericErrorToFile()
End Try
End While

no it won't exit the program, depending on how/where you handle the exception. If you do something like :
Dim WrongValuedLinesList As New List(Of String)
Dim ConversionFailedList As New List(Of String)
Dim InsertionFailedList As New List(Of String)
Dim NumberOfInsertedLines As integer = 0
For Each (CurrentLine in my csv)
' 1. line processing
Try
' (process my line : split, convert, check range...)
If (I know the insertion will fail) Then
' (Store information about that wrong line, in List, log, or do nothing)
WrongValuedLinesList.Add(" This line : " & CurrentLine
& " has wrong values because...
Continue For
End If
Catch ex as exception
' (here handle the line conversion failed : store in list, or log, or do nothing ...)
' for expl :
ConversionFailedList.Add(" Conversion failed for line " & CurrentLine
& " exception details : " & ex.message " )
End Try
' 2. Line insertion
Try
'(insert my processed data into database)
NumberOfInsertedLines +=1
Catch ex as exception
' (here handle the insertion failed exception (expl : primary key might not be unique)
' : store in list, log, do nothing...)
' for example :
InsertionFailedList.Add(" Insertion failed for line " & CurrentLine
& " exception details : " & ex.message " )
End Try
Next
(Here you might wanna report how things went to your user using
your error list.)

Related

How to Determine Exception Subtype

I'm wondering if there's a standard way to determine the sub-type of an exception. For example, for the File.Copy() method, IOException indicates that the destination file exists OR a general I/O error occurred. There are other such cases. In my exception handler, how can I determine which it is? I'm checking the end of ex.Message for the string already exists., which works, but seems awfully kludgy and unreliable.
While it is possible to check File.Exists() on the destination file, confirm overwrite with the user if it exists and then perform File.Copy() this is not atomic, which is to say, between checking and copying, it is possible for the conditions to change, for example if some other process created or copied a file into the destination location.
EDIT:
I had already changed the code based on comments here, but I just rolled it back and will post it here, just to show what I was doing:
Try
File.Copy(SrcFile, DstFile, OverWrite)
Catch ex As DirectoryNotFoundException
MsgBox(ex.Message)
Catch ex As FileNotFoundException
MsgBox("File not found: " & ex.FileName)
Catch ex As UnauthorizedAccessException
MsgBox("You do not have write access to the destination.")
Catch ex As IOException
' IOException represents an existing destination file OR a general IO error.
If SubStr(ex.Message, -15) = "already exists." Then
OverwriteCheck = MsgBox(
"Overwrite " & IO.Path.GetFileName(SrcFile) & " in destination directory?",
MsgBoxStyle.YesNo
)
If OverwriteCheck = DialogResult.Yes Then
Try
File.Copy(SrcFile, DstFile, OverWrite)
Catch iex As Exception
MsgBox("Unable to copy " & SrcFile & ":" & vbNewLine & iex.Message)
End Try
End If
Else
Throw ex
End If
Catch ex As ArgumentException
' The user left a blank line in the text box. Just skip it.
End Try
Here is an option using FileStreams to get more granular information about your exception
Sub Main()
Try
copyTo("C:\t\output3.txt", "C:\t\output1.txt", True)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Sub
Private Sub copyTo(source As String, destination As String, Optional overwrite As Boolean = False)
' raises FileNotFoundException if source doesn't exist
Using fsSource As New FileStream(source, FileMode.Open, FileAccess.Read, FileShare.None)
If Not overwrite AndAlso File.Exists(destination) Then
' Raises exception when destination file exists and not overwrite
Throw New Exception(
String.Format("Destination file '{0}' exists and overwrite is false.", destination))
Else
Using fsDestination As New FileStream(destination, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
fsSource.CopyTo(fsDestination)
End Using
End If
End Using
End Sub
This is a rudimentary example, but you can see how you can differentiate different exception cases, while having atomicity between checking file existence and copying.
I believe you looking for this pattern:
Try
IO.File.Copy("source", "Dest", True)
Catch exUnAuth As System.UnauthorizedAccessException
Catch exArg As System.ArgumentException
Catch exNotFound As IO.FileNotFoundException
Catch exGeneral As System.Exception
End Try
Place the list of specific exceptions first in the sequence. The last exception tested for should be the least derived.
You should read through the documentation: How to use structured exception handling in Visual Basic .NET or in Visual Basic 2005. Yes this is an old reference, but that is an indication how long this has been part of the language.

How do I handle NullReferenceException with my code

How do I handle this type of error or exception?
Try
If log.Trim = txtUSN.Text Then
MessageBox.Show("USN found: " & log)
Else
MessageBox.Show("USN not found: " & log)
End If
Catch ex as Exception
MessageBox.Show(ex.Message)
The message was "Object reference not set to an instance of an object."
This is the rest of the code:
Dim log As String
Dim sql As New SqlCommand
sql.Connection = MyConnection
sql.CommandText = "SELECT * FROM dbo.tblAcc WHERE USN = '" & txtUSN.Text & "' "
MyConnection.Open()
log = sql.ExecuteScalar
MyConnection.Close()
The simple answer is your trying to use an object that is nothing. If it's nothing you can't use it, hence "Object reference not set to an instance of an object."
As already mentioned in my comments above, the culprit is: log. I'm not sure where you have declared this or when your using it and how your using it, for all I know it's nothing. If you have more code that would be greatly appreciated as I can point out where it's nothing, until then here's how to get around your issue.
Try
If log IsNot Nothing Then
If log.Trim = txtUSN.Text Then
MessageBox.Show("USN found: " & log)
Else
MessageBox.Show("USN not found: " & log)
End If
Else
MessageBox.Show("Log is NOTHING!")
End If
Catch ex as Exception
MessageBox.Show(ex.Message)
End Try
**EDIT**
After you posted more code in a comment (please post code in the area, not in comment's) it seem's there are a few issue's. You have log defined as a string; when you do this set it to something like: String.Empty instead of nothing. Also you want to sse the ExecuteScalar method to retrieve a single value (for example, an aggregate value) from a database which could be an integer, long, single etc data types. In your query your selecting everything, you can't call ExecuteScalar to return that data... I would recommend looking up information about building queries and executing them, it's to long for me to get in depth with it here.
Happy Coding!
Make sure that (log) is not an empty string.
if not String.IsNullorEmpty(log) then
end if

Is it possible to customize error message "Column does not allow nulls" in a datatable?

I have a form with controls bound to a datatable in VB.net.
When keeping empty a field that should be filled, I'm receiving the error message : Column does not allow nulls.
Is it possible to replace this error message string by another one ?
There are a lot of ways when it comes to error handling.
You could get your code to throw a custom error alert:
This will throw an alert with the text: NullCollumContent
Try
'your code here
Catch ex As Exception
Throw New System.Exception("NullCollomContent")
End Try
Also as K3rnel31 explained:
This will just show a simple message box to the user
Try
'your code here
Catch ex As Exception
msgbox("error message here")
End Try
You could also use If statements to check the string:
This if checks the length of the string and checks if its equal to 0:
If Not yourString.Length = 0 Then
'your code here
else
'some error handling here
End If
This if checks if your string is equal to "" which basically means an empty string:
If Not yourString Is "" Then
'your code here
Else
'some error handling here
End If
Try
'your code here to the datatable
Catch ex As Exception
msgbox("changed error string here")
End Try
Thank you all for your answers but that's not really my problem.
Everything is about DataTable Object.
My problem is that I don't know where the exception is raised.
Let me explain.
Assume Column X has AllowDBNull property to false
And I'm not creating manually a new row that I add to the table
Which means I'm not using such code:
Dim NewRow as DataRow = DataTable.NewRow
NewRom.Item("X") = textbox1.text
DataTable.rows.add(NewRow)
If I was using that code, I would have indeed added the Try/Catch
Dim NewRow as DataRow = DataTable.NewRow
NewRom.Item("X") = textbox1.text
try
DataTable.rows.add(NewRow)
catch Ex as Exception
...
end try
I'm using a DataNavigator which adds in a hidden manner the new row to be added to the table.
So, if Column X is empty, he raises by himself the error. It's not an Exception to be handled. It's an error displayed at run time. Programmatically speaking, nothing went wrong at run time. It seems like DataTable object is designed with this feature and default Error Message.
I hope it's clear now.

Process Start and Errors from Slow Applications

I made an application that our company uses to launch databases and updates them on the users machine, when needed.
I am having a slight problem when it comes to launching databases and the database starts up slow. When this occurs my application throws an exception, as I assuming its awaiting some kind of response back.
As of now the error thrown is: The system cannot find the file specified
I am trying to prevent this exception logging for cases like this(Slow Application), but still allow the logging if a real error occurs while opening a database.
Current Code I am using:
Private Sub OpenApplication()
If File.Exists(LocalPathString) Then ' File Found. Open the File.
Try
Dim ps As New Process
ps = Process.Start(LocalPathString)
Catch ex As Exception
ex.Source += " | " & LocalPathString
RaiseEvent ShowError(ex)
Finally
RaiseEvent CancelIt() ' Thread Complete. Close the ActionForm
End Try
Else
If LocalPathString = vbNullString Then
RaiseEvent CancelIt() ' No file exits. Cancel thread.
Else
RaiseEvent ShowError(New Exception("Database Not Located: " & LocalPathString))
End If
End If
End Sub
StackTrace:
System.Diagnostics.Process.StartWithShellExecuteEx(startInfo As ProcessStartInfo)
App.exe: N 00912
System.Diagnostics.Process.Start()
App.exe: N 00136
System.Diagnostics.Process.Start(startInfo As ProcessStartInfo)
App.exe: N 00049
SAMi.ActionClass.OpenApplication()
App.exe: N 00117
Maybe I'm missing something, but why don't you simply omit the logging if you found that specific exception?
Catch ex As Exception
ex.Source += " | " & LocalPathString
if not ex.Message.Contains("The system cannot find the file specified") Then
RaiseEvent ShowError(ex)
end if

Building A True Error Handler

I am trying to build an error handler for my desktop application. The code Is in the class ZipCM.ErrorManager listed below.
What I am finding is that the outputted file is not giving me the correct info for the StackTrace.
Here is how I am trying to use it:
Try
'... Some stuff here!
Catch ex As Exception
Dim objErr As New ZipCM.ErrorManager
objErr.Except = ex
objErr.Stack = New System.Diagnostics.StackTrace(True)
objErr.Location = "Form: SelectSite (btn_SelectSite_Click)"
objErr.ParseError()
objErr = Nothing
End Try
Here is the class:
Imports System.IO
Namespace ZipCM
Public Class ErrorManager
Public Except As Exception
Public Location As String
Public Stack As System.Diagnostics.StackTrace
Public Sub ParseError()
Dim objFile As New StreamWriter(Common.BasePath & "error_" & FormatDateTime(DateTime.Today, DateFormat.ShortDate).ToString().Replace("\", "").Replace("/", "") & ".log", True)
With objFile
.WriteLine("-------------------------------------------------")
.WriteLine("-------------------------------------------------")
.WriteLine("An Error Occured At: " & DateTime.Now)
.WriteLine("-------------------------------------------------")
.WriteLine("LOCATION:")
.WriteLine(Location)
.WriteLine("-------------------------------------------------")
.WriteLine("FILENAME:")
.WriteLine(Stack.GetFrame(0).GetFileName())
.WriteLine("-------------------------------------------------")
.WriteLine("LINE NUMBER:")
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
.WriteLine("-------------------------------------------------")
.WriteLine("SOURCE:")
.WriteLine(Except.Source)
.WriteLine("-------------------------------------------------")
.WriteLine("MESSAGE:")
.WriteLine(Except.Message)
.WriteLine("-------------------------------------------------")
.WriteLine("DATA:")
.WriteLine(Except.Data.ToString())
End With
objFile.Close()
objFile = Nothing
End Sub
End Class
End Namespace
What is happenning is the .GetFileLineNumber() is getting the line number from objErr.Stack = New System.Diagnostics.StackTrace(True) inside my Try..Catch block. In fact, it's the exact line number that is on.
Any thoughts of what is going on here, and how I can catch the real line number the error is occuring on?
Edit: Changed the code to account for the Exception.StackTrace being a string rather than a real StackTrace
You're creating a new StackTrace, so then it will be for the line you're declaring it on, if you want the line number of the original exception, use the stack trace in Exception.StackTrace.
I think you're being a little confused, I can't see why you create the new StackTrace at all?
Edit: Added more bits to the answer here since easier to see the syntax than in a comment
Currently you have the line
objErr.Stack = New System.Diagnostics.StackTrace(True)
Which means that you're creating a whole new stacktrace, starting when you're creating it.
Instead change that line to:
objErr.Stack = New System.Diagnostics.StackTrace(ex, True)
Which will have the stacktrace from when the error actually happened.
Edit: Added complete sample:
Private Sub a1()
Try
a2()
Catch ex As Exception
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST after exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
st = New StackTrace(ex, True)
Debug.WriteLine(String.Format("ST after exception using exception info, will give line number for where exception was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
End Try
End Sub
Private Sub a2()
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST before exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
Dim b As Integer = 0
Dim a As Integer = 1 / b
End Sub
SOLVED: You should change the wrong instruction, of the original code:
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
with this new one:
.WriteLine(Stack.GetFrame(Stack.FrameCount - 1).GetFileLineNumber)
and you will see, it will return the exact Line_Number of code
where the run time error occurs!!
You do not need to include a Stack property for your ErrorManager, because you have access to the stack trace through the exception.
From experience, I would create a Shared Sub Write(ex As Exception, location As String) method on the ErrorManager, and call in your Catch statement as:
ZipCM.ErrorManager.Write(ex, "Form: SelectSite (btn_SelectSite_Click)")
This change results in cleaner code, reduces the need to write lots of code in each Catch statement, and allows you to change the implementation of Write without having to revisit/rework/refactor each Catch statement.
For instance, you can change the Write method to also call Debug.WriteLine(ex) so you can see which exceptions you are handling during debugging without having to open the file. Moreover, you may want to include WriteNotify method that displays a message box of the exception then calls the Write method to log the exception.