How do I gracefully handle errors when trying to connect to a database that may be down, or given an incorrect connection string in MVC 4?
I have my string in Web.config and the way it accesses the database is just instantiating my class derived from DBContext in each controller, like so:
private DBEntities db = new DBEntities("database");
This works fine if the database is up and everything is correct, but if it's not, the pages on my web site display this error:
Exception Details: System.ComponentModel.Win32Exception: The network path was not found
Obviously I don't want this to appear, what I'd like to do is have a way to try catch the error and display a custom error page (similar to 404 Page Not Found).
Thank you.
Do the initialisation in the function
private DBEntities db;
// then in your function attempt to initialise
try{
db = new DBEntities("database");
db.Connection.Open();
}
catch(Exception ex){
if(db.Connection != ConnectionState.Closed){
db.Connection.Close();
db.Dispose();
//go to error page
}
}
It depends where private DBEntities db = new DBEntities("database"); is and when it is called but why not just:
try {
private DBEntities db = new DBEntities("database");
} catch (Exception ex) {
//do something here
}
start by adding an 'Error.cshtml' view in the Shared views folder. Give it a model type of
System.Web.Mvc.HandleErrorInfo
Then add the following to your web.config file
<system.web>
<customErrors mode="On" defaultRedirect="/Error" />
</system.web>
This will redirect to the Error view on any exception.
Now it's just a matter of styling your error page.
I'd advise you try { } catch { } as suggested in the other posts, in your catch block, just rethrow with a user-friendlier message - like "Database connection could not be made."
Quite a full discussion/explanation here: http://www.prideparrot.com/blog/archive/2012/5/exception_handling_in_asp_net_mvc
Related
I have the following global error handling setup for a webapp.
Global.asax.cs
protected void Application_Error()
{
//log error
_logger.Error("something bad happened", Server.GetLastError());
//clear error
Server.ClearError();
//redirect to error page
Response.RedirectToRoute(new { controller = "services", action = "error" });
}
Services controller with Error Action. This view uses the ErrorModel class as the model.
public ActionResult Error()
{
return View(new ErrorModel{ Message = "nothing really happened go to bed" });
}
General view:
public ActionResult General()
{
throw new Exception("Exception XYZ");
}
When I debug the application using VS F5, and go to /general view, the correct exception gets logged (Exception XYZ).
But when testing the published IIS webapp, and going to /general view, a different, unexpected exception is getting logged:
System.InvalidOperationException: The model item passed into the
dictionary is of type 'System.Web.Mvc.HandleErrorInfo', but this
dictionary requires a model item of type
'MyWebsite.Models.Services.ErrorModel'.
The Exception XYX does not get logged at all. The correct Error view with the correct error message (nothing really happened go to bed) does get shown.
What am I doing wrong? How do I make sure Exception XYZ gets logged?
I don't intend to use HandleErrorAttribute or override void OnException in the controller at this time.
EDIT
Seems like this issue is already discussed in 2010 on SO here
I have a project that is written using C# on the top of ASP.NET Core 2.2 framework.
The application throws a custom exception when something unexpected happens.
For example, if the app can't find a default setting in the database, it throws ApplicationIsNotSetupException(). This exception indicates that the admin did not install the project using the installation process as they should. Therefore, I want to direct them to the installation controller. (i.e, SetupController.Install())
How can I redirect the user to s specific route if the ApplicationIsNotSetupException() was caught?
Here's a possibility:
// This should be before app.UseMvc
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (ApplicationIsNotSetupException)
{
context.Response.Redirect("/setup/install");
}
});
I found this Question, there are some good answers there too.
You can insert your code into try/catch:
try {...yourCode...}
catch (Exception ex)
{
if (ex is ApplicationIsNotSetupException)
..Do what ever you want..
else
...
}
than, everytime it will catch the exception you want it will go into the if loop
I've created a simple webpage called mytestpage and am using the following code:
#using System
#using System.Drawing
#using System.IO
try
{
int d = 1/0;
<p>Error not caught</p>
}
catch(Exception notUsedForTesting)
{
<p>Error caught</p>
}
Instead of seeing "Error caught" text on mytestpage, my site redirects to a differing page as follows: Error.cshtml?aspxerrorpath=/mytestpage.
I would like to know how to avoid this re-direct, and handle the exception my self.
My current web.config file contains:
<customErrors mode="RemoteOnly">
<error statusCode="500" redirect="~/Error.cshtml"/>
</customErrors>
I have tried:
<customErrors mode="Off">
</customErrors>
as well, and I get the same redirect result.
Just tested it locally. It seems like the error you are getting is not a division by zero exception but rather a compile error
What happens is that your page will be compiled on the fly since you did not enable precompile. So when you navigate to the page it will trigger a compile but that 1/0 immediately error out. Your try catch logic did not even execute.
Try doing a throw new Exception(); instead of 1/0 and it should work as expected
However I would discourage you from doing complex logic directly inside View. You should try to move the try catch logic into ViewModel or at least controller level
In the config of my application I deleted the customErrors to make a test of an error that is difficult to replicate.
And where the error should be happening I put a try:
if (ModelState.IsValid)
{
db.TarefaHoras.Add(tarefaHora);
try
{
db.SaveChanges();
}
catch (DbUpdateException ex)
{
ErroDetalhe erro = new ErroDetalhe();
erro.Data = tarefaHora.Data;
erro.UsuarioId = tarefaHora.ApplicationUserId;
erro.JSON = JsonConvert.SerializeObject(tarefaHora);
erro.Tipo = "TarefaHora";
erro.Controller = "TarefaHoras";
erro.Action = "Create Post";
erro.Exception = ex.GetType().FullName;
db.ErroDetalhes.Add(erro);
db.SaveChanges();
return RedirectToAction("ErroNaAtualizacaoDaBase", "Erros", new { id = erro.ID });
}
return RedirectToAction("Index", "Home");
}
return View(tarefaHora);
}
This error only happens at runtime and only on Azure server, so my attempt was to get a better description of the error and for this I created a table where the information is deposited.
But....
The problem is that the View error that I'm getting is not the View "ErrorInputUpdate" that I created and that is redirected in Catch.
I am getting the default AspNET error view.
So I went into Shared Views and deleted the default page and removed customErrors from web.config.
And even then Azure still brings the default error view.
Anyone have any idea what might be happening?
So calling db.SaveChanges(); generates an exception. That means that the context db is invalid and it should be resolved or discarded.
However, what you are doing is creating an error entry and add using the same db context which is still in a faulty state. So the call to db.SaveChanges(); in your catch block will still generate an exception, but this time it won't be catched.
The solution is either to write the error to something else than the database, like a file or whatever or to use another EF context instance. Other options are to removed or correct the invalid entries before calling db.SaveChanges(); again or do not store the error in the database but include all the details in the redirect view.
I have a wizard that loads up a partial view on each step. When the form generates an error it loads the error page. However it loads the FULL error page in the partial view and makes it look like its loading two pages at once. How can I redirect to the error page outside of the partial view?
I have this on the bottom of the step
try
{
return PartialView("AccountSelection", vm);
}
catch (Exception ex)
{
return View("Error");
}
}
return PartialView();
Personally, I'd handle errors at a higher level. You can specify an error page in your web.config file.
<configuration>
<system.web>
<customErrors defaultRedirect="error.aspx" mode="RemoteOnly">
</customErrors>
</system.web>
</configuration>
If the goal is to have just a section of the page show an error message, then you could create a partial page named "Error" and save it in Views/Shared. Then change your code to...
try
{
return PartialView("AccountSelection", vm);
}
catch (Exception ex)
{
return PartialView("Error");
}
}
return PartialView();
Use RedirectToAction("ErrorAction","ErroController"). Based upon your needs, the action might be in a separate controller or in the same one(which I don't think would be right). You'll also need to customize your Error action method and view so that you can pass different messages for different circumstances.