how to handle cascade delete exception in c# - c#

On cascade delete, I want show message to user in c# when user wants to delete field and occur exception, but don't know number of this error. please help me.
try{
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
if(ex.number == (?))
MessageBox.Show("could not deleted, used in other tables");
}

Masoud,
This link has an interesting explanation on how to do that.
http://blogs.msdn.com/b/tomholl/archive/2007/08/01/mapping-sql-server-errors-to-net-exceptions-the-fun-way.aspx
You will have some something similar to that:
try
{
db.ExecuteNonQuery();
}
catch (SqlException ex)
{
if (ex.Errors.Count == 1) // Assume the interesting stuff is in the first error
{
switch (ex.Errors[0].Number)
{
case 547: // Foreign Key violation
throw new InvalidOperationException("Some helpful description", ex);
break;
case 2601: // Primary key violation
throw new DuplicateRecordException("Some other helpful description", ex);
break;
default:
throw new DataAccessException(ex);
}
}
else throw;
}
I'd recommend you to present an alternative to the user, just show the error message is not the best User Experience that you can provide. Maybe create a column to define the entry as inactive.

Related

How to handle Unique Key Constraint in Insert or Update? [duplicate]

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

How to catch an exception and use error codes when the requested PostgreSQL table can't be found?

I am using Entity Framework so I believe I should catch an NpgsqlException since it's the .NET data provider for PostgreSQL. So let's say I make a query to the context. If the table doesn't exist in the PostgreSQL database, I want to catch the exception thrown and then manually create it. The code below is an example of how an entity is inserted, and I have attempted to use error handling to create the table if need be:
try
{
return _context.Set(entityType).Add(entity);
}
catch (NpgsqlException)
{
CreateEntityTable(entity); //a private method I made
return _context.Set(entityType).Add(entity);
}
The problems are:
I'm not 100% sure I should be catching an NpgsqlException
I want to be sure that if an exception is thrown, it's because the table doesn't exist. I looked up the PostgreSQL documentation of error codes and error code 42P01 is undefined table. I believe I want to use that, but how? I looked up the members of the NpgsqlException class, and I found ErrorCode. However, that's a type int. It would be nice if I could change the code above to be like the following
try
{
return _context.Set(entityType).Add(entity);
}
catch (NpgsqlException ex)
{
if (ex.ErrorCode.Equals(42P01))
{
CreateEntityTable(entity); //a private method I made
return _context.Set(entityType).Add(entity);
}
}
But I'm not sure if that makes sense (I'm not even sure how 42P01 can be an int).
Any help would be appreciated.
You will want to use the Code property of the NpgsqlException as that will contain PostgreSql error code.
Updating your example:
try
{
return _context.Set(entityType).Add(entity);
}
catch (NpgsqlException ex)
{
if (ex.Code == "42P01")
{
CreateEntityTable(entity); //a private method I made
return _context.Set(entityType).Add(entity);
}
}
As an aside, I would suggest that you don't perform schema updates in your normal code. Only do something of this nature in an installer, or on start up as an upgrade.
Tested and working with ASP NET Core 3.1 and ASP NET Core 5.0
try
{
_context.Add(entity);
await _context.SaveChangesAsync().ConfigureAwait(false);
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException ex)
{
if (ex.GetBaseException() is PostgresException pgException)
{
switch (pgException.SqlState)
{
case "23505":
ModelState.AddModelError(string.Empty, "This entity exists in the database");
return View(yourViewModelFromRequest);
default:
throw;
}
}
}
Try
...
catch (NpgsqlException e)
{
switch (e.SqlState)
{
case "23505":
MessageBox.Show("Some message...");
break;
default:
MessageBox.Show("Some message. Details: " + e.Message);
break;
}
}

How can I catch UniqueKey Violation exceptions with EF6 and SQL Server?

One of my tables have a unique key and when I try to insert a duplicate record it throws an exception as expected. But I need to distinguish unique key exceptions from others, so that I can customize the error message for unique key constraint violations.
All the solutions I've found online suggests to cast ex.InnerException to System.Data.SqlClient.SqlException and check the if Number property is equal to 2601 or 2627 as follows:
try
{
_context.SaveChanges();
}
catch (Exception ex)
{
var sqlException = ex.InnerException as System.Data.SqlClient.SqlException;
if (sqlException.Number == 2601 || sqlException.Number == 2627)
{
ErrorMessage = "Cannot insert duplicate values.";
}
else
{
ErrorMessage = "Error while saving data.";
}
}
But the problem is, casting ex.InnerException to System.Data.SqlClient.SqlException causes invalid cast error since ex.InnerException is actually type of System.Data.Entity.Core.UpdateException, not System.Data.SqlClient.SqlException.
What is the problem with the code above? How can I catch Unique Key Constraint violations?
With EF6 and the DbContext API (for SQL Server), I'm currently using this piece of code:
try
{
// Some DB access
}
catch (Exception ex)
{
HandleException(ex);
}
public virtual void HandleException(Exception exception)
{
if (exception is DbUpdateConcurrencyException concurrencyEx)
{
// A custom exception of yours for concurrency issues
throw new ConcurrencyException();
}
else if (exception is DbUpdateException dbUpdateEx)
{
if (dbUpdateEx.InnerException != null
&& dbUpdateEx.InnerException.InnerException != null)
{
if (dbUpdateEx.InnerException.InnerException is SqlException sqlException)
{
switch (sqlException.Number)
{
case 2627: // Unique constraint error
case 547: // Constraint check violation
case 2601: // Duplicated key row error
// Constraint violation exception
// A custom exception of yours for concurrency issues
throw new ConcurrencyException();
default:
// A custom exception of yours for other DB issues
throw new DatabaseAccessException(
dbUpdateEx.Message, dbUpdateEx.InnerException);
}
}
throw new DatabaseAccessException(dbUpdateEx.Message, dbUpdateEx.InnerException);
}
}
// If we're here then no exception has been thrown
// So add another piece of code below for other exceptions not yet handled...
}
As you mentioned UpdateException, I'm assuming you're using the ObjectContext API, but it should be similar.
In my case, I'm using EF 6 and decorated one of the properties in my model with:
[Index(IsUnique = true)]
To catch the violation I do the following, using C# 7, this becomes much easier:
protected async Task<IActionResult> PostItem(Item item)
{
_DbContext.Items.Add(item);
try
{
await _DbContext.SaveChangesAsync();
}
catch (DbUpdateException e)
when (e.InnerException?.InnerException is SqlException sqlEx &&
(sqlEx.Number == 2601 || sqlEx.Number == 2627))
{
return StatusCode(StatusCodes.Status409Conflict);
}
return Ok();
}
Note, that this will only catch unique index constraint violation.
try
{
// do your insert
}
catch(Exception ex)
{
if (ex.GetBaseException().GetType() == typeof(SqlException))
{
Int32 ErrorCode = ((SqlException)ex.InnerException).Number;
switch(ErrorCode)
{
case 2627: // Unique constraint error
break;
case 547: // Constraint check violation
break;
case 2601: // Duplicated key row error
break;
default:
break;
}
}
else
{
// handle normal exception
}
}
// put this block in your loop
try
{
// do your insert
}
catch(SqlException ex)
{
// the exception alone won't tell you why it failed...
if(ex.Number == 2627) // <-- but this will
{
//Violation of primary key. Handle Exception
}
}
EDIT:
You could also just inspect the message component of the exception. Something like this:
if (ex.Message.Contains("UniqueConstraint")) // do stuff
I thought it might be useful to show some code not only handling the duplicate row exception but also extracting some useful information that could be used for programmatic purposes. E.g. composing a custom message.
This Exception subclass uses regex to extract the db table name, index name, and key values.
public class DuplicateKeyRowException : Exception
{
public string TableName { get; }
public string IndexName { get; }
public string KeyValues { get; }
public DuplicateKeyRowException(SqlException e) : base(e.Message, e)
{
if (e.Number != 2601)
throw new ArgumentException("SqlException is not a duplicate key row exception", e);
var regex = #"\ACannot insert duplicate key row in object \'(?<TableName>.+?)\' with unique index \'(?<IndexName>.+?)\'\. The duplicate key value is \((?<KeyValues>.+?)\)";
var match = new System.Text.RegularExpressions.Regex(regex, System.Text.RegularExpressions.RegexOptions.Compiled).Match(e.Message);
Data["TableName"] = TableName = match?.Groups["TableName"].Value;
Data["IndexName"] = IndexName = match?.Groups["IndexName"].Value;
Data["KeyValues"] = KeyValues = match?.Groups["KeyValues"].Value;
}
}
The DuplicateKeyRowException class is easy enough to use... just create some error handling code like in previous answers...
public void SomeDbWork() {
// ... code to create/edit/update/delete entities goes here ...
try { Context.SaveChanges(); }
catch (DbUpdateException e) { throw HandleDbUpdateException(e); }
}
public Exception HandleDbUpdateException(DbUpdateException e)
{
// handle specific inner exceptions...
if (e.InnerException is System.Data.SqlClient.SqlException ie)
return HandleSqlException(ie);
return e; // or, return the generic error
}
public Exception HandleSqlException(System.Data.SqlClient.SqlException e)
{
// handle specific error codes...
if (e.Number == 2601) return new DuplicateKeyRowException(e);
return e; // or, return the generic error
}
If you want to catch unique constraint
try {
// code here
}
catch(Exception ex) {
//check for Exception type as sql Exception
if(ex.GetBaseException().GetType() == typeof(SqlException)) {
//Violation of primary key/Unique constraint can be handled here. Also you may //check if Exception Message contains the constraint Name
}
}
You have to be very specific while writing the code.
try
{
// do your stuff here.
{
catch (Exception ex)
{
if (ex.Message.Contains("UNIQUE KEY"))
{
Master.ShowMessage("Cannot insert duplicate Name.", MasterSite.MessageType.Error);
}
else { Master.ShowMessage(ex.Message, MasterSite.MessageType.Error); }
}
I have just updated the above code a bit and its working for me.

Customizing errors thrown based on default SystemExceptions

I have a line:
string[] cPathDirectories = Directory.GetDirectories(Properties.Settings.Default.customerFolderDirectory);
that will throw the error "Path is not of legal form" if the user didn't specify a search path (this setting is saved as String.Empty at this point). I would like throw this error to say, "Hey you idiot, go into the application settings and specify a valid path" instead. Is there a way to do this instead of:
...catch (SystemException ex)
{
if(ex.Message == "Path is not of legal form.")
{
MessageBox.Show("Hey you idiot, go into the application settings and specify a valid path","Error");
}
else
{
MessageBox.Show(ex.Message,"Error");
}
}
No, you need to check what the type of the exception is and catch that explicitly. Testing for strings in exception messages is a bad idea because they might change from one version of the framework to another. I'm pretty sure Microsoft doesn't guarantee that a message will never change.
In this case, looking at the docs you might be getting either a ArgumentNullException or ArgumentException, so you need to test for that in your try/catch block:
try {
DoSomething();
}
catch (ArgumentNullException) {
// Insult the user
}
catch (ArgumentException) {
// Insult the user more
}
catch (Exception) {
// Something else
}
Which exception you need here, I have no idea. You need to determine that and structure your SEH block accordingly. But always try to catch exceptions, not their properties.
Note the last catch is highly recommended; it ensures that if something else happens you won't get an unhandled exception.
you might check for an argument exception
...catch (SystemException ex)
{
if(ex is ArgumentException)
{
MessageBox.Show("Hey you idiot, go into the application settings and specify a valid path","Error");
}
else
{
MessageBox.Show(ex.Message,"Error");
}
}
That's an ArgumentException:
catch (ArgumentException) {
MessageBox.Show("Please enter a path in settings");
} catch (Exception ex) {
MessageBox.Show("An error occurred.\r\n" + ex.Message);
}
A couple ways to go about it.
First, just check the setting first before you make the GetDirectories() call:
if(string.IsNullOrEmpty(Properties.Settings.Default.customerFolderDirectory))
{
MessageBox.Show("Fix your settings!");
}
else
{
string[] cPathDirectories = Directory.GetDirectories(Properties.Settings.Default.customerFolderDirectory);
}
Or catch a more specific exception:
catch (ArgumentException ex)
{
MessageBox.Show("Hey you idiot, go into the application settings and specify a valid path","Error");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I'd probably go with the former, since then you don't run into a penalty (albeit minor) for exception throwing and can do any other validation you want such as checking whether the path exists, etc.
If you prefer the latter, though, you can find the list of exceptions Directory.GetDirectories() throws here, so you can tailor your messages appropriately.
P.S. I also wouldn't call your users idiots, but that's between you and your god. :)
Yes, you can again throw exception from catch block, example:
catch (SystemException ex)
{
if(ex.Message == "Path is not of legal form.")
{
throw new Exception("Hey you idiot, go into the application settings and specify a valid path", ex);
}
else
{
MessageBox.Show(ex.Message,"Error");
}
}

How can I know if an SQLexception was thrown because of foreign key violation?

I want to tell the user that a record was not deleted because it has child data, but how can I be sure that the exception was thrown because of a foreign key violation? I see that there a sqlexception class that is used for all sql exception.
Assume you're using SQL Server.
Using teh web archive - https://web.archive.org/web/20190120182351/https://blogs.msdn.microsoft.com/tomholl/2007/08/01/mapping-sql-server-errors-to-net-exceptions-the-fun-way/
try
{
# SQL Stuff
}
catch (SqlException ex)
{
if (ex.Errors.Count > 0) // Assume the interesting stuff is in the first error
{
switch (ex.Errors[0].Number)
{
case 547: // Foreign Key violation
throw new InvalidOperationException("Some helpful description", ex);
break;
case 2601: // Primary key violation
throw new DuplicateRecordException("Some other helpful description", ex);
break;
default:
throw new DataAccessException(ex);
}
}
}
Case 547 is your man.
UPDATE The above is sample code and should not be used. Please follow the link as to explain why.
You can write your exception-expected code in the Try block if any exception will be thrown it will be catch further now you can get error number.now can check is it a Foreign Key violation or not
try
{
//your deletetion code
}catch (SqlException ex)
{
if (ex.Errors.Count > 0) // Assume the interesting stuff is in the first error
{
switch (ex.Errors[0].Number)
{
case 547: // Foreign Key violation
lblError.Text = "Cannot Delete this Record this is associated with other record...!";
break;
default:
throw;
}
}
}

Categories

Resources