SaveChanges throws System.Data.Entity.Core.EntityException - c#

I'm having an issue trying to update the AspNetUsers database in MVC 5 with ASP.NET.
I'm trying to change a value in the user, Credits, which is stored in the database defined as Models.UserDB2.AspNetUsers.
However, when I attempt this, I get thrown this error.
A first chance exception of type 'System.Data.Entity.Core.EntityException' occurred in EntityFramework.SqlServer.dll
FATAL ERROR: An error occurred while starting a transaction on the provider connection. See the inner exception for details.`
Inner exception:
A first chance exception of type 'System.Data.Entity.Core.EntityException' occurred in EntityFramework.SqlServer.dll is the inner exception
This is the code causing the error.
if(player != null) {
foreach(Models.Item item in itemDB.Items) {
if(item.UserAssetOptionId == id) {
if(!item.Owner.ToLower().Equals(username.ToLower())) {
return Content("false");
} else {
item.Owner = "HomeguardDev";
item.InMarket = true;
player.Credits += item.Value / 10;
try {
itemDB.SaveChanges();
userDB2.SaveChanges();
return Content("true");
} catch(Exception e) {
Debug.WriteLine("FATAL ERROR: " + e.Message);
return Content("false");
}
}
}
}
}
The database updates fine if I only update the itemDB database, but I need to update the Credits value as well!
The model is updated with the latest schema with the database, so no problems there.
Anyone know what's up?

The problem is that you're trying to save the changes to itemDB while you're still iterating itemDB.Items, try to change your code to:
if (player != null) {
foreach(Models.Item item in itemDB.Items) {
if (item.UserAssetOptionId == id) {
if (!item.Owner.ToLower().Equals(username.ToLower())) {
return Content("false");
} else {
item.Owner = "HomeguardDev";
item.InMarket = true;
player.Credits += item.Value / 10;
break;
}
}
}
try {
itemDB.SaveChanges();
userDB2.SaveChanges();
return Content("true");
} catch (Exception e) {
Debug.WriteLine("FATAL ERROR: " + e.Message);
return Content("false");
}
}
As long as you're in the foreach, you can't start a second transaction.

Related

Net5 mysql Access Duplicate ErrorCode

I'm trying to show a Duplicate Record error message in an WebAPI service. I'm using .net5 and mysql. I cannot seem to find a way to pull the duplicateEntry message from the exception response. I'd like to key in on the ErrorCode field and use this to tailor a response to the user. I can see the Message property, but cannot figure out how to access the innerException.
{
try
{
module.Id = Guid.NewGuid();
await _moduleRepository.Add(module);
await _uow.CompleteAsync();
return true;
}
catch (DbUpdateException ex)
{
logger.LogWarning("Module - Add Error: " + ex.Message);
return false;
}
}
You may try to catch an Exception object which has a InnerException attribute. Also, you may also check DbEntityValidationException class.
Reference: link
Found something that worked. Made a helper function, but this is highly coupled to Mysql. Had to check the InnerException to see if it was of type MsyqlException.
{
public static string GetExceptionMessage(Exception ex)
{
var message = ex.Message;
var innerException = ex.InnerException;
if (innerException != null && (innerException.GetType() == typeof(MySqlException)))
{
var dbException = (MySqlException)innerException;
if(dbException.ErrorCode == MySqlErrorCode.DuplicateKeyEntry)
{
return ErrorMessages.RecordExists;
} else
{
return ErrorMessages.UnknownError;
}
}
return message;
}
}

How to notify an exception in C#?

I have an exception occurred when the Database connection failed in a Class. The problem is how do I notify my Main Window that this exception is caught and show a message box to notify my user?
Thanks
Use the Try ... Catch clause like this:
try
{
// The code that could generate an exception
}
catch(Exception ex)
{
MessageBox.Show("Error: " ex.Message);
}
Or if you're using SQL-Server connection, use it like this:
try
{
// The code that could generate an exception
}
catch(SqlException ex)
{
MessageBox.Show("SQL Error: " ex.Message);
}
Thanks. I may have not make my question clearly. I mean this exception
is occurred in one class, but the message box should be show in an
other windows class. So how do I communicate and show this error?
From your clarification in one of the comments:
So if you have class TestClass.cs with method Test in it.
public void Test()
{
//if you want to throw an exception defined by your business logic
if(someCondition == false)
throw CustomException();
//if you have exception in the code
int a = 5;
int b =0;
//here you will be thrown an exception can't divide by 0.
int c = a/b;
}
Your winform Button Click or whatever
public void Button_Click1(object sender, EventArgs e)
{
try
{
TestClass cl = new TestClass();
cl.Test();
}
catch(CustomException custEx)
{
//this for your Bussines logic exception
//write your message
}
catch(DivideByZeroException div)
{
//this for divide by zero exception
//write message
}
//you can catch all other exception like this but I don't advice you to do that
catch(Exception ex)
{
//for this to working properly, this catch should be under all of others(last priority)
}
}

System.Data.Entity.Infrastructure.DbUpdateException occurred

So I have made a custom controller by parsing objects in a model to a database in my project and inside that controller, there is a post method called Edit which includes the database fields such as Id, Title, Description, FileName, FileType, FileSize, Author,DateUploaded
In the edit html view, I removed some elements because all I want to edit is "Title" and "Description" and I have also removed the fields in the edit method in the controller I created.
To explain that;
public ActionResult Edit([Bind(Include = FileSharing "Id,Title,Description,FileName,FileType,FileSize,Author,DateUploaded")] FileSharing fileSharing)
into:
public ActionResult Edit([Bind(Include = "Id,Title,Description")] FileSharing fileSharing)
When I try to edit a title or description. It will give a exception error saying
System.Data.Entity.Infrastructure.DbUpdateException' occurred in
EntityFramework.dll but was not handled in user code
Why am I getting this error and how can I get around it?
Edit method
public ActionResult Edit([Bind(Include = "Id,Title,Description")] FileSharing fileSharing)
{
if (ModelState.IsValid)
{
try
{
db.Entry(fileSharing).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception)
{
ViewBag.EditFail = "Error editing file details.";
}
}
return View(fileSharing);
}
Here's how I handle that exception.
private Exception HandleDbUpdateException(DbUpdateException dbu)
{
var builder = new StringBuilder("A DbUpdateException was caught while saving changes. ");
if (!(dbu.InnerException is System.Data.Entity.Core.UpdateException) ||
!(dbu.InnerException.InnerException is System.Data.SqlClient.SqlException))
{
try
{
foreach (var result in dbu.Entries)
{
builder.AppendFormat("Type: {0} was part of the problem. ", result.Entity.GetType().Name);
}
}
catch (Exception e)
{
builder.Append("Error parsing DbUpdateException: " + e);
}
}
else
{
var sqlException = (System.Data.SqlClient.SqlException)dbu.InnerException.InnerException;
for (int i = 0; i < sqlException.Errors.Count; i++)
{
builder.AppendLine(" SQL Message: " + sqlException.Errors[i].Message);
builder.AppendLine(" SQL procedure: " + sqlException.Errors[i].Procedure);
}
}
string message = builder.ToString();
return new Exception(message, dbu);
}
Then at least the exception has more relevant information, which probably makes it easy for you to figure out what's wrong (like some DB validation issue).

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.

Why does AutoCAD throw a fatal error when I try to read line start and end points?

I am writing a plug in to pull as much data out of a CAD as possible. The main issue I am having right now is when I try to access StartPoint.X, for example, the script fails without catching an exception with "FATAL ERROR: Unhandled Access Violation Reading 0xffffffff at d8e176b4h." Depending on what I try to access, the memory location and whatever the second number change. Example:
foreach (Objects o in globalListOfObjs)
{
string type = o.obj.GetType().ToString().Split('.').Last();
if (type == "Line")
{
try
{
Line l = (Line)o.obj;
if (l != null)
{
MessageBox.Show("Not Null!");
MessageBox.Show(l.StartPoint.X.ToString());
}
//listOfLines.Add(new LinkLines(lx1, ly1, lx2, ly2, Guid.NewGuid()));
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
MessageBox.Show(ex.StackTrace);
}
}
}
An object can be safely accessed only if the transaction used to open the object is still active. If you need to store a reference to an object, store the ObjectId and start a new transaction if you need to access the object properties.
Does your code is running in the main thread? AutoCAD does not support multithreading.
Try something like this:
public void getStartPoint(Transaction oTr, ObjectId oId)
{
try {
Line oLn = (Line)oTr.GetObject(oId, OpenMode.ForRead);
if (oLn != null) {
Interaction.MsgBox(oLn.StartPoint.X.ToString);
}
} catch (System.Exception ex) {
Interaction.MsgBox(ex.StackTrace, (MsgBoxStyle)MsgBoxStyle.Exclamation + MsgBoxStyle.OkOnly, ex.Message);
}
}

Categories

Resources