I have a handler class file, a default page and a master page.
The default page instantiates an instance of the handler class, and the handler then does all the communication with the database.
The master page contains a label that is supposed to display error outputs, passed to it from the handler via the default page. This is done via the following:
Handler:
catch (SqlException e)
{
errorString = e.ToString();
}
Default.aspx.cs:
errorString = handler.errorString;
((SiteMaster)Master).getErrorLabel.Text = errorString;
Site.Master.cs:
public Label getErrorLabel
{
get { return this.errorLabel; }
}
When I pass a value that doesn't match any record in my database, the errorString continues to hold null. Am I doing something wrong?
edit: I have also tried e.Message and e.Message.ToString() without success
Your catch() specifies an SQLException class. If the exception thrown is not of that type, then the actual exception is not handled. Try changing that to the base Exception class and see what you get. It's probably an error being thrown from somewhere else in the code.
Related
I log errors in my Actions using NLog to store errors with additional information, for example:
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x;
return RedirectToAction(MVC.Client.Index());
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
}
}
And I have Error handling configured in Web.config:
<customErrors mode="On" />
But I don't get redirected to the Error.cshtml when I execute the Action (the page remains in the same place), why?
Can I use Elmah to do the same thing? (logging additional information like client Id)
First of all, most people solve this error by not catching the exception. This way, the exception propagates to ASP.NET, which displays a "500 Internal Error" webpage, and all the pertinent information is logged.
If your server is configured for production, the error page will just say "an error occurred, details were logged."
If the server is configured for development, then you will get the famous yellow page with the exception type, the message, and the stack trace.
Swallowing the exception and manually redirecting to an error page is a bad practice because it hides errors. There are tools that examine your logs and give you nice statistics, for example about percentages of successful/failed requests, and these won't work any more.
So, not swallowing the exception is what people do, and at the very least, it solves your problem.
Now, I find this very clunky, because I do not like manually looking for the source files mentioned in the yellow page and manually going to the mentioned line numbers. I practically have no use for the yellow page, it might just as well just say "an error occurred, cry me a river, nah-nah-nah." I don't read the yellow page.
Instead, I do like to log exceptions on my own, and I have my logger begin each line with full-path-to-source-filename(line):, so that every line on the debug log in visual studio is clickable, and clicking on a line automatically opens up the right source file, and scrolls to the exact line that issued the log message. If you want this luxury, then go ahead and catch the exception, but right after logging the exception you have to rethrow it, so that things can follow their normal course.
Amendment
Here is some information that was added in comments:
So, you can do the following:
try
{
...
}
catch (Exception e)
{
log( "information" );
throw; //special syntax which preserves original stack trace
}
Or
try
{
...
}
catch (Exception e)
{
throw new Exception( "information", e ); //also preserves original stack trace
}
Do not do this: catch( Exception e ) { log( "information" ); throw e; } because it loses the original stack trace information of e.
In your code, error occur at the division portion(x/=x) so no execution of redirect line(index page) and jump to catch portion executing the logger. You have to define the redirect to Error.cshtml in catch portion also.
Note: when you use try catch block error will not occur at ASP.NET level resulting no redirect to Error.cshtml page
using NLog;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Edit(Client client)
{
try
{
// FORCE ERROR
var x = 0;
x /= x; /// error occur here
return RedirectToAction(MVC.Client.Index()); /// no execution of this line
}
catch (Exception e)
{
_logger.Error("[Error in ClientController.Edit - id: " + client.Id + " - Error: " + e.Message + "]");
/// add redirect link here
return RedirectToAction(MVC.Client.Error()); /// this is needed since the catch block execute mean no error at ASP.net level resulting no redirect to default error page
}
}
This will streamline your exception handling and allow you to manage the process more succinctly. Create an attribute like this:
public class HandleExceptionAttribute : System.Web.Mvc.HandleErrorAttribute
{
// Pass in necessary data, etc
private string _data;
public string Data
{
get { return _data; }
set { _data = value; }
}
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// Logging code here
// Do something with the passed-in properties (Data in this code)
// Use the filterContext to retrieve all sorts of info about the request
// Direct the user
base.OnException(filterContext);
}
}
Now you can use it on a controller or method level with an attribute like this:
[HandleException(Data="SomeValue", View="Error")]
Or, register it globally (global.asax) like this:
GlobalFilters.Filters.Add(new HandleExceptionAttribute());
How can i handle an exception that occurs when properties in my ViewModel get occurs? The property gets happen before the Loaded event. For example, I have a property (get-only) that calls some data method to return a collection of states to fill a combobox's itemsource. But sometimes SQL will not connect, and I get an exeption. There are multiple properties like this, I want to tell the user that the combos could not be loaded correctly and then just put them back at my home screen. However, i don'twant 5 message boxes if they all fail. Also, why does it continue to try to get the properties, even though i told it to go to the home screen when the first exception occured? Note: the GetStatesList() method also has try/catch and throw in the catch...
try
{
ObservableCollection<string> states=null;
// perform sql query
states=StateDat.Instance.GetStatesList(); //get the collection of state names
}
catch(Exception ex)
{
MessageBox.Show("Error"); //display an error message
MessengerInstance.Send(ViewModelNamesEnum.HomeVM); //go home
}
Have all the five statements continuously with in 1 try catch, instead of having try catch for each statement, so if exception occurs 2nd statement following 3 will not get executed and at any cost you will have only 1 msg box and you can return to the home screen as well without any issuse
Here is one way you could handle this..
Make separate methods for each property call.. and throw a custom exception to indicate something went wrong with that specific call..
Anyway the outer exception will make sure that if one fails, it bails out..
Method1() {
try {
//Code for Method1
}catch(Exception ex) { throw new CustomException(""); }
}
Method2() {
try {
//Code for Method2
}catch(Exception ex) { throw new CustomException(""); }
}
Method3() {
try {
//Code for Method3
}catch(Exception ex) { throw new CustomException(""); }
}
try {
Method1();
Method2();
Method3();
}catch(CustomException custom) {
// You would know specific reasons for crashing.. and can return meaningful message to UI.
} catch(Exception ex) {
//Anything that was un-handled
}
class CustomException : Exception {
//Implementation here..
}
I have setup so that if an Exception is thrown I can display it with my custom error page. But in some cases I don't want to be navigated to the error page, but want it to display a simple dialog window.
public ActionResult Page1()
{
//The custom error page shows the exception, if one was thrown
throw new Exception( "An exception was thrown" );
return View();
}
public ActionResult Page2()
{
//A dialog should show the exception, if one was thrown
try
{
throw new Exception( "An exception was thrown" );
}
catch( Exception ex )
{
ViewData["exception"] = ex;
}
return View();
}
Is it possible to have a CustomAttribute to handle an exception which has been thrown in an Controller action? If I added CatchException to Page2, can I automate the process of storing the exception in the ViewData, each time an exception was thrown. I don't have much experience of CustomAttributes and I'd be much appreciated if you could help me.
The Page2 example works perfectly fine, I just want to make the code cleaner as it isn't really pretty to have try catches in every action (where I want to show a dialog).
I am using .NET MVC 4.
You can create a base controller that catch the exceptions and handle it for you.
Also, looks like the Controllers already have a mechanism to do that for you. You'll have to override the OnException method inside the controller. You can get a good example here:
Handling exception in ASP.NET MVC
Also, there's another answer on how to use the OnException here:
Using the OnException
By using that, your code will be cleaner, since you will not be doing a lot of try/catch blocks.
You'll have to filter the exception you wanna handle. Like this:
protected override void OnException(ExceptionContext contextFilter)
{
// Here you test if the exception is what you are expecting
if (contextFilter.Exception is YourExpectedException)
{
// Switch to an error view
...
}
//Also, if you want to handle the exception based on the action called, you can do this:
string actionName = contextFilter.RouteData.Values["action"];
//If you also want the controller name (not needed in this case, but adding for knowledge)
string controllerName = contextFilter.RouteData.Values["controller"];
string[] actionsToHandle = {"ActionA", "ActionB", "ActionC" };
if (actionsTohandle.Contains(actionName))
{
//Do your handling.
}
//Otherwise, let the base OnException method handle it.
base.OnException(contextFilter);
}
You can create subclass of Exception class, and catch it in your Page 2
internal class DialogException : Exception
{}
public ActionResult Page2()
{
//This should a dialog if an exception was thrown
try
{
//throw new Exception( "An exception was thrown, redirect" );
throw new DialogException( "An exception was thrown, show dialog" );
}
catch( DialogException ex )
{
ViewData["exception"] = ex;
}
return View();
}
I am using Entity Framework and Telerik RadGrid. I have a table with a constraint which throws an exception if a product with a duplicate name is tried as an insert. I am trying to catch the exception in my business layer and it seems to run through the catch block fine but I get an error from the Telerik RadScriptManager
"Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerServerErrorException: Exception has been thrown by the target of an invocation."
instead of the Jquery popup that I am expecting with the message "Duplicate Product" Does anyone know what I am doing wrong? does the exception need to behandled in my DAL? but I dont think i should throw BusinessRuleExceptions from somewhere besides the BLL. I have posted the Insert function in my BL class below, if anyone has an idea what might be causing the Jscript error, please let me know, thanks!!
Edit
The Object Data Source TypeName is tied to the Business layer ProductBL
Product BL function Insert_Product is being called from my ObjectDataSource as the Insert function. In the Product.cs code behind class I have a function for Inserting Products where I am passing the product Name (see below), this function has a try catch bloeck..should I be throwing the exception here? I thought it would be right to throw the BusinessRulException in the Business Layer.
Product.CS class (Object Data Source Insert command)
protected void ODSProducts_Inserting(object sender, ObjectDataSourceMethodEventArgs e)
{
try
{
TextBox txtProductName = (TextBox)ProductsGrid.MasterTableView.GetInsertItem().FindControl("txtProductName");
((ACME.DAL.Product)e.InputParameters[0]).Product.product_name = txtProductName.Text;
}
catch (Exception ex)
{
HTMLError.HtmlError.LogHtmlError(ex, Application["ErrorLog"].ToString());
throw;
}
}
ProductsBL.CS
public void Insert_Product(Product product)
{
try
{
repository.Insert_Product(product);
}
catch (Exception ex)
{
if (ex.GetType().Name == "UpdateException")
{
throw new BusinessRuleException("Duplicate Product");
}
}
}
Product.DAL
public void InsertProduct(Product product)
{
context.Products.AddObject(product);
context.SaveChanges();
}
You will want to tap into the RadGrid_ItemInserted event. There the event arg should have a reference to the exception thrown, and you can mark that you handled the exception so it doesn't bubble up to the user.
That's if you are letting the ODS do the full insert, and not inserting manually.
ok I figured this out after wasting too many hours on this. It seems like Telerik doesnt like me throwing the Business rule exception from the Business Layer class. So I ended up handling the exception instead in the codebehind in the object data source_Inserted event. This is what I did to display the error in a RadAlertWindow from the event.
enter code here
protected void ODSProducts_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
if (e.Exception != null)
{
if (((e.Exception.InnerException).InnerException).Message.Contains("Cannot insert duplicate key row"))
{
RadWindowManager.RadAlert("Duplicate Product, Enter a new Product", 330, 100, "Insert Error", "");
e.ExceptionHandled = true;
}
}
}
I dont like checking the .InnerException.Message.Contains bit but thats the only way I knew how to make sure if the exception was SQL throwing duplicate error on the unique constraint on the field. If someone knows of a more elegant way to do this please share. Hope this helps someone else as well.
try{
myConnection.Open();
SqlCommand myCommd = new SqlCommand(StrMemberId, myConnection);
myCommd.Parameters.AddWithValue("#MemberId", TxtEnterMemberId.Text);
int value=(int)myCommd.ExecuteScalar();
if (value!= 0 )
{
Response.Redirect("GeneralExamination.aspx? MemberId=" + this.TxtEnterMemberId);
}
else
{
string js = "$.jGrowl(' Invalid Member Id Try Again ');";
Page.ClientScript.RegisterStartupScript(typeof(string), "jgrowlwarn", js, true);
TxtEnterMemberId.Text = "";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally {
myConnection.Close();
}
}
What I am trying to do here is search a Member if does not exist or invalid input jgrowl will show a message(works fine).
i.) Now the problem is when i give the correct memberId a message is generated saying "thread was being aborted." but it does gets redirected to the destined page.What isthe exception about?
ii.) When i go to the next page and click on the back button.A msg box says"To display this page, Firefox must send information that will repeat any action (such as a search or order confirmation) that was performed earlier." if i click resend the growl is displayed again. How to deal with that?
Please help to overcome the problems..
I) I think that the exception is for making a Response.Redirect inside a try/catch block, to avoid the exception you could add a false parameter to Redirect.
more info: ThreadAbortException Occurs If You Use Response.End, Response.Redirect, or Server.Transfer