PL/SQL Exceptions and Errors Handling - c#

I'm doing a project in Asp .Net and C#. I'm also using ODP .NET to connect to my Oracle DB.
I am using a stored procedure to insert a value into the database. Everything is fine, it also raises all the exceptions I have.
I have 3 exceptions:
when the value I try to insert already exists in the database
when i try to insert a null value
Default "Others"
When the first two exceptions are raised I insert the error into a table Errors, everything goes well.
What I really want to know is, how can I show a message to the user when a Exception occurs?
Something like dbms_output.put_line("Error");...but I want it to show in my webpage. Is this possible?
Any tips are welcome, thanks in advance.

Since your .NET program is your client, you should let any unhandled exceptions propagate from your PL/SQL program and back to the client, where you can deal with it like any other exception.
In other words, you should remove the "when others" exception from your PL/SQL code, and instead wrap your database call (using ODP.NET) with a C# exception block. There you can catch the exception and get the Oracle error number and text, and display it to the user if you want.
(Using this approach you can also use RAISE_APPLICATION_ERROR in your PL/SQL code to signal errors back to the C# client.)

You can fix the stored procedure to return an integer, 0 means ok, 1 means exception of type 1, 2 exception of type 2 and so on... in this way the UI can notify the user that saving method did not go well as expected.
Normally I would not have handled the exception in the stored procedure but I understand that you want to log the error from SQL so the above way should allow you to do what you want.

In our sql server stored procedures we have something like this:
IF ##ERROR <> 0
return (1)
ELSE
return (0)
but it depends on what the stored does and what else it returns, if the stored returns a table, you can then use output parameter to send back the integer discussed above.

Related

How do I get ExecuteReader() to efficiently add items to a List?

I have a button in the UI that reveals all remaining records in a table.
To handle this, in my controller, I have a simple SQL SELECT * statement with a LIMIT and OFFSET. My ExecuteReader is currently returning my data from the SQL command, which I am adding to a List. The list contains instances of my custom Run class.
In SSMS, the SQL query executes without exception no matter how large of a LIMIT I request. If my limit is > the number of rows in the table, it just returns all rows.
In webAPI, though, when my limit is > 200, it returns an exception. Otherwise, when less than 200, it returns a List of Runs without exception. I'm trying to debug the exception that occurs when I try to return all the data, but when it passes to the catch block, the exception is null. Which is weird.
So, I think there is a step I'm missing. Maybe I shouldn't be transforming the data into the Run class while the Reader is streaming. If I verified that the SQL command is accurate, then this seems to be the step that is causing the bug. Maybe transforming the data is making the Reader sorta time out? I don't understand ExecuteReader well enough to be able to figure out how I can pass all the data to List and then transform the data in that list into Runs after closing the connection. And don't even know if that would solve problem anyway.
All misgivings about potential SQL injections and lack of dbContext, etc. aside, how can I return all my records from the database utilizing ExecuteReader()?
Thanks.
Edit to add:
My exception value in the catch block is {"Data is Null. This method or property cannot be called on Null values."}.
In the debugger output, I my exception Exception thrown: 'System.Data.SqlTypes.SqlNullValueException' in Microsoft.Data.SqlClient.dll.
Edit to comment on the solution.
Ann L. figured this out. I had null values coming from the database. I learned from her and PSGuy that I can check for null values by using DbNull. Thank you!
Note - an easy place to get tripped up is that your class has to allow for nulls or else VS won't allow you to check for nulls in the method in the controller.
Here's one approach to the syntax you'll need to use (although there are lots of other approaches: see here for a bunch of alternatives!)
shoeAge = reader.IsDBNull(13) ? null : reader.GetInt64(13)
This assumes shoeAge is a nullable Int64. If it isn't, you'll get another error since you won't be able to assign null to it.

Exception Handling better in Db or in c# code?

I have a stored procedure to insert data in SQL Server DB. I'm using WCF Service which takes data from client and inserts data in the DB. I have a unique key constraint on Name column of my DB table.
If there is any unique key constraint errors:
I handle that in Stored Procedure. e.g using if exists statement in SQL Server and if value is there then I should return -1 otherwise it should insert row in db.
I handle that in my (WCF Service)c# code. I get sql exception and return that sql exception code to the client.
In the first solution I think there is performance issue, because unique key constraint will be checked 2 times. First time I’m checking it manually within stored procedure and second time unique key constraint will check it again. So, 1 value is being checked twice.
In second solution exception is being handle by wcf service using c# code and I’ve heard exceptions in wcf is not so good.
What's the best solution?
"Better" is a little bit subjective here, and it kinds of depends on which "better" you like.
If you mean from a performance perspective, I'd be willing to bet that Option 1 is actually more performant, since the process of checking an index for an existing value in SQL Server (even twice) will probably be dwarfed by the time it takes to raise and propagate an exception back into your code. (Not to mention that you don't have to check it twice at all, you can try/catch in T-SQL itself and return -1 on a Unique key violation)
However if you mean from a design and maintenance point of view, then Option 2 is in my opinion far more desirable, since it is very clear what is going on
In other words, as a developer I would rather read (pseudo-code)
//assuming you have a connection open, a command prepared, etc.
try
{
var result=command.ExecuteNonQuery();
}
catch(SqlException ex)
{
if(ex.Number==2627)
{
//Unique Key constraint violation.
//Error 2627 is documented and well known to be a Unique Key Constraint Violation
//so it's immediately obvious what's going on here
}
}
than
var result=command.ExecuteNonQuery();
if (result==-1)
{
//this means a unique key violation
//what if that comment got removed? I'd have no clue what
//-1 meant...
}
Even though at first glance the second is shorter and more succint.
Note: as MetroSmurf pointed out, catching the exception here is not ideal, it should be handled by the caller, so this is just for illustrative purposes.
This is because the -1 here is pretty arbitrary on the part of the stored procedure; so unless you can guarantee that you can document it and that this document will never go out of date ,etc, then you could be placing the burden on the next developer to have to go look up the Stored Procedure, and figure out what exactly -1 means in this context.
Plus since it's possible for someone to change the SP without touching your C#, what are you going to do if the SP suddenly starts returning "42" for Unique Key Violations? Of course you may be in full control of the SP but will that always be the case?

ExecuteNonQuery always returns zero. Can i use this 0 value into my code for validation?

I am creating a oracle user in dba_users table by using the below c# code where i am using oledbcommand and ExecuteNonQuery. User is being successfully created in the dba_users table but ExecuteNonQuery is always retun value as "0"
So i am doing validation in my code as (IsUserCreated==0). Am i correct with my coding here?
int IsUserCreated= oleCreateUserCommands.ExecuteNonQuery();
if(IsUserCreated==0)
{
//TBD code
Response.write("User Created Successfully");
}
else
{
//TBD Code
Response.write("User creation failed with some error");
}
No, basically. That 0 doesn't mean much - in fact, the main thing it tells me is that you probably have SET NOCOUNT ON somewhere, or this is a sproc without a RETURN - otherwise I would expect 1 to be returned to indicate 1 row impacted. Either way: it does not indicate the lack of an error. The lack of an exception indicates the lack of an error. Returning 1 is useful as a "yes, exactly 1 row was updated" check, if it is enabled.
As Marc said, you can't rely on the return value. The return value is actually not consistent or portable, across different databases and statement types you may see -1 or 0 for success for non-DML, and 0, 1 or greater for DML, in my experience. Per his comment about SET NOCOUNT ON, Oracle doesn't support that, its a SQL Server feature.
Incidentally, for a CREATE USER statement, I always see -1 (I develop several desktop database tools and I've done a lot of tracing) though I don't use OleDb much. I am surprised you see 0, you should double check.
Regardless, you must use exceptions to handle error cases for ExecuteNonQuery and ExecuteScalar and its siblings. It is not possible to write robust code otherwise. The lack of exception implies success. As far as the return code, it is really useless for validation, except in DML. How do you write a generic algorithm that can accept -1, 0 or 1, or N as valid? I simply check it when I know I issue a possible DML, and need to return the row count to the user.
Your code should be in a using block (all IDisposable types in ADO should typically be disposed in a using statement)
You should have a try/catch or at least a try/finally
If you don't like repeating yourself, then wrap ExecuteNonQuery in your own function that will handle exception and return a bool true/false. In certain cases, I like to write extension methods for the connection or reader classes.

Batch inserts using ContinueOnError

I'm using the following code to do a batch insert using the C# driver. I have a unique index, and I want it to fail silently if I try to insert a record that isn't unique.
Even though I have InsertFlags.ContinueOnError set, I still get an error on the InsertBatch call. If I swallow the error as I have shown below, everything works ok. But this certainly feels wrong.
var mio = new MongoInsertOptions {Flags = InsertFlags.ContinueOnError};
// newImages is a list of POCO objects
try
{
_db.GetCollection("Images").InsertBatch(newImages, mio);
}
catch (WriteConcernException)
{
}
Are you using version 1.8 of the csharp Mongo driver?
If so, try upgrading to version 1.8.1 which contains a fix for the following two issues:
InsertBatch fails when large batch has to be split into smaller sub batches
InsertBatch throws duplicate key exception with too much data...
So your inserts could succeed, but the driver is still throwing an exception on bulk insert operations due to the bug above.
And this exception doesn't originate from the database itself, explaining why the inserts succeed but you still need to catch the exception afterwards - i.e. the db is in fact respecting your ContinueOnError flag but the driver throws an exception anyway afterwards.

TSQL BULK INSERT row errors cause C# exceptions

I am processing extremely large delimited files. These files have been pre-processed to ensure that field and row delimiters are valid. Occasionally a row is processed that fails TSQL constraints (usually a datatype issue). 'Fixing' the input data is not an option in this case.
We use MAXERRORS to set an acceptable number of input errors and ERRORFILE to log failed rows.
The bulk insert completes in SSMS with severity level 16 error messages logged to the messages window for each failed row. Attempting to execute this code via the C# SqlCommand class causes an exception to be thrown when the first severity level 16 error message is generated, causing the batch to fail.
Is there a way to complete the operation and ignore SQL error messages via C# and something like SqlCommand?
Example Command:
BULK INSERT #some-table FROM 'filename'
WITH(FIELDTERMINATOR ='\0',ROWTERMINATOR ='\n',FIRSTROW = 2, MAXERRORS = 100, ERRORFILE = 'some-file')
Why not use SqlBulkCopy, and then capture the rows copied using the SqlRowsCopied event. This will more closely mimic the BULK INSERT T-SQL command.
EDIT: It looks like the error handling isn't that robust with SqlBulkCopy. However, here is an example that seems to do what you're looking for:
http://www.codeproject.com/Articles/387465/Retrieving-failed-records-after-an-SqlBulkCopy-exc
Since .NET support all the datatype as SQL you should be able to TryParse in .NET to catch any conversion errors. On date you also need to test for within the SQL data range. On text need to test length. I do exactly this on some very large inserts were I parse down some CSV. TryParse is pretty fast. Better than a Try Catch as it does not have the overhead of throwing and error.
And why not insert in .NET C#. There is a class for bulkcopy. I use TVP asynch insert while parsing and do 10,000 at a time.
It appears that an exception is thrown for each row that has an error but is just counted by SQL. However, it is also passed back to C# (SSIS in my case). I found that wrapping the bulk insert with TRY/CATCH logic and using THROW (to re-throw the exceptions) when more than MAXERRORS occur, works for me.
BEGIN TRY
BULK INSERT #some-table FROM 'filename'
WITH(FIELDTERMINATOR ='\0',ROWTERMINATOR ='\n',FIRSTROW = 2, MAXERRORS = 100,
ERRORFILE = 'some-file')
END TRY
BEGIN CATCH
THROW;
END CATCH

Categories

Resources