I am integrating with MS Dynamics GP WebServices from C# and I am not sure how to handle exception.
If I do a GetCustomer with a inexistant ID, the web services return me a "generic" SoapException and the message is "Business object not found." So the only way I see to be sure it's an invalid ID and not any other error, is by parsing the error message, I find this solution extremely fragile. My GP version is English, on customer site it's gonna be french and I have no idea in which language web services message gonna be. I am thinking about catching it, parsing the message and throw a more meaningful error type.
Do you see a better option ?
Unfortunately both the eConnect API and the GP Web Services both return generic errors, just be glad you don't have to parse the eConnect ones.
Good things is, the errors are generally static, so you can build parsers for them. Creating custom exceptions is definitely a good way to do it with this type of web service.
I have a blog post that details how I overcame this question in WCF (though as you can see, I don't mind parsing the error message to get the details). Here's the meat of it:
catch (FaultException soapEx)
{
MessageFault mf = soapEx.CreateMessageFault();
if (mf.HasDetail)
{
XmlDictionaryReader reader = mf.GetReaderAtDetailContents();
Guid g = reader.ReadContentAsGuid();
}
}
Once you have the GUID you can use it to query the GP Web Service for the details of the error.
Are you in control over the WebService code?
In that case I would return SoapExceptions with simple error codes that are easier to parse and let the client application decide what message to display based and the parsed error code.
You could use an "Error codes" enum on the WebService to make the code more readable.
//Example
enum ErrorCodes
{
BusinessObjectNotFound = 1000,
AnotherPossibleError = 1002
}
try
{
//Code
}
Catch(BusinessObjectNotFoundException bex)
{
throw new SoapException(ErrorCodes.BusinessObjectNotFound);
//Or maybe...
//throw new SoapException(((int)ErrorCodes.BusinessObjectNotFound).ToString());
}
For information to people interested in the topics, Jacob Proffitt response look like the way to go. here a snipper from Dynamics GP documentation:
catch(SoapException soapErr)
{
// If a validation exception occurred, the logid will be in a child node
if(soapErr.Detail.HasChildNodes == true)
{
// Create a guid for the logid value in the soap exception
Guid guid = new Guid(soapErr.Detail.InnerText);
// Get the validation result object
validationResult = wsDynamicsGP.GetLoggedValidationResultByKey(guid, context);
// Display the number of validation exceptions
MessageBox.Show("Number of validation exceptions: " +
validationResult.Errors.Length.ToString());
}
}
But in the case I cited : GetCustomer with an unexisting ID, the line "soapErr.Detail.HasChildNodes" is false so it fails.
The webservices seem full of funny behavior, this will take longer than I expected :(.
I'm beginning to loathe GP. This may be "bad-form" but here's what I did:
try
{
// query service for object by key
}
catch (System.ServiceModel.FaultException e)
{
if (e.Message == "Business object not found.")
{
// create new object
}
else
{
// log the exception appropriately
}
}
Related
I'm trying to import a bunch of data from an Excel to a DB in my Web API, this data is being validate using FluentValidation, the problem is, every time I hit a line with bad information from the Excel table my code stops running and the API returns the exception.
I wish I could store all these exceptions, keep my code running until the end of the Excel table, and then after that return all the exceptions as my API response. I also think it would be a good idea to return in which line of the table the exception occurred.
My code is running inside a for each statement so for storing the line in which errors occurred I can simply start a counter inside of it.
As for keeping my code running I can run it inside of a Try-Catch (or would there be a better way?), but inside of it how can I store all the exceptions together to then return them later?
Most .NET parts that can return multiple exceptions use AggregateException to achieve that. A generic template for that would be:
var exceptions = new List<Exception>();
foreach (var w in work)
{
try
{
DoWork(w);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
Here's the docs for AggregateException.
the problem is, every time I hit a line with bad information from the excel table my code stops running and the API returns the exception.
Going on with a process after an exception has never been a good idea. At best the thing you are working with is now in a invalid state and will throw more exceptions "InvalidState" every further access attempt. At worst, it is utterly broken and will cause utterly unpredictbale behavior because it asumes you would not do something so bad. Such behavior can go all the way into your process being kicked out by windows, due to Memory Access Violations.
There is basic classification I use for Exceptions. While this exception is at worst a Exogenous Exception for your code, it should be considered a fatal one for the parser and this Documet. Going on after it, is not going to add any value. At best, it adds more useless error messages wich hide the real error.
90% of the times you get a compiler error, you get a message because the Syntax is wrong. And often it is so wrong, the Source Code parser can not make heads or tails of what it is seeing anymore. It stop being able to tell you where the issue even is.
You should expose/log each exception, stop the parsing after the first one and have the user deal with the issue (wich is clearly outside your job).
You can try to add an extra column Status in your excel table , for example you can adapt your logic and store in the excel table which stores into DB.
class Program
{
static void Main(string[] args)
{
Customer Cust = new Customer();
List<Customer> Customers = Cust.GetCustomers();
foreach(Customer c in Customers)
{
Console.WriteLine(c.Name);
}
Console.Read();
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
List<Customer> Customers = new List<Customer>();
public List<Customer> GetCustomers()
{
//Your Foreach Logic will be replaced
for(int i=0;i<3;i++)
{
if (i % 2 == 0)
{
Customers.Add(new Customer { Id = i, Name = "Name" + i, Status = "Success Message" });
}
else
{
Customers.Add(new Customer { Id = i, Name = "Name" + i, Status = "Error Message" });
}
}
return Customers;
}
}
I'm trying to reconcile a few opinions I've found out in the wild. One source says that in C# Request.UrlReferrer will throw a System.UriFormatException if the HTTP header is malformed. The other says that Request.UrlReferrer actually doesn't throw an exception in this case, it just returns null.
That said, I'm developing a webapp that will capture the Referral Url for future reference. I'm checking it for null so that the "not set to instance of an object" error does not burst forth when converting to a string, etc., and that tests out fine. I am also wondering whether I should include error handling for potential System.UriFormatExceptions.
Has anyone run into this? Thank you!
EDIT: Given NightOwl888's answer below, it does appear that exceptions can occur. How often, I'm not sure, but I'd rather protect against it than risk exceptions on a public site.
I am only grabbing the referralUrl for logging, so it won't get used in any downstream application context. It's just getting grannGiven that, I'm guessing that the following code should cover me:
Uri referrer = null;
try
{
referrer = Request.UrlReferrer;
}
catch (UriFormatException)
{
referrer = null;
}
catch (Exception)
{
referrer = null;
}
var referralUrl = (referrer != null) ? referrer.ToString() : "None Found";
EDIT: Changed exceptions and added general catch
Checking the source of System.Web.HttpContext.Current.Request.UrlReferrer shows the following.
public Uri UrlReferrer
{
get
{
if ((this._referrer == null) && (this._wr != null))
{
string knownRequestHeader = this._wr.GetKnownRequestHeader(0x24);
if (!string.IsNullOrEmpty(knownRequestHeader))
{
try
{
if (knownRequestHeader.IndexOf("://", StringComparison.Ordinal) >= 0)
{
this._referrer = new Uri(knownRequestHeader);
}
else
{
this._referrer = new Uri(this.Url, knownRequestHeader);
}
}
catch (HttpException)
{
this._referrer = null;
}
}
}
return this._referrer;
}
}
So, indeed it is possible that a UriFormatException can occur if the URL is malformed, since HttpException is the only type that is set to null and UriFormatException does not inherit from HttpException.
A better option may be to use Request.Headers["Referer"], which will return the raw string as pointed out here. But do note that the header can contain any value (including values that are not URLs).
IMO, the Referer header should only be used for logging purposes because it is not reliable enough to use for implementing any kind of application functionality (unless there is some kind of backup functionality in the case it is missing or malformed). The browser can (and often does) neglect to send it in certain cases.
I have this WCF Service Application which contains 10-15 services. the services generally serve the same purpose but have different implementations. There was this one really simple method which was part of the soap service. The method basically looked like this
public Data GetData(string param1, string param2, string checksum)
{
try
{
if (Utilities.StringsAreEmpty(param1, param2, checksum)
{
throw new FaultException<ServiceFault>(){ ErrorCode = 1 };
}
var caller = Repository.GetProviders(param1);
if (caller == null)
{
throw new FaultException<ServiceFault>(){ ErrorCode = 2 };
}
var realChecksum = Utilities.CalculateSha256Hash(string.Format("{0}{1}{2}", param1, param2, caller.Key));
if (realChecksum != checksum)
{
throw new FaultException<ServiceFault>(){ ErrorCode = 3 };
}
var data = Repository.GetData(param2);
return data;
}
catch (Exception ex)
{
LogException(ex);
throw new FaultException<ServiceFault>(){ ErrorCode = 99 };
}
}
The method shown above worked perfectly, just as expected. after some time I had to modify some other service and after doing that and publishing the changes to the server (all those services are build into one .dll assembly So they cannot be deployed partially) this particular method started to behave really weirdly. I was not seeing any errors in Log and the method itself did not return anything at all. now first thing I did was removed try catch block, (I had this kind of problem before and removing try catch helped), and magically everything started working again. Now, I don't really see any problem in here and since I had this kind of issue for the second time now I am really concerned about it. Can somebody explain why does try catch removal work here ? first time I had this problem the internal server error occurred and the response from the server was absolutely nothing. not even an error HTML (returned by wcf) or anything like that. is this some kind of a bug with WCF ? or is it supposed to work this way ? if so, how can that be avoided ?
How do you catch exceptions from a Web Service that is returning a custom object?
I've seen this post but it doesn't seem to show how to get the exception that was thrown by the service.
I can pull the SOAP Exception, but I want to be able to get the original exception that the web service returned. I've looked at the variables that are set at this time and can't seem to see the exception anywhere, I just see:
"Server was unable to process request. ---> Exception of type
'RestoreCommon.ConsignmentNotFoundException' was thrown."
try
{
Consignment cons = WebServiceRequest.Instance.Service
.getConsignmentDetails(txtConsignmentNumber.Text);
lblReceiverName.Text = cons.Receiver.Name;
}
catch (ConsignmentNotFoundException)
{
MessageBox.Show("Consignment could not be found!");
}
Is this possible?
In short, no.
Web services will always throw SOAP fault. In your code,
MessageBox meant to be used in Windows forms and nowhere else.
You can throw this exception and in the client application, you will have to handle a SOAP fault.
Edit: If you do not want to send exceptions across to the client, this what you could do:
class BaseResponse
{
public bool HasErrors
{
get;
set;
}
public Collection<String> Errors
{
get;
set;
}
}
Each WebMethod response must inherit from this class. Now, this is how your WebMethod blocks would look like:
public ConcreteResponse SomeWebMethod()
{
ConcreteResponse response = new ConcreteResponse();
try
{
// Processing here
}
catch (Exception exception)
{
// Log the actual exception details somewhere
// Replace the exception with user friendly message
response.HasErrors = true;
response.Errors = new Collection<string>();
response.Errors[0] = exception.Message;
}
finally
{
// Clean ups here
}
return response;
}
This is just an example. You may need to write proper exception handling code rather than simply using generic catch block.
Note: This will take care of exceptions occurring in your application only. Any exceptions occurring during communication between client and service, will still be thrown to the client application.
I am using ELMAH to log unhandled exceptions in an ASP.NET Webforms application. Logging is working fine.
I want to pass the ELMAH error log id to a custom error page that will give the user the ability to email an administrator about the error. I have followed the advice from this answer. Here is my global.asax code:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
Session[StateKeys.ElmahLogId] = args.Entry.Id;
// this doesn't work either:
// HttpContext.Current.Items[StateKeys.ElmahLogId] = args.Entry.Id;
}
But, on the Custom error page, the session variable reference and HttpContext.Current.Items are giving me a NullReference exception. How can I pass the ID to my custom error page?
This works for me:
void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args)
{
if (args.Entry.Error.Exception is HandledElmahException)
return;
var config = WebConfigurationManager.OpenWebConfiguration("~");
var customErrorsSection = (CustomErrorsSection)config.GetSection("system.web/customErrors");
if (customErrorsSection != null)
{
switch (customErrorsSection.Mode)
{
case CustomErrorsMode.Off:
break;
case CustomErrorsMode.On:
FriendlyErrorTransfer(args.Entry.Id, customErrorsSection.DefaultRedirect);
break;
case CustomErrorsMode.RemoteOnly:
if (!HttpContext.Current.Request.IsLocal)
FriendlyErrorTransfer(args.Entry.Id, customErrorsSection.DefaultRedirect);
break;
default:
break;
}
}
}
void FriendlyErrorTransfer(string emlahId, string url)
{
Server.Transfer(String.Format("{0}?id={1}", url, Server.UrlEncode(emlahId)));
}
Unable to comment on Ronnie's solution. I had that in place for a while but it breaks the standard error flow process and causes ErrorLog_Logged to always transfer, even when calling
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
This is a problem if you still want to log an error from within a catch statement, for instance you have a workaround for an error but want to log the error to perform a proper fix, which can be pretty helpful on difficult to replicate issues.
I was able to correct this by using the following change:
//if (customErrorsSection != null)
if (customErrorsSection != null && this.Context.Error != null)
This respects the typical error handling properly, as the context.Error will be null in cases where you explicitely raise the exception in Elmah, but is not null when falling through default error handling (not via a catch or if caught and re-thrown). This causes Ronnie's solution to respond similar to the .Net error handling logic.