So I have a database that fills up when a value is not found inside of it already, basically is a web API written in C# that checks if there is a value inside of it, if its null then calls another web service that will retrieve the data and insert a new record into the database and return the newly created record to the user. The code is like this:
[HttpGet ("{idDocument}")]
public async Task<IActionResult> GetPerson (string idDocument)
{
// Get the person record from the database
var person = await repository.GetPersonAsync (idDocument);
// if the record does not exist, return not found
if (person == null)
{
Person newPerson = await GetNewPersonFromRemoteServer(idDocument);
var result = mapper.Map<Person, PersonResource>(newPerson);
return Ok(result);
}
// Map the record to a resource to return to user
var resource = mapper.Map<Person, PersonResource> (person);
// return record resource to user
return Ok (resource);
}
private async Task<Person> GetNewPersonFromRemoteServer(string id)
{
Person newPerson = new Person();
string address = "http://remoteservice.serv/?id=";
string fullAddress = address + id;
try
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(new Uri(fullAddress));
string responseMessage = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
Person newPerson = JsonConvert.Deserialize<Person>(responseMessage);
repository.AddPersonAsync(newPerson);
await uow.CompleteAsync();
return newPerson;
}
return newPerson;
}
}
catch (Exception ex)
{
return newPerson;
}
}
But the service is working sometimes and sometimes its returning null even tho if I execute the remote service I get a response with the correct data. I believe it has something to do with the nested async/await methods/functions but I cant seem to figure it out a better solution to my problem. Any help would be greatly appreciated!
You have extremely faulty exception handling. Right now you swallow Exceptions. Moreover, you do it with Fatal Exceptions. That is a deadly sin of exception handling and can easily explain any odd behavior.
After a fatal exception your programm should always close. Ignoring them only leads to more followup errors that are impossible to debug or even predict.
You should read up on proper exception handling. Here are two articles I linked often:
http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx
http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
Related
I'm trying to figure out what to do with some errors in Unity when I call an API and how to propogate it up to the user interface - where and how to handle things. I've built aspnet APIs but there I'd normally use some error handling middleware to keep my controllers clean.
Let say we have some code like this (I'm using controller / repository language cos that's what I know).
A UI button fires an event like OnLoginButtonPressed.
An AuthController class reacts to the event by calling it's login method and then doing some logic when the response comes through, as follows:
public async void Login(LoginModel input)
{
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
The Auth.Repo calls the API and tries to return a Token class (just a wrapper around a JWT string)
public async Task<Token> Login(LoginModel input)
{
string json = JsonConvert.SerializeObject(input);
var request = UnityWebRequest.Post(app.baseURL + "authentication/login", json);
request.SetRequestHeader("Content-Type", "application/json");
request.SendWebRequest();
while (!request.isDone)
{
await Task.Yield();
}
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else
{
throw
}
}
So that's without exception handling. So I want to try to let the user know if there is a connection error, or they have put invalid details etc... I'm guessing I'm supposed add some logic into the AuthRepo such as:
if (request.result == UnityWebRequest.Result.Success)
{
Token token = JsonConvert.DeserializeObject<Token>(request.downloadHandler.text);
return token;
}
else if (request.result== UnityWebRequest.Result.ConnectionError)
{
throw new ConnectionException(request.error);
}
else if (request.result == UnityWebRequest.Result.DataProcessingError)
{
throw new BadRequestException(request.error);
}
else
{
throw new System.Exception(request.error);
}
This seems like a lot of code, and would end up in every method in every repo (unless I pull it out into some helper method?).... anyway, and then in the controller I would do something like:
try {
var result = await AuthRepo.instance.Login(input);
app.token = result;
EventService.OnSuccessfulLogin();
}
catch (ConnectionException ex)
{
EventService.OnConnectionError(ex.Message);
//some UI object would listen for this event and show the connection error message.
}
catch (BadRequestException ex)
{
EventService.LoginFailedError(ex.Message);
}
finally
{
EventService.UnknownError(ex.Message);
}
Is this completely down the wrong path? Seems like the code is just gonna get swamped with exception handling, or is this the correct way?
I've worked through a few YouTube videos that seem to suggest this is right, but they don't really show my use case (talking to APIs) so I'm just trying to be sure.
because UnityWebRequest.Result is an enum, you can start by using a switch statement here. Not only is this cleaner, it performs better too.
Another thing you can do is create an abstract class (e.g. APIException) and make that responsible for creating the correct exception instances, by giving it some static method like APIException FromUWRResult(UnityWebRequest.Result result).
Handling the exceptions can be done in APIException too. Give it an abstract method Handle() and implement accordingly in each of the deriving classes.
Now your code would look like this:
var ex = APIException.FromUWRResult(request.result);
if(ex != null) {
throw ex;
}
...
catch(APIException ex) {
ex.Handle();
}
I've been using azure table storage for years, and I'm not sure what the "proper" way to do this is with the newest WindowsAzure.Storage library, version 5.0.1-preview (for use in a new ASP.NET 5 application):
Problem:
Given a partition key and row key, delete the row without checking for existence first, and without failing if it does not exist.
Current Solution: This code works... but the exception handling is confusing:
public async Task DeleteRowAsync(CloudTable table, string partition, string row)
{
var entity = new DynamicTableEntity(partition, row);
entity.ETag = "*";
var op = TableOperation.Delete(entity);
try
{
await table.ExecuteAsync(op);
}
catch (Exception ex)
{
var result = RequestResult.TranslateFromExceptionMessage(ex.Message);
if (result == null || result.HttpStatusCode != 404)
throw ex;
}
}
Questions:
The exception itself pointed me to this TranslateFromExceptionMessage method... I can't find a whole lot of information on that and WrappedStorageException (the type of the exception that is thrown). Is this some kind of new/preferred way to check for 404 errors on storage exceptions? Does anyone know if all storage exceptions will now use this, or do I need to write code to test and figure it out?
There is an InnerException of type StorageException. Presumably our older code that used StorageException.RequestInformation.HttpStatusCode could access this inner exception in the same way. Is that "OK", or is parsing these new XML error messages better or more robust somehow?
Is there a different approach altogether that I should be considering for this case?
If you are using the latest client (Azure.Data.Tables), the delete method automatically swallows 404 responses and does not throw. This approach avoids the need to write code that introduces race conditions (checking first before performing an operations) or having to handle this condition with a try/catch block.
If you want to know if the operation actually deleted a table or it didn't exist, you can inspect the Status property of the response.
Response response = await tableClient.DeleteAsync();
if (response.Status == (int)HttpStatusCode.NotFound)
{
// entity didn't exist)
}
The RequestResult.TranslateFromExceptionMessage method is now marked [Obsolete] and I wanted a way to ignore 404's myself.
Based on your tip to check out the RequestInformation.HttpStatusCode I came up with the following:
try
{
await table.ExecuteAsync(op);
}
catch (StorageException storEx)
{
if (storEx.RequestInformation.HttpStatusCode != 404)
{
throw;
}
}
There is a similar approach found in the AspNet WebHooks project when configured to use Azure Table Storage. Take a look at the Microsoft.Aspnet.WebHooks.custom.AzureStorage StorageManager class.
I'm not sure this adds much on top of what you'd already found, but they handle everything without throwing an exception and always return a status code so you can react to that as necessary.
One difference here is they pass in the table and the operation to a multi-purpose ExecuteAsync method, rather than having one specifically for delete, but that's just an implementation detail.
Relevant code from their example:
public async Task<TableResult> ExecuteAsync(CloudTable table, TableOperation operation)
{
if (table == null)
{
throw new ArgumentNullException(nameof(table));
}
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
try
{
var result = await table.ExecuteAsync(operation);
return result;
}
catch (Exception ex)
{
var errorMessage = GetStorageErrorMessage(ex);
var statusCode = GetStorageStatusCode(ex);
var message = string.Format(CultureInfo.CurrentCulture, AzureStorageResources.StorageManager_OperationFailed, statusCode, errorMessage);
_logger.Error(message, ex);
return new TableResult { HttpStatusCode = statusCode };
}
}
public string GetStorageErrorMessage(Exception ex)
{
if (ex is StorageException storageException && storageException.RequestInformation != null)
{
var status = storageException.RequestInformation.HttpStatusMessage != null ?
storageException.RequestInformation.HttpStatusMessage + " " :
string.Empty;
var errorCode = storageException.RequestInformation.ExtendedErrorInformation != null ?
"(" + storageException.RequestInformation.ExtendedErrorInformation.ErrorMessage + ")" :
string.Empty;
return status + errorCode;
}
else if (ex != null)
{
return ex.Message;
}
return string.Empty;
}
public int GetStorageStatusCode(Exception ex)
{
return ex is StorageException se && se.RequestInformation != null ? se.RequestInformation.HttpStatusCode : 500;
}
I have a Web API 2 endpoint set up to test a 2+ second ADO.NET call. When attempting to "burst" this api, it fails horribly when using async methods. I'm getting connection timeouts and reader timeouts. This doesn't happen with the synchronous version of this method when "bursted". The real problem is as follows...
Async ADO.NET behaves strangely when exceptions are thrown. I can see the exceptions thrown in the Visual Studio output, but they are not caught by my code. As you'll see below, I've tried wrapping try/catches around just about everything and have had no results. I did this to be able to set break points. I understand that catching exceptions just to throw them is bad. Initially, I only wrapped the call in the API layer. The worst part is, this locks up the entire server.
Clearly, there's something I'm missing about async ADO.NET. Any ideas?
EDIT:
Just to clarify what I'm trying to do here. This is just some test code on my local computer that's talking to our developmental database. It was just to prove/disprove that we can handle more traffic with async methods against our longer running db calls. I think what's happening is that as the calls are stacking up. In doing so, the await'ed connections and readers are timing out because we're not getting back to them quickly enough. This is why it doesn't fail when it's ran synchronously. This is a completely different issue. My concern here is that the operations are not throwing exceptions in a way that can be caught. The below is not production code :)
Web API2 Controller:
[Route("api/async/books")]
[HttpGet]
public async Task<IHttpActionResult> GetBookAsync()
{
// database class instantiation removed
// search args instantiation removed
try
{
var books = await titleData.GetTitlesAsync(searchArgs);
return Ok(books);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
Data Access:
public async Task<IEnumerable<Book>> GetTitlesAsync(SearchArgs args)
{
var procName = "myProc"
using(var connection = new SqlConnection(_myConnectionString))
using (var command = new SqlCommand(procName, connection) { CommandType = System.Data.CommandType.StoredProcedure, CommandTimeout = 90 })
{
// populating command parameters removed
var results = new List<Book>();
try
{
await connection.OpenAsync();
}
catch(Exception ex)
{
throw;
}
try
{
using (var reader = await command.ExecuteReaderAsync())
{
try
{
while (await reader.ReadAsync())
{
// FROM MSDN:
// http://blogs.msdn.com/b/adonet/archive/2012/07/15/using-sqldatareader-s-new-async-methods-in-net-4-5-beta-part-2-examples.aspx
// Since this is non-sequential mode,
// all columns should already be read in by ReadAsync
// Therefore we can access individual columns synchronously
var book = new Book
{
Id = (int)reader["ID"],
Title = reader.ValueOrDefault<string>("Title"),
Author = reader.ValueOrDefault<string>("Author"),
IsActive = (bool)reader["Item_Active"],
ImageUrl = GetBookImageUrl(reader.ValueOrDefault<string>("BookImage")),
ProductId = (int)reader["ProductID"],
IsExpired = (bool)reader["Expired_Item"]
};
results.Add(book);
}
}
catch(Exception ex)
{
throw;
}
}
}
catch(Exception ex)
{
throw;
}
return results;
}
}
I am making a call to a Web API that I wrote. I am working through the bugs on both sides and am getting a 500 error. I want to see that is in that error message to see what might be the problem. How do I find that?
using (var client = new HttpClient())
{
var fooURL = Url.RouteUrl("PayrollApi", new { httproute = string.Empty, controller = "LeaveRequest" }, Request.Url.Scheme);
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result;
}
I don't see where that text might be stored so I can figure out what other bugs need to be fixed. How do I get that text?
Update: something I didn't clarify. I put a breakpoint on the WebAPI I am calling and the break point is never hit. However, when I call it from Poster, I hit the break point. The URL is correct.
public HttpResponseMessage Post([FromBody]LeaveRequest value)
{
if (ModelState.IsValid)
{
// code here
}
}
I was able to make a change to get the error message:
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result.Content.ReadAsStringAsync();
instead of
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result;
I was then able to see my error and fix it.
I am making a call to a Web API that I wrote. I want to see that is
in that error message to see what might be the problem.
You might want to put the code you have in PayrollApi.LeaveRequest in a try-catch block, something like:
public HttpResponseMessage LeaveRequest()
{
try {
// do something here
}
catch(Exception ex) {
// this try-catch block can be temporary
// just to catch that 500-error (or any unexpected error)
// you would not normally have this code-block
var badResponse = request.CreateResponse(HttpStatusCode.BadRequest);
badResponse.ReasonPhrase = "include ex.StackTrace here just for debugging";
}
}
Then make a call inside a try-catch block to capture that extra error:
using (var client = new HttpClient())
{
// rest of your code goes here
try
{
var repsonse = client.PostAsJsonAsync(fooURL, leaveRequest).Result;
}
catch(HttpRequestException httpEx)
{
// determine error here by inspecting httpEx.Message
}
}
httpEx.Message can have this message:
Response status code does not indicate success: 500
({stack_trace_goes_here}).
I have a service method that very simply gets the information for all stores in the database. It maps the stores from EF using Auto Mapper, and returns a generic response of type StoreDTO (a simple POCO).
The problem is this: the method executes just fine, I step through all the way to the end. Every property in response has a value, nothing is null. The list is populated with items, the items in the list are valid, etc etc.
But the following code throws a NullReferenceException as soon as GetAllStores returns:
ListResponseDTO<StoreDTO> allStores = Services.Stores.Stores.GetAllStores();
EDIT: Here is a screenshot of the debugger, right when it is returning. You can see in the watch window that the values look kosher: http://i.imgur.com/rd853.png
Any help is greatly appreciated. Here is the code from the method:
public static ListResponseDTO<StoreDTO> GetAllStores()
{
ListResponseDTO<StoreDTO> response = new ListResponseDTO<StoreDTO>("Get Stores not successful");
try
{
response.Items = new List<StoreDTO>();
using (DomainEntities db = new DomainEntities(Global.ConnectionString))
{
foreach (var IndividualStore in db.Stores)
{
Mapper.CreateMap<Store, StoreDTO>();
var IndividualStoreDTO = Mapper.Map<Store, StoreDTO>(IndividualStore);
response.Items.Add(IndividualStoreDTO);
}
}
response.Message = "Store(s) retrieved successfully";
response.Success = true;
}
catch (Exception ex)
{
Logging.Log("Get All Stores", response.Message + " " + ex.ToString(), Logging.LogPriority.Error, "Store Operations");
}
return response;
}
Here is the generic DTO definition:
public class ListResponseDTO<DtoType> : ResponseDTO
{
public ListResponseDTO()
: base()
{
Items = new List<DtoType>();
}
public ListResponseDTO(string defaultMessage)
: base(defaultMessage)
{
Items = new List<DtoType>();
}
public List<DtoType> Items;
}
In case you were wondering, ResponseDTO has two properties:
bool Success
string Message
Here is the exception details, I'm afraid it's not too helpful:
System.NullReferenceException was unhandled by user code
Message=Object reference not set to an instance of an object.
Source=Infinity
StackTrace:
at PLM.Infinity.Default.GetDrawersForUser() in C:\Users\jlucas\Documents\Visual Studio 2010\PLM Source Control\Utilities\InfinityInterface\Infinity\Default.aspx.cs:line 96
InnerException:
Can you put a where clause so you return only stores that you are sure they have all fields, and see if the problem persists?
This some times happen because, somewhere in your data set, you have missing data and during debug you don't see it.
You could also put another try catch that boxes the Mapper call and see if something is happening there.
This are more suggestions then an answer.