Extract user-friendly error message from entity framework - c#

I have the following code inside my asp.net mvc web application :-
try
{
var count = repository.changeDeviceSwitch(s.Switch.SwitchID, (Int32)s.GeneralSwitchTo, User.Identity.Name.Substring(User.Identity.Name.IndexOf("\\") + 1));
repository.Save();
return Json(new { IsSuccess = "redirect", description = Url.Action("Details", new { id = s.GeneralSwitchTo }) }, JsonRequestBehavior.AllowGet);
}
catch (DbUpdateException exception)
{
return Json(new { IsSuccess = "custome", description = "Error occurred." + exception.InnerException.InnerException.Message.ToString() }, JsonRequestBehavior.AllowGet);
}
catch (Exception e)
{
return Json(new { IsSuccess = "custome", description = "Error occurred." }, JsonRequestBehavior.AllowGet);
}
which will call the following repository method :-
public int changeDeviceSwitch(int fromID , int toID, string username)
{
var currentdevices = tms.TMSSwitchPorts.Where(a => a.SwitchID == fromID);
int count = 0;
foreach (var d in currentdevices)
{
DeletePort(d, username);
//d.SwitchID = toID;
count++;
}
foreach (var d in currentdevices)
{
TMSSwitchPort tsp = new TMSSwitchPort() { SwitchID = toID, TechnologyID = d.TechnologyID, PortNumber = d.PortNumber };
InsertOrUpdatePort(tsp, username);
}
return count;
}
Currently if the DbUpdateException happen the user will get the following information :-
Violation of PRIMARY KEY constraint 'PK_SwitchPortServer'. Cannot
insert duplicate key in object 'dbo.TMSSwitchPorts'. The duplicate key
value is (1484, e). The statement has been terminated.
this exception will happen when the user try to add a port number that already exists under the same record. (SwitchID + Portno) combination are unique inside database. which is purely technical message , so i am not sure if there is a way to extract a more user friendly error message from entity framework? or i need to do the check by myself ?

How about throwing your own exception?
try
{
InsertOrUpdatePort(tsp, username);
}
catch (DbUpdateException e)
{
throw new Exception("Friendly message here", e)
}
Better yet is to define your own exception class to use here, instead of just Exception.

Related

System.Web.Http.HttpResponseException in catch block while exception handling

I am intentionally making a violation of a unique constraint in my database, and trying to handle an exception.
This among else is in my form:
HttpResponseMessage response = KorisniciService.PostResponse(k);
if (response.IsSuccessStatusCode)
{
MessageBox.Show(Messages.add_usr_succ);
DialogResult = DialogResult.OK;
Close();
}
else
{
string message = response.ReasonPhrase;
if (string.IsNullOrEmpty(Messages.ResourceManager.GetString(response.ReasonPhrase)))
message = Messages.ResourceManager.GetString(response.ReasonPhrase);
MessageBox.Show("Error code: " + response.StatusCode + " Message: " + message);
}
My controller:
public IHttpActionResult PostKorisnici(Korisnici obj)
{
if (!ModelState.IsValid)
return BadRequest();
try
{
obj.KorisnikId = Convert.ToInt32(dm.esp_Korisnici_Insert(obj.Ime, obj.Prezime, obj.Email, obj.Telefon, obj.KorisnickoIme, obj.LozinkaSalt, obj.LozinkaHash, obj.Status, obj.Adresa, obj.GradId).FirstOrDefault());
}
catch (EntityException ex)
{
throw CreateHttpResponseException(Util.ExceptionHandler.HandleException(ex), HttpStatusCode.Conflict);
}
foreach (var item in obj.Uloge)
{
dm.esp_KorisniciUloge_Insert(obj.KorisnikId, item.UlogaId);
}
return CreatedAtRoute("DefaultApi", new { id = obj.KorisnikId }, obj);
}
HttpResponseException making function:
private HttpResponseException CreateHttpResponseException(string reason, HttpStatusCode code)
{
HttpResponseMessage msg = new HttpResponseMessage()
{
StatusCode = code,
ReasonPhrase = reason,
Content = new StringContent(reason)
};
return new HttpResponseException(Request.CreateResponse(msg));
}
Exception handler class:
public class ExceptionHandler
{
public static string HandleException(EntityException error)
{
SqlException ex = error.InnerException as SqlException;
switch (ex.Number)
{
case 2627:
{
return GetConstraintExceptionMessage(ex);
}
default:
return error.Message + "(" + error +")";
}
}
/*Message "Violation of UNIQUE KEY constraint 'CS_KorisnickoIme'. Cannot insert duplicate key in object 'dbo.Korisnici'. The duplicate key value is (farish).\r\nThe statement has been terminated." string*/
private static string GetConstraintExceptionMessage(SqlException error)
{
string newMessage = error.Message;
int startIndex = newMessage.IndexOf("'");
int endIndex = newMessage.IndexOf("'", startIndex + 1);
if (startIndex>0 && endIndex>0)
{
string constraintName = newMessage.Substring(startIndex + 1, endIndex - startIndex - 1);
if (constraintName == "CS_KorisnickoIme")
newMessage = "username_con";
else if (constraintName == "CS_Email")
newMessage = "email_con";
}
return newMessage;
}
So when I produce an error, instead of a popup window (which shows up fine in a tutorial video) I get a System.Web.Http.HttpResponseException in a first catch block of my post method and nothing passed back to my form.
I think because the exception is being thrown and not inside a try/catch block, or the catch block receiving the CreateHttpResponseException, is absorbing it and not providing a response object.
EDIT
Can you post the code for KorisniciService.PostResponse?
and nothing passed back to my form
And what is the eventual result? From the form code you have posted it should either popup message box with success message, or popup message box with fail message. What actually happens?
2nd EDIT
Following further information, use this in your form code...
try
{
HttpResponseMessage response = KorisniciService.PostResponse(k);
if (response.IsSuccessStatusCode)
{
MessageBox.Show(Messages.add_usr_succ);
DialogResult = DialogResult.OK;
Close();
}
}
catch(HttpResponseException ex)
{
string message = ex.ReasonPhrase;
if (string.IsNullOrEmpty(Messages.ResourceManager.GetString(ex.ReasonPhrase)))
message = Messages.ResourceManager.GetString(ex.ReasonPhrase);
MessageBox.Show("Error code: " + ex.StatusCode + " Message: " + message);
}

Kendo Grid duplication message

I am using Kendo grid and I have stopped the grid from saving duplicate values as follows in create method:
var results = new List<ProviderTypeMasterViewModel>();
try
{
_logger.LogInformation("ProviderTypeMastersController ProviderType_Create Start");
foreach (var ProviderTypeMaster in ProviderTypeMasterList)
{
TblProviderTypeMaster ptm = new ProviderTypeMasterViewModel().ToModel(ProviderTypeMaster);
var provd = _context.TblProviderTypeMasters.Where(p => p.ProviderTypeName == ProviderTypeMaster.ProviderTypeName).ToList();
if (provd != null && provd.Count() == 0)
{
if (ProviderTypeMasterList != null && ModelState.IsValid)
{
string userID = GetUserID();
providerTypeMasterService.SaveProviderTypeMaster(ProviderTypeMaster, userID);
}
}
else
{
duplicate = true;
//Session["ErrMsg"] = "Already Exists";
//return RedirectToAction("ProviderType_Read", "ProviderTypeMasters");
}
}
_logger.LogInformation("ProviderTypeMastersController ProviderType_Create Complete");
}
catch (Exception e)
{
_logger.LogError("ProviderTypeMastersController ProviderType_Create Failed - " + e.Message);
}
return Json(results.ToDataSourceResult(request, ModelState));
And in the read method I have displayed the error message to the user as follows
try
{
if (duplicate == true)
{
TempData["ErroMsg"] = "Already Exists";
}
_logger.LogInformation("In ProviderTypeMastersController ProviderType_Read");
return Json(providerTypeMasterService.ListProviderTypeMaster().ToDataSourceResult(request));
}
catch (Exception e)
{
_logger.LogError("ProviderTypeMastersController ProviderType_Read Failed - " + e.Message);
}
return View();
The duplication process has stopped. But I am unable to show the error message to the user. Can anyone let me know what I should do where I have gone wrong. I have tried using ViewBag,ViewData,TempData.
This is my View
<div>
if (TempData["ErroMsg"] != null)
{
<p>#TempData["ErroMsg"].ToString()</p>
}
you can use DataBinding() and DataBound() function of kendo grid...these functions call in client side after Read method on server side..for example you can set a field and decision with this field

How to throw Exception from WebAPI

I have business logic which has throw exception, which I need to transfer to my api controller and show when my webapi fails to read. I have tray catch in all place. In Business Logic`
public static Models.User Login(Models.Login model)
{
try
{
using (var db = new Data.TPX5Entities())
{
var query = (from a in db.User
where a.UserID == model.UserName || a.UserCode == model.UserName || a.UserName == model.UserName
select new Models.User
{
EMail = a.EMail,
IsUsed = a.IsUsed,
Memo = a.Memo,
MobilePhone = a.MobilePhone,
Password = a.Password,
Telephone = a.Telephone,
UserCode = a.UserCode,
UserID = a.UserID,
UserName = a.UserName
}).ToList();
if (query == null || query.Count == 0)
{
throw new Exception(#LanguageHelper.GetSystemKeyValue(CultureHelper.GetCurrentCulture(), "/resource/Model/BLL_User_MSG_UserNotFound"));
}
else if (query.Count > 1)
{
throw new Exception(#LanguageHelper.GetSystemKeyValue(CultureHelper.GetCurrentCulture(), "/resource/Model/BLL_User_MSG_UserCodeRepeat"));
}
else
{
if (query[0].Password == model.Password)
{
return query[0];
}
else
{
throw new Exception(#LanguageHelper.GetSystemKeyValue(CultureHelper.GetCurrentCulture(), "/resource/Model/BLL_User_MSG_InCorrectPassword"));
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
then web api controller I use try catch again
[HttpPost]
public Models.User Login(Models.Login model)
{
Models.User mUser = null;
try
{
mUser = BusinessLogic.User.Login(model);
if (mUser == null)
throw new Exception("Object is null.");
}
catch(Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(ex.Message, Encoding.UTF8), ReasonPhrase = "Login Exception" });
}
return mUser;
}
and then I call in my client I use try catch to check again
private void btnLogin_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty( txtUser.Text))
{
TPX.Core.MessageBoxHelper.ShowError(Core.LanguageHelper.GetSystemKeyValue(GlobalParameters.Language, "/resource/Message/MS_FormLogin_Error_UserEmpty"));
return;
}
try
{
//登录系统
string md5Password = TPX.Core.Security.MD5.GetMD5(txtPassword.Text);
TPX.Models.Login mLogin = new TPX.Models.Login();
mLogin.UserName = txtUser.Text.Trim();
mLogin.Password = md5Password;
//Retrieve User Information
string itemJson = Newtonsoft.Json.JsonConvert.SerializeObject(mLogin);
string userURL = GlobalParameters.Host + "api/User/Login";
using (System.Net.WebClient webClient = new System.Net.WebClient())
{
webClient.Headers["Content-Type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
string sJson = webClient.UploadString(userURL, "POST", itemJson);
TPX.Models.User myDeserializedObj = (TPX.Models.User)Newtonsoft.Json.JsonConvert.DeserializeObject(sJson, typeof(TPX.Models.User));
ClientContext.Instance.UserID = myDeserializedObj.UserID;
ClientContext.Instance.UserCode = myDeserializedObj.UserCode;
ClientContext.Instance.UserName = myDeserializedObj.UserName;
ClientContext.Instance.Password = myDeserializedObj.Password;
}
DialogResult = System.Windows.Forms.DialogResult.OK;
}
catch (WebException ex)
{
TPX.Core.MessageBoxHelper.ShowException((Core.LanguageHelper.GetSystemKeyValue(GlobalParameters.Language, "/resource/Message/MS_FormLogin_Ex_LoginError")),ex);
}
}
When I login with wrong credential need to throw error. Now I am getting error "The remote Server return error:(500)Internal Server Error', Instead I want to throw exact error which my business logic throw. Thanks
`
Do not throw 500 internal server error but try and communicating using specific http codes. In your case you want to communicate login failure so tell your client that specifically.
Either use:
throw new HttpResponseException(HttpStatusCode.Unauthorized);
Or, a custom message like this:
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) {
ReasonPhrase = "whatever you want it!" };
hrow new HttpResponseException(msg);
your business layer and your api are two different things.
you don't throw errors from your api unless something really bad happened with your own code.
The api always returns meaningful http codes and that's how the clients know what's going on.
Example:
[HttpPost]
public IHttpActionResult Login(Models.Login model)
{
var mUser = BusinessLogic.User.Login(model);
if (mUser == null)
return NotFound();
return Ok(mUser);
}
you are now returning something meaningful which makes sense to a client and you are actually helping them understand what's going on.
There are multiple ways to return data, this is only one of them.
Avoid throwing errors, it is expensive in terms of resources used and the more users you have the worse it will get.
You can have your business layer return a message in form of a string and then return that as a result of the API call and the other response shows you how.

Message = "New transaction is not allowed because there are other threads running in the session." [duplicate]

This question already has answers here:
SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session
(23 answers)
Closed 6 years ago.
I'm trying to copy data row by row from ProductStatisticsTemptable to ProductStatistics table.
[HttpGet]
public ActionResult Copy_DatatoProductStatistics()
{
string errormsg = "";
//var incomplete_product_result = db.Database.SqlQuery<ProductStatistics>("Copy_ProductStatistics");
var str = from a in db.ProductStatisticsTemp select a;
ProductStatistics ls = new ProductStatistics();
try
{
foreach (var val in str) // iterator the data from the list and insert them into the listSecond
{
var product = db.Product.Find(val.Product_ID);
try
{
if (product.ProductID == val.Product_ID & product.ProductTitleEn == val.ProductNameEn & product.ProductTitleAr == val.ProductNameAr)
{
try
{
if (!String.IsNullOrEmpty(val.SalesVolumes) | !String.IsNullOrEmpty(val.Target) | !String.IsNullOrEmpty(val.Profitability) | !String.IsNullOrEmpty(val.AnnualGrowthRate))
{
ls.Product_ID = val.Product_ID;
ls.ProductNameEn = val.ProductNameEn;
ls.ProductNameAr = val.ProductNameAr;
ls.Profitability = val.Profitability;
ls.Target = val.Target;
ls.SalesVolumes = val.SalesVolumes;
ls.Subsidary_ID = val.Subsidary_ID;
ls.AnnualGrowthRate = val.AnnualGrowthRate;
db.ProductStatistics.Add(ls);
}
db.SaveChanges();
}
catch (Exception ex)
{
errormsg = ex.Message;
}
}
}
catch (Exception ex)
{
errormsg = "Product ID or Product Name Doesnt match with existing details";
}
}
}
catch (Exception ex)
{
errormsg = ex.Message;
}
return RedirectToAction("FileUpload", "FileUpload", new { error = errormsg });
}
but after db.SaveChanges(); above line its directing to catch (Exception ex) . once I debug I can see following inner exception message
Message = "New transaction is not allowed because there are other
threads running in the session."
Try to use:
IList<ProductStatistics> str = from a db.ProductStatisticsTemp select a;
foreach (var val in str)
{
// your code
}
You need to call EF and try to return it into an IList<T> of that target type and then we can use a loop on the IList<T>
You can also check this related thread

CRM transferring phonecall activity to a new Entity shows it on both Entities

I am trying to move the activities on some of our contacts/accounts to a new_private_contact_details entity. I have all of them but the phonecall working. The below code does seem to work, but it ends up showing the phonecall on the activity feed for both the new_private_contact_details entity as well as the existing Contact entity. Obviously this is a problem, since I'm trying to migrate those kinds of details to the private details, so having them still show up voids that process.
if (phoneCallsList != null && phoneCallsList.Count > 0)
{
foreach (PhoneCall pc in phoneCallsList)
{
if (pc.StatusCode.Value != 1)
{
int oldState = 0; //for the beginning statecode
int oldStatus = 0; //for the beginning statuscode
if (pc.StatusCode.Value == 3)
{
oldState = 2;
oldStatus = 3;
}
else
{
oldState = 1;
oldStatus = pc.StatusCode.Value;
}
//change status to open
SetStateRequest setStateRequest = new SetStateRequest()
{
EntityMoniker = new EntityReference
{
Id = pc.Id,
LogicalName = pc.LogicalName
},
State = new OptionSetValue(0),
Status = new OptionSetValue(1)
};
try
{
crm.Execute(setStateRequest);
}
catch (Exception ex)
{
throw new Exception("Error: " + ex);
}
pc.RegardingObjectId = pcd.ToEntityReference();
pc.StatusCode.Value = 1;
try
{
service.Update(pc);
}
catch (Exception ex)
{
throw new Exception("Error: " + ex);
}
//return status to closed
setStateRequest = new SetStateRequest()
{
EntityMoniker = new EntityReference
{
Id = pc.Id,
LogicalName = pc.LogicalName
},
State = new OptionSetValue(oldState),
Status = new OptionSetValue(oldStatus)
};
try
{
crm.Execute(setStateRequest);
}
catch (Exception ex)
{
throw new Exception("Error: " + ex);
}
}
else
{
pc.RegardingObjectId = pcd.ToEntityReference();
try
{
service.Update(pc);
}
catch (Exception ex)
{
throw new Exception("Error: " + ex);
}
}
}
}
I have already handled for when the phonecall is completed/closed. I'm updating the RegardingObjectId, but it doesn't remove it from the original entity, and deleting it in CRM from either entity, deletes it from both.
Again, I only seem to be having this issue with the phonecall entity. This code works perfectly for the others, i.e., appointments, tasks, letters, and emails
I figured out where the problem was occurring. It showed up on both entities, because for PhoneCalls there is also a PhoneCalls.To field that still referenced the old Contact entity. Unfortunately this field requires a PartyList, which, from what I've found, cannot be customized, so it must always point to a Contact, Lead, Opportunity or Account entity.

Categories

Resources