Propagate error back to user in ASP.net - c#

I'm trying to propagate an error back to the user if their input is wrong.
Here's the code that should throw the error:
var sql2 = "select COD_id from COD_Codes where COD_name = #Value and COD_COT_id = #codeId";
SqlCommand cmd2 = new SqlCommand();
cmd2.CommandText = sql2;
cmd2.Parameters.AddWithValue("#Value", value);
cmd2.Parameters.AddWithValue("#codeId", result);
cmd2.Connection = connection;
cmd2.Transaction = transaction;
var code = Convert.ToInt32(cmd2.ExecuteScalar());
if (code == 0)
{
throw new ApplicationException(string.Format(#"({0}) is not a valid data for this credit report. Please check with your sys admin if this is a new data value", code));
}
return code;
This code is a few levels below my controller which looks like this:
[HttpPost]
public HttpResponseMessage SaveCreditReport(GrowData growData)
{
using (SqlConnection connection = CreateSqlConnection())
{
using (var transaction = connection.BeginTransaction())
{
try
{
var service = CreateCreditService(connection, transaction);
var user = GetUser(service);
if (log.IsInfoEnabled)
log.InfoFormat("Save credit report called by user '{0}' ",
user.UserName);
var growValues = growData.GrowRows.Select(growRow => new GrowValue() { Mnemonic = growRow.Mnemonic, Time = growRow.Time, Value = growRow.Value }).ToList();
var growCompValues = growData.GrowCompRows.Select(compRow => new GrowCompRowValue() { Mnemonic = compRow.Mnemonic, Value = compRow.Value }).ToList();
var reportId = service.SaveGrowCreditData(user.Id, growData.CompanyId, growValues, growCompValues, growData.Publish);
var message = this.Request.CreateResponse<int?>(HttpStatusCode.OK, reportId);
transaction.Commit();
return message;
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
I'm not sure how and what kind of exception I need to throw to propagate the error back to user level. I don't want the standard "500 internal server error" message but instead the one that I've formatted in the code.
Can I just throw a new httpresponseexception instead of ApplicationException directly from the code? Will it then propagate back to controller level and unto the user?

In your catch method use CreateErrorResponse to create a response with whatever HttpStatusCode is appropriate:
catch (ApplicationException ex)
{
transaction.Rollback();
return this.Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, ex);
}
A caveat from the documentation:
The request must be associated with an HttpConfiguration instance. An HttpResponseMessage whose content is a serialized representation of an HttpError instance [is returned]

Related

How to store result from mysql query

How do I store the results from a mysql query for use in other classes most efficiently?
I've tried the following code, which executes properly and stores all data in reader as it should. Reading the DataReader here works fine if I want to!
public class DatabaseHandler
{
public void MySqlGetUserByName(string input_username, MySqlDataReader reader)
{
try
{
_database.Open();
string query = "SELECT * FROM users WHERE username = '#input'";
MySqlParameter param = new MySqlParameter(); param.ParameterName = "#input"; param.Value = input_username;
MySqlCommand command = new MySqlCommand(query, _database);
command.Parameters.Add(param);
reader = command.ExecuteReader();
_database.Close();
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
But when I try to read the same DataReader here, it is null and throws an exception (right after Debug6).
public class LoginHandler
{
public static void UserAuth(Client user, string input_username, string input_password)
{
DatabaseHandler dataBase = new DatabaseHandler();
MySqlDataReader dataReader = null;
dataBase.MySqlGetUserByName(input_username, dataReader);
Console.WriteLine("Debug6");
if (!dataReader.HasRows)
{
user.SendChatMessage("No match found.");
return;
}
while (dataReader.Read())
{
user.SetData("ID", (int)dataReader[0]);
user.SetData("username", (string)dataReader[1]);
user.SetData("email", (string)dataReader[2]);
user.SetData("password", (string)dataReader[3]);
}
dataReader.Close();
}
}
Please let me know how to make this work, or if there is a more efficient way of doing this without limiting the function of MySqlGetUserByName. The purpose of it is to input a name and a place to store all info from the match in the database.
Also, feel free to drop in any other suggestions that could make the code more efficient.
You could change your MySqlGetUserByName to return a User instance if all goes well, otherwise you return a null instance to the caller (Or you can thrown an exception, or you can set a global error flag in the DatabaseHandler class..., but to keep things simple I choose to return a null)
public class DatabaseHandler
{
public User MySqlGetUserByName(string input_username)
{
User result = null;
try
{
string query = "SELECT * FROM users WHERE username = #input";
using(MySqlConnection cnn = new MySqlConnection(......))
using(MySqlCommand command = new MySqlCommand(query, cnn))
{
cnn.Open();
command.Parameters.AddWithValue("#input", input_username);
using(MySqlDataReader dataReader = command.ExecuteReader())
{
if (dataReader.Read())
{
result = new User();
result.ID = Convert.ToInt32(dataReader[0]);
..... and so on with the other user properties ....
}
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
// Return the user to the caller. If we have not found the user we return null
return result;
}
}
In the same way the caller handles the situation
public class LoginHandler
{
public static void UserAuth(string input_username, string input_password)
{
DatabaseHandler dataBase = new DatabaseHandler();
User result = dataBase.MySqlGetUserByName(input_username);
// If we have not found the user we have a null in the variable
if(result == null)
{
// Send your message using a static method in the user class
// User.SendMessage("User with username {input_username} not found!");
}
else
{
// User ok. return it? or do something with its data?
}
}
}

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.

Declare a var of type Stripe charge create outside try catch

I have the following Payments code.
var service = new StripeChargeService(newgenSecretKey);
try
{
var result = service.Create(newCharge);
if (result.Paid)
{
lab.Text = "It worked";
CartOrders.UpdateTransactionID(result.Id, OrderID, "Express Checkout");
Response.Redirect("PgeCustSuccess.aspx?OrderID=" + OrderID);
}
}
catch (StripeException stripeException)
{
Debug.WriteLine(stripeException.Message);
stripe.Text = stripeException.Message;
//CartOrders.UpdateTransactionID(result.FailureMessage, OrderID, "Express Checkout");
}
I would like to log the commented out value in the catch block(result.FailureMessage).I am trying to catch the Stripe Errors if thrown and logging it into my database. The problem I am facing is that I cannot get the result.FailureMessage to work in the catch block. I understand that a global var cannot be declared and must only be used locally.
Here are a few thing that I tried.
Declared the result outside the try block but did not succeed because var should be initialised with a type and it is of type stripe
var service = new StripeChargeService(newgenSecretKey);
var result = new { };
try
{
//did not work
result = service.Create(newCharge);
if (result.Paid)
{
Any Ideas ?
You could do:
var service = new StripeChargeService(newgenSecretKey);
Stripe result;
try
{
result = service.Create(newCharge);
if (result.Paid)
{
Try moving result up out of the try, or:
var service = new StripeChargeService(newgenSecretKey);
var result;
try
{
result = service.Create(newCharge);
if (result.Paid)
{
lab.Text = "It worked";
CartOrders.UpdateTransactionID(result.Id, OrderID, "Express Checkout");
Response.Redirect("PgeCustSuccess.aspx?OrderID=" + OrderID);
}
}
catch (StripeException stripeException)
{
Debug.WriteLine(stripeException.Message);
stripe.Text = stripeException.Message;
//CartOrders.UpdateTransactionID(result.FailureMessage, OrderID, "Express Checkout");
}
Note that your result.FailureMessage may throw an Exception if result is never populated; you could use an if statement or try-catch to prevent against this.

SqlException: do not abort a transaction

I have a code that adds data to two EntityFramework 6 DataContexts, like this:
using(var scope = new TransactionScope())
{
using(var requestsCtx = new RequestsContext())
{
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
logsCtx.SaveChanges();
}
}
}
There is an insert trigger in Requests table that rejects some values using RAISEERROR. This situation is normal and should be handled by the try-catch block where the SaveChanges method is invoked. If the second SaveChanges method fails, however, the changes to both DataContexts must be reverted entirely - hence the transaction scope.
Here goes the error: when requestsCtx.SaveChanges() throws a exception, the whole Transaction.Current has its state set to Aborted and the latter logsCtx.SaveChanges() fails with the following:
TransactionException:
The operation is not valid for the state of the transaction.
Why is this happening and how do tell EF that the first exception is not critical?
Really not sure if this will work, but it might be worth trying.
private void SaveChanges()
{
using(var scope = new TransactionScope())
{
var log = CreateRequest();
bool saveLogSuccess = CreateLogEntry(log);
if (saveLogSuccess)
{
scope.Complete();
}
}
}
private LogEntry CreateRequest()
{
var req = new Request { Id = 1, Value = 2 };
var log = new LogEntry { RequestId = 1, State = "OK" };
using(var requestsCtx = new RequestsContext())
{
requestsCtx.Requests.Add(req);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
finally
{
return log;
}
}
}
private bool CreateLogEntry(LogEntry log)
{
using(var logsCtx = new LogsContext())
{
try
{
logsCtx.Logs.Add(log);
logsCtx.SaveChanges();
}
catch (Exception)
{
return false;
}
return true;
}
}
from the documentation on transactionscope: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx
If no exception occurs within the transaction scope (that is, between
the initialization of the TransactionScope object and the calling of
its Dispose method), then the transaction in which the scope
participates is allowed to proceed. If an exception does occur within
the transaction scope, the transaction in which it participates will
be rolled back.
Basically as soon as an exception is encountered, the transaction is rolled back (as it seems you're aware) - I think this might work but am really not sure and can't test to confirm. It seems like this goes against the intended use of transaction scope, and I'm not familiar enough with exception handling/bubbling, but maybe it will help! :)
I think I finally figured it out. The trick was to use an isolated transaction for the first SaveChanges:
using(var requestsCtx = new RequestsContext())
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
using(var outerScope = new TransactionScope())
{
using(var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
requestsCtx.SaveChanges();
innerScope.Complete();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
}
logsCtx.SaveChanges();
outerScope.Complete();
}
}
Warning: most of the articles about RequiresNew mode discourage using it due to performance reasons. It works perfectly for my scenario, however if there are any side effects that I'm unaware of, please let me know.

Error when Inserting using SqlMapperExtension Dapper

Currently playing around with Dapper I'm trying to insert values into the db as follows
using (var sqlCon = new SqlConnection(Context.ReturnDatabaseConnection()))
{
sqlCon.Open();
try
{
var emailExists = sqlCon.Query<UserProfile>(#"SELECT UserId FROM User_Profile WHERE EmailAddress = #EmailAddress",
new { EmailAddress = userRegister.EmailAddress.Trim() }).FirstOrDefault();
if (emailExists == null) // No profile exists with the email passed in, so insert the new user.
{
userProfile.UniqueId = Guid.NewGuid();
userProfile.Firstname = userRegister.Firstname;
userProfile.Surname = userRegister.Surname;
userProfile.EmailAddress = userRegister.EmailAddress;
userProfile.Username = CreateUsername(userRegister.Firstname);
userProfile.Password = EncryptPassword(userRegister.Password);
userProfile.AcceptedTerms = true;
userProfile.AcceptedTermsDate = System.DateTime.Now;
userProfile.AccountActive = true;
userProfile.CurrentlyOnline = true;
userProfile.ClosedAccountDate = null;
userProfile.JoinedDate = System.DateTime.Now;
userProfile.UserId = SqlMapperExtensions.Insert(sqlCon, userProfile); // Error on this line
Registration.SendWelcomeEmail(userRegister.EmailAddress, userRegister.Firstname); // Send welcome email to new user.
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
sqlCon.Close();
}
}
The error I get is
ExecuteNonQuery requires the command to have a transaction when the connection
assigned to the command is in a pending local transaction. The Transaction
property of the command has not been initialized.
I have googled this error, but I misunderstood the answers provided.
From the error message I assume that you have started a transaction that was neither committed nor rolled back. The real cause for this error message is elsewhere.
I suggest you to log requests in Context.ReturnDatabaseConnection() and trace what requests precede this error.
Also I advice you to look in your code for all transactions and check if they are correctly completed (commit/rollback).

Categories

Resources