ASP.Net Core PostgreSQL Exceptions not throwing

I’m sure I’m missing something…

Using ASP.Net Core…

In Startup.cs Configure() I have the following when IsDevelopment().

app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseDatabaseErrorPage();

For whatever reason when debugging in VS 2017 and I have a Postgres error, the error will cause any compiled code, e.g. any code *.cs, to stop from executing, but then it will continue to execute the Razor page *.cshtml for that dto. The Dto will however be blank (all fields null or default) and may cause an error within the error page, but it may not have anything to do with the initial Postgres error.

Example:

  1. browse to a service route, e.g. /home/{myId}
  2. The HomeService will pick up that route.
  3. HomeService makes a db call. db.SingleById<HomeModel>(myId);
  4. That SingleById throws an error, like a column wasn’t found (usually a typo) {Npgsql.PostgresException (0x80004005): 42703: column "my_id" does not exist...
  5. HomeService doesn’t continue executing or throw db error so it can be shown.
  6. Instead, goes directly to executing the Razor page Home.cshtml
  7. May show an error or exception, but isn’t the db error, instead usually an error because the HomeService didn’t finish executing so a InvalidOperation or null exception on some other part of the dto.

Is there a way to catch/throw all these db errors directly into the browser and stop executing?

Right now I’m having to do the following to catch these errors.

try { 
  return db.SingleById<HomeModel>(id); 
} catch (Exception e) {
  throw; // set breakpoint here.
}`;

The page is still executed but instead of the Model being populated, it will be populated with an Error ResponseStatus with GetErrorStatus() or IsError.

This answer shows different ways of handling/rendering errors in your page:

The Razor Page lets you access the Error Info about the Request in:

bool IsError { get; }            //GetErrorStatus() != null
ResponseStatus GetErrorStatus(); //Error ResponseStatus
HtmlString GetErrorMessage();    //Just the Error Message to display

Which lets you render the same page but show the above error info in a UX friendly way.

A handy short-cut you can add to your page to just display the Error message is to

@if (RenderErrorIfAny()) { return; }

Which short-circuits the page and displays the error message in a bootstrap-friendly format which looks like:

<div id="error-response" class="alert alert-danger">
    <h4>{ErrorCode} : {Message}</h4>
    <pre>{StackTrace}</pre>
</div>

You can also add CSS to modify how the error looks.

Not in my case. The Razor page IsError = false after the db exception and continues to execute the razor page Home.cshtml. Only if there is another error in the razor page will the IsError be set to true. The original db exception is completely lost and can only be caught with a try {} catch {} around the db call.

Are you saying:

return db.SingleById<HomeModel>(id); 

And wrapped in a try/catch that rethrows:

try { 
  return db.SingleById<HomeModel>(id); 
} catch (Exception e) {
  throw; // set breakpoint here.
}

Yields different results? Because they both should have exactly the same behavior.

No those produce the same result.

The only way to catch or see the error is to set a breakpoint on the re-throw. Then when that breakpoint is triggered only then can you view the original exception.

Example:

public class HomeService : Service {
 public HomePage Any(HomePage request) {
  // below line throws error and stops execution.
   var model = Db.SingleById<HomeModel>(request.MyId); 
   // anything beyond this point does not execute, however the request is returned and razor page executes.
   request.Name = model.Name; // e.g. this is never set
  return request;
 }
}

the only way to see the error is to set a breakpoint around the try/catch, e.g.

public class HomeService : Service {
 public HomePage Any(HomePage request) {
  HomeModel model;
  // below line throws error and stops execution.
  try {
   model = Db.SingleById<HomeModel>(request.MyId); 
   } catch (Exception e) {
     throw; // set breakpoint here to view the Exception
   }
   // anything beyond this point does not execute, however the request is returned and razor page executes.
   request.Name = model.Name; // e.g. this is never set
   return request;
 }
}

The usual error is a typo in a property that maps to a postgres field.
Npgsql.PostgresException (0x80004005): 42703: column "my_id" does not exist...

It’s working as expected for me, I’m assuming you’re not seeing any Exception not just DB Exceptions?

I’m going to need a small stand-alone repro to identify what’s going on.

Yeah, it seems to be only db exceptions. I’ll see if I can put something together. Do you have a ASP.Net Core example project with a db? I can then use that as a starting point.

This would be a great time to try out v5.5’s new lightweight .NET Core projects support :slight_smile:

$ md ProjectName && cd ProjectName
$ web +init

I’ve just added a couple more gist apply.md options which will now let you specify which DB you want to use:

$ web +init+postgres
1 Like

I haven’t tried a new lightweight project yet, but I did enable Common Language Runtime Exceptions [Ctrl] + [Alt] + [E] in VS 2017. I ended up enabling all of them, but probably only needed to enable Npgsql.PostgresException. I’ll try a new project based upon v5.5’s lightweight projects when I have more time.

Unless there is something else I accidentally enabled (or disabled), this seems to break on npgsql database errors now and at least gives some indication of an error. Though I’ve never needed to do this before in previous versions of VS or in non-Core versions of .Net. You still can F5 / Continue thru the error which is fine so long as it breaks.

These are just debugger settings, so the issue was only about debugging behavior? i.e. does it have the expected behavior when the App is run normally?

The project isn’t in prod yet or tested an environment other than debug. So if this happens in integration/prod it’s definitely not expected behavior if these errors can’t be logged.

These settings wont change how the App is run and all Exceptions apart from StackOverflowException (and AccessViolationException by default) can be caught.