I m using EF as ORM and on deleting a record I m getting below exception
"The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Students_dbo.Classes_Class_ClassID". The conflict occurred in database "Demo.DataAccess.DemoContext", table "dbo.Students", column 'Class_ClassID'.
The statement has been terminated."
I can understand it is because this record's PK is being refereed as FK in other table's records. Question is how to properly handle it ? My code is handling ConstrainException and sQLException but this error is not being caught by them rather it is being handled by Exception, Please see below code:
public void DeleteClass(int Id)
{
try
{
unitOfWork.ClassRepository.Delete(Id);
unitOfWork.Save();
}
catch (ConstraintException e)
{
throw e;
}
catch (SqlException e)
{
throw;
}
catch (Exception e)
{
throw e;
}
}
Question:
How to handle it specifically ? Exception handles every error but I need to know when expectly this "Constraint" exception fires. Based on exception type I will need to do different things.
Just try this
catch (DbEntityValidationException e)
cheers
Related
I have a simple entity insert as follows:
using (var db = new MyContext())
{
var item = new Artist();
TryUpdateModel(item);
if (ModelState.IsValid)
{
db.Artist.Add(item);
try
{
db.SaveChanges();
gvArtist.DataBind();
gvArtist.Visible = true;
}
catch (Exception e)
{
Master.Warning = e.InnerException.InnerException.Message;
}
}
}
e.Message and e.InnerException.Message both equate to:
"An error occurred while updating the entries. See the inner exception for details."
But, e.InnerException.InnerException.Message gives the exception I'm looking for, which is:
"Violation of UNIQUE KEY constraint 'UQ_artist_Cuid'. Cannot insert duplicate key in object 'dbo.artist'. The duplicate key value is (11). The statement has been terminated."
I'm worried about missing other exceptions, or causing an exception if I just keep
Master.Warning = e.InnerException.InnerException.Message;
in play.
Your fears are completely founded. Something like this is what you're looking for.
catch (Exception ex)
{
while (ex.InnerException != null)
{
ex = ex.InnerException;
}
Master.Message = ex.Message;
}
The reason for the errors being varying levels deep is that the errors can occur in different sections of code, and they may bubble up through a varying number of methods that wrap them inside other exceptions. You can't plan for them to come from a specific level.
I want to know how we identify the primary key duplication error from SQL Server error code in C#.
As a example, I have a C# form to enter data into a SQL Server database, when an error occurs while data entry, how can I identify the reason for the error from the exception?
If you catch SqlException then see its number, the number 2627 would mean violation of unique constraint (including primary key).
try
{
// insertion code
}
catch (SqlException ex)
{
if (ex.Number == 2627)
{
//Violation of primary key. Handle Exception
}
else throw;
}
MSSQL_ENG002627
This is a general error that can be raised regardless of whether a
database is replicated. In replicated databases, the error is
typically raised because primary keys have not been managed appropriately across the topology.
This is an old thread but I guess it's worth noting that since C#6 you can:
try
{
await command.ExecuteNonQueryAsync(cancellation);
}
catch (SqlException ex) when (ex.Number == 2627)
{
// Handle unique key violation
}
And with C#7 and a wrapping exception (like Entity Framework Core):
try
{
await _context.SaveChangesAsync(cancellation);
}
catch (DbUpdateException ex)
when ((ex.InnerException as SqlException)?.Number == 2627)
{
// Handle unique key violation
}
The biggest advantage of this approach in comparison with the accepted answer is:
In case the error number is not equal to 2627 and hence, it's not a unique key violation, the exception is not caught.
Without the exception filter (when) you'd better remember re-throwing that exception in case you can't handle it. And ideally not to forget to use ExceptionDispatchInfo so that the original stack is not lost.
In case of Entity Framework, the accepted answer won't work and the error will end up not being caught. Here is a test code, only the entity catch statement will be hit or of course the generic exception if entity statement removed:
try
{
db.InsertProcedureCall(id);
}
catch (SqlException e0)
{
// Won't catch
}
catch (EntityCommandExecutionException e1)
{
// Will catch
var se = e1.InnerException as SqlException;
var code = se.Number;
}
catch (Exception e2)
{
// if the Entity catch is removed, this will work too
var se = e2.InnerException as SqlException;
var code = se.Number;
}
Working code for filter only duplicate primary key voilation exception
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
.........
try{
abc...
}
catch (DbUpdateException ex)
{
if (ex.InnerException.InnerException is SqlException sqlEx && sqlEx.Number == 2601)
{
return ex.ToString();
}
else
{
throw;
}
}
Note fine detial :- ex.InnerException.InnerException not ex.InnerException
I have a piece of try catch code:
try
{
...
}
catch(Exception ex)
{
ModelState.AddModelError(
"duplicateInvoiceNumberOrganisation", "The combination of organisation and invoice number must be unique");
}
For this piece of code I'm trying to insert a record into a database: The dba has set it up so that the database checks for duplicates and returns an error if there are duplicates. Currently, as you can see, I'm adding the same error to the model no matter what error occurred. I want it changed so this error is only added to the model if it was caused by the duplicate error set up by the dba.
Below is the error I want to catch. Note it's in the inner exception. Can anyone tell me how to specifically catch this one?
before your current catch add the following:
catch(DbUpdateException ex)
{
if(ex.InnerException is UpdateException)
{
// do what you want with ex.InnerException...
}
}
From C# 6, you can do the following:
catch(DbUpdateException ex) when (ex.InnerException is UpdateException)
{
// do what you want with ex.InnerException...
}
Replace System.Threading.ThreadAbortException with your exception.
try
{
//assume ThreadAbortException occurs here
}
catch (Exception ex)
{
if (ex.GetType().IsAssignableFrom(typeof(System.Threading.ThreadAbortException)))
{
//what you want to do when ThreadAbortException occurs
}
else
{
//do when other exceptions occur
}
}
Not enough rep to comment. In response to #conterio question (in #Davide Piras answer):
is there a catch "when not" syntax?
There is.
catch (Exception e) when (!(e is ArgumentException)) { }
To get name of the exception you can use
catch (Exception exc){
if (exc.GetType().FullName == "Your_Exception")
{
// The same can be user for InnerExceptions
// exc.InnerException.GetType().FullName
}
}
You can take a look at the SQLException class -- and check for the contents of the exception's message if it contains what you now see in your inner exception..Something like this:
try
{
//your code here
}
catch (SQLException ex)
{
if (ex.Message.Contains("Cannot insert duplicate key in obj...."))
{
//your code here
}
}
How can I catch specific exception using c# ?
In my database there is unique index on some columns.
when user inserts duplicate record this exception has been throw :
Cannot insert duplicate key row in
object 'dbo.BillIdentity' with unique
index 'IX_BillIdentity'. The
statement has been terminated.
How can I catch this exception?
Currently I am checking using this code :
catch (Exception ex) {
if (ex.Message.Contains("Cannot insert duplicate key row in object 'dbo._BillIdentity' with unique index 'IX__BillIdentity")) {
string ScriptKey = "$(function() {ShowMessage('Error');});";
ScriptManager.RegisterStartupScript(Page, GetType(), "script", ScriptKey, true);
}
}
I think its bad smell code.
Is there any better way?
Handle SqlException only in this case.
[Edit]
To check duplicate key exception in MS SQL server:
try
{
// try to insert
}
catch (SqlException exception)
{
if (exception.Number == 2601) // Cannot insert duplicate key row in object error
{
// handle duplicate key error
return;
}
else
throw; // throw exception if this exception is unexpected
}
Edit:
Where 2601 come from?
select *
from sys.messages
where text like 'Cannot insert duplicate key%'
Returns:
message_id language_id severity is_event_logged text
----------- ----------- -------- --------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2601 1033 14 0 Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'. The duplicate key value is %ls.
Using exception.Number and referencing sys.messages view you can handle any specific MS SQL exception.
You haven't shown the type of exception which is thrown, but you can catch that specific exception type. For example:
catch (DuplicateKeyException e) {
...
}
It's possible that there won't be a specific exception type for just this error - but if you have to catch something fairly general like SqlException you can then look for more details within the class itself. For example in SqlException there's an Errors property where you can look at more detailed information about each of the (possibly multiple) errors at the database side. Each SqlError then has a Number property which will give the type of error. You can always fall back to the message if you absolutely have to, but you then need to be aware of the possibility of the message changing for different cultures etc.
Note that if you're not really handling the exception, you should probably rethrow it:
catch (SqlException e) {
if (CheckWeCanHandle(e)) {
// Mess with the ScriptManager or whatever
} else {
throw;
}
}
I've just picked up a project where someone went down this route:
Catch ex As SqlException
Select Case ex.Number
Case 2601
...
Note the following (from sys.messages in SQL Server):
2601 - Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'.
But what about this..?
2627 - Violation of %ls constraint '%.*ls'. Cannot insert duplicate key in object '%.*ls'."
I just spent some time tracking down exactly this problem.
And what if we change DB provider? Presumably 2601 is not absolutely universal... This stinks, IMO. And if you are (were) dealing with this in your presentation layer, I think there are bigger questions to ask.
If this must be the mechanism of choice, bury it deep, deep down in the DAL and let a custom Exception percolate up. That way, changes to the data store (or, ideally, this mechanism) have a much more limited area of effect and you can handle the case consistently without any questions in the presentation layer.
I'm currently leaning towards doing a light-weight SELECT for an ID on an open connection and avoiding the exception altogether.
As documented here, you can use exception filters. Example:
try
{ /* your code here */ }
catch (SqlException sqlex) when (sqlex.Number == 2627)
{ /* handle the exception */ }
Working code for filter only duplicate primary key violation exception
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
try {
abc...
} catch (DbUpdateException ex) {
if (ex.InnerException.InnerException is SqlException sqlEx && sqlEx.Number == 2601) {
return ex.ToString();
} else {
throw;
}
}
Note fine detail :- ex.InnerException.InnerException not ex.InnerException
You could only catch the SqlException for starters
catch (SqlException ex) {
if (ex.Message.Contains("Cannot insert duplicate key row in object 'dbo._BillIdentity' with unique index 'IX__BillIdentity")) {
string ScriptKey = "$(function() {ShowMessage('Error');});";
ScriptManager.RegisterStartupScript(Page, GetType(), "script", ScriptKey, true);
}
}
my question is how to handle sql exception in c#, is there anyway to check what kind of the sql exception throws from data access layer? For example, if db throws an unique constraint exception, or foreign key exception, is there any way to catch it from c#? what's the exception handling pattern you are using for these db exception?
Have a look at the documentation of the SqlException class, in particular, at its properties: SqlException.Number, for example, should allow you to identify which type of SqlException (unique constraint, foreign key, ...) occurred.
You can use filtered exceptions to catch specific errors. VB.NET:
Try
...
Catch ex as SqlException When ex.Number = ...
...
Catch ex as SqlException When ex.Number = ...
...
End Try
C# (Version 6 and above):
try
{
...
}
catch (SqlException ex) when (ex.Number == ...)
{
...
}
catch (SqlException ex) when (ex.Number == ...)
{
...
}
you can check message text,Number and do switch case on it to know the error...
try {
}
catch (SqlException ex)
{
string str;
str = "Source:"+ ex.Source;
str += "\n"+ "Number:"+ ex.Number.ToString();
str += "\n"+ "Message:"+ ex.Message;
str += "\n"+ "Class:"+ ex.Class.ToString ();
str += "\n"+ "Procedure:"+ ex.Procedure.ToString();
str += "\n"+ "Line Number:"+ex.LineNumber.ToString();
str += "\n"+ "Server:"+ ex.Server.ToString();
Console.WriteLine (str, "Database Exception");
}
It depends on the exception and your database backend. You database will produce a unique error code for the specific things like constraints, permissions, etc. but that error code varies from DB to DB. Oracle has a MASSIVE pdf (2000+ pages) that lists every possible error it can throw and I am sure Sqlserver has something similar. My point is you looking for specific error codes then you need to trp just those in the catch section and handle them differently then the multitude of other errors you can get.
Catch SqlException
catch(SqlException ex)
{
foreach(SqlError error in ex.Errors)
{
}
}
The general-purpose exception class is DbException, but you'd be limited to parsing the error message text.
For finer-grained handling, you'll need to catch the provider-specific implementation of DbException for the specific DB you are using. These usually provide access to the error code and other details. Here are some common ones:
For SQL Server DB, SqlException
For Oracle DB, OracleException
For MySql DB, MySqlException
For SQLite DB, SQLiteException