How should I handle navigation errors if they occur in the OnNavigatedTo method. For example, a query string parameter is not passed or throws an exception while parsing to an integer.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string itemIdParam;
if (NavigationContext.QueryString.TryGetValue("itemId", out itemIdParam))
{
int itemId = int.Parse(NavigationContext.QueryString["itemId"]);
_item = App.MainViewModel.Items.Where(i => i.ItemId == itemId).First();
DataContext = _item;
}
}
Should I catch them and display a MessageBox or let them
Try like this:
if( this.NavigationContext.QueryString.ContainsKey("itemId"))
{
string s_itemid = this.NavigationContext.QueryString["itemId"];
int i_itemid;
bool result = Int32.TryParse(s_itemid, out i_itemid);
if(result)
//parsing success
else
//parsing error
}
else
//parameter doesn't exist
More information about TryParse: LINK
Another thing you can do to reduce the error is to define your navigation uri.
For example in Details.xaml page you can create a static method called GetUri() with parameters of wanted type.
public static Uri GetUri(int itemId){
return new Uri(string.Format("/Details.xaml?itemId={0}", itemId), UriKind.Relative);
}
When you want to navigate to Details page just write:
NavigationService.Navigate(Details.GetUri(2));
Related
I'm writing a code to create a new object with key metadata being provided by a user input form. I'm wondering if there's a way to return the object (q) in the try block and not at the end of the method?
Here's my current code with some notes about how I want it all to look:
public NewSearchQuery GetEntry()
{
//pull all input field information and store ready for validation
string name = Convert.ToString(Companies.SelectedItem);
string location = String.Concat(Convert.ToString(Property.Text), " ", Convert.ToString(SearchLocation.Text).ToLower());
string searchtype = Convert.ToString(Search.SelectedItem);
var q = new NewSearchQuery();
//check all required input fields are filled in
if (String.IsNullOrEmpty(name) || String.IsNullOrEmpty(location) || String.IsNullOrEmpty(searchtype))
{
MessageBox.Show("Please ensure you have filled in all the required fields (*) before proceeding", "Insufficient Information");
this.ShowDialog();
}
else
{
try
{
q.GetFormData(name, location, searchtype, Paid.Checked); //replace this with a constructor for var q
q.Contract = ThisAddIn.GetContract(q.Name);
q.CreateIdNum();
q.CreateFilePath(q.Contract, q.RefNum);
q.CalculateFees();
}
catch (Exception d)
{
MessageBox.Show(Convert.ToString(d)); //return null if the try fails
}
}
return q; //relocate this to the try block
}
I want to make these changes because I suspect that returning the q value irrespective of the process working or not is causing my winform to error out it if try to exit it prematurely.
Is there a way I can get around the inevitable 'not all code paths return a value' error?
You can rewrite your method as follows:
public NewSearchQuery GetEntry()
{
//pull all input field information and store ready for validation
string name = Convert.ToString(Companies.SelectedItem);
string location = String.Concat(Convert.ToString(Property.Text), " ", Convert.ToString(SearchLocation.Text).ToLower());
string searchtype = Convert.ToString(Search.SelectedItem);
//check all required input fields are filled in
if (String.IsNullOrEmpty(name) || String.IsNullOrEmpty(location) || String.IsNullOrEmpty(searchtype))
{
MessageBox.Show("Please ensure you have filled in all the required fields (*) before proceeding", "Insufficient Information");
this.ShowDialog();
return null;
}
try
{
var q = new NewSearchQuery();
q.GetFormData(name, location, searchtype, Paid.Checked); //replace this with a constructor for var q
q.Contract = ThisAddIn.GetContract(q.Name);
q.CreateIdNum();
q.CreateFilePath(q.Contract, q.RefNum);
q.CalculateFees();
return q;
}
catch (Exception d)
{
MessageBox.Show(Convert.ToString(d)); //return null if the try fails
return null;
}
}
Now all code paths return a value. I omitted the else block, because if you leave the method inside the if block. This means, that the code following the if block is never executed when your condition is true, as it would be with the else block. The advantage of this is that you don't have so much nested bracings, which makes the code easier to understand.
Be sure to check whether the return value is not null, otherwise you might have a NullReferenceException.
Yes,
Write a return statement within every single block.
if(something)
{
return value;
}
else
{
try
{
return value;
}
catch
{
return value;
}
}
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
I have an SSIS package which calls a Data Flow Task as part of a loop which iterates different end-point addresses (out of scope).
The Data Flow Task has a source Script Component responsible for calling a REST API and creating a row for each result.
There are 3 output buffers;
1. actual data row
2. error row
3. monitoring
The monitoring buffer used for telemetry and is populated through an event (EventHander) that is fired every time the API makes a request.
During the first iteration of the ForEach int the Control Flow loop, everything runs as expected, all the buffers produce the correct rows.
However, during the next iterations, the monitoring buffer which is populated within the event throws;
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.HandleUserException(Exception e)
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PrimeOutput(Int32 outputs, Int32[] outputIDs, PipelineBuffer[] buffers)
at Microsoft.SqlServer.Dts.Pipeline.ManagedComponentHost.HostPrimeOutput(IDTSManagedComponentWrapper100 wrapper, Int32 outputs, Int32[] outputIDs, IDTSBuffer100[] buffers, IntPtr ppBufferWirePacket)
I don't understand why the MonitoringBuffer is not initialised in the proceeding iterations.
The exception occurs while calling MonitoringBuffer.AddRow();.
Here's the whole Script Component simplified for readability:
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
private string ClientCode { get { return Variables.ErplyClientCode; } }
private string Username { get { return Variables.ErplyUsername; } }
private string Password { get { return Variables.ErplyPassword; } }
private bool IsTest { get { return Variables.IsTest; } }
private int ErplyRecordsPerPage { get { return Variables.ErplyRecordsPerPage; } }
private string ErplyDebugOutputPath { get { return Variables.ErplyDebugOutputPath; } }
private DateTime ChangeSince { get { return Variables.ChangeSince; } }
private int records { get; set; }
private int errors { get; set; }
private string rawFolder { get; set; }
public override void PreExecute()
{
base.PreExecute();
}
public override void PostExecute()
{
base.PostExecute();
}
public override void CreateNewOutputRows()
{
ErplyAPI.OnPreRequestEvent += new EventHandler<EAPIEvent>(ErplyAPI_OnPreRequestEvent);
var staff = ErplyAPI.getStaff(ClientCode, Username, Password, ChangeSince, ErplyRecordsPerPage, IsTest);
foreach (var p in staff.List)
{
try
{
if (!p.IsError)
{
EmployeeBuffer.AddRow();
EmployeeBuffer.employeeID = p.employeeID;
}
else
{
ErrorBuffer.AddRow();
ErrorBuffer.employeeID = p.employeeID;
ErrorBuffer.Error = p.Error.Message.Trim() + "\n" + p.Error.StackTrace;
errors++;
}
records++;
}
catch (Exception ex)
{
this.ComponentMetaData.FireWarning(0, "Script", ex.Message + "\n" + ex.StackTrace, string.Empty, 0);
}
}
EmployeeBuffer.SetEndOfRowset();
ErrorBuffer.SetEndOfRowset();
}
private void ErplyAPI_OnPreRequestEvent(object sender, EAPIEvent e)
{
var request = string.Empty;
var sessionKey = string.Empty;
bool fireAgain = true;
if (e == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
if (e.eAPI == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent.eAPI is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
try
{
if (e.Parameters != null && e.Parameters.ContainsKey("request"))
request = e.Parameters["request"].ToString();
if (request != "verifyUser" && e.Parameters != null && e.Parameters.ContainsKey("sessionKey"))
sessionKey = e.Parameters["sessionKey"].ToString();
}
catch (Exception ex)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("Error occurred assigning variables from EAPIEvent parameters in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace), string.Empty, 0);
}
try
{
MonitoringBuffer.AddRow(); // Exception occurs here
MonitoringBuffer.Request = ResizeString(request, 255);
MonitoringBuffer.SessionKey = ResizeString(sessionKey, 128);
}
catch (Exception ex)
{
var message = string.Format("Error occurred outputting Erply request in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace);
MonitoringBuffer.ErrorMessage = ResizeString(message, 8000);
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", message, string.Empty, 0);
}
finally
{
MonitoringBuffer.EndOfRowset();
}
}
}
I sorted the problem out.
The exception was being raised when the variable dispenser was being accessed from the Event. For some reason the GetValueWithContext(ScriptComponent.EvaluatorContext) is being dropped during the second call. Why this happens is beyond me.
The solution is simple, assign the variables from the variables dispenser to a local property or variable in the OnPreExecute function.
It's also good practice to not call the variable dispenser in the CreateNewOutputRows as it cause variable locking.
I ran into this issue too, but my solution was a little different -- moving the variable assignments into PreExecute() didn't help.
Instead, what I'd done is that I wanted to parse three different files, and read each of them with a Script Component. Their columns were kinda similar, so I created one Data Flow task, made sure it worked, then copied it and modified each copy to reflect the differences in the files. Running each individual Data Flow task was successful, but when I tried to run two of them, one after the other in a loop, I got a NullReferenceException from HostPrimeOutput() after calling the OutputBuffer.AddRow() method in my Script Component.
It turns out that when I copied each Data Flow task, the Script Components all kept the same namespace, and I guess it doesn't like that. So, I created brand new Script Components, set up all the output columns again (ugh!), copied the body of the script over, and it's happy.
i have the following code that gets an error message.i want to pass it into a string before an exception is thrown,this is my code
ValidateError(authDeserialized, "Succeed", "error", "failed"); //the validateError is a function as indicated below
Model.Response= authResponse.Content;
protected static void ValidateError(dynamic response, string validStatus,string categoryMatch, string message)
{
if (response.result.status != validStatus)
{
try
{
var category = response.result.category;
if (category == categoryMatch)
message=ErrorCodes.MessageFor(code,description);
//so i get the message back fine here but now how do i pass it back to this line Model.Response= authResponse.Content; so that it can get saved?
}
catch (Exception) { }
throw new Exception(message ?? "Request was not successfull");
}
}
As you are already sending message to the ValidateError() method, pass that parameter as a out parameter, it will update value of message if you assign new value to it then it will update message and will be accessible to outside environment.
string failureMessage = "failed";
ValidateError(authDeserialized, "Succeed", "error", out failureMessage);
//^^^ This is what you have to change
//Now you can assign failureMessage to any other value
Model.Response= authResponse.Content;
protected static void ValidateError(dynamic response, string validStatus,string categoryMatch, out string message)
{ //^^^ This is what you have to change
if (response.result.status != validStatus)
{
try
{
var category = response.result.category;
if (category == categoryMatch)
message=ErrorCodes.MessageFor(code,description); //so i get the message back fine here but now how do i pass it back to this line Model.Response= authResponse.Content; so that it can get saved?
}
catch (Exception) { }
throw new Exception(message ?? "Request was not successfull");
}
}
In this way you can assign value to failure message before throwing an error.
Try out online
I am trying to load many pages using the AngleSharp. The idea is that it loads a page, and if this page has a link to the next, loads the next page and so forth, the methods are described like bellow. But I am getting the inner exception:
Specified argument was out of the range of valid values.
Parameter name: index"
I believe is something related with Thread and syncrhronization.
public static bool ContainsNextPage(IDocument document)
{
String href = document.QuerySelectorAll(".prevnext a")[0].GetAttribute("href");
if (href == String.Empty)
return false;
else
return true;
}
public static string GetNextPageUrl(IDocument document)
{
return document.QuerySelectorAll(".prevnext a")[0].GetAttribute("href");
}
public static async Task<IDocument> ParseUrlSynch(string Url)
{
var config = new Configuration().WithDefaultLoader();
IDocument document = await BrowsingContext.New(config).OpenAsync(Url);
return document;
}
public static async Task<ConcurrentBag<IDocument>> GetAllPagesDOMs(IDocument initialDocument)
{
ConcurrentBag< IDocument> AllPagesDOM = new ConcurrentBag< IDocument>();
IDocument nextPageDOM;
IDocument currentDocument = initialDocument;
if (initialDocument != null)
{
AllPagesDOM.Add(initialDocument);
}
while (ContainsNextPage(currentDocument))
{
String nextPageUrl = GetNextPageUrl(currentDocument);
nextPageDOM = ParseUrlSynch(nextPageUrl).Result;
if (nextPageDOM != null)
AllPagesDOM.Add(nextPageDOM);
currentDocument = nextPageDOM;
}
return AllPagesDOM;
}
static void Main(string[] args)
{
List<IDocument> allPageDOMs = new List<IDocument>();
IDocument initialDocument = ParseUrlSynch(InitialUrl).Result;
List<String> urls = new List<string>();
List<Subject> subjects = new List<Subject>();
IHtmlCollection<IElement> subjectAnchors = initialDocument.QuerySelectorAll(".course_title a");
String[] TitleAndCode;
String Title;
String Code;
String Description;
IDocument currentDocument = initialDocument;
ConcurrentBag<IDocument> documents =
GetAllPagesDOMs(initialDocument).Result; //Exception in here
...
}
Error message is caused by this code:
document.QuerySelectorAll(".prevnext a")[0]
One of your documents doesn't have any anchors inside prevnext. Maybe it's first page, maybe the last, either way you need to check the array for it's length.
Also blocking call on async method is a bad practice and should be avoided. You'll get the deadlock in any UI app. The only reason you don't get it now is that you're in console app.
Your instincts are correct, if you are using this from an application with a non-default SynchronizationContext such as WPF, Win Forms, or ASP.NET then you will have a deadlock because you are synchronously blocking on an async Task returning function (this is bad and should be avoided). When the first await is reaching inside of the blocking call, it will try to post the continuation to the current SyncronizationContext, which will be already locked by the blocking call (if you use .ConfigureAwait(false) you avoid this, but that is a hack in this case).
A quick fix would be to use async all the way through by changing:
nextPageDOM = ParseUrlSynch(nextPageUrl).Result;
with:
nextPageDOM = await ParseUrlSynch(nextPageUrl);
After you get stung by this a few times, you'll learn to have alarm bells go off in your head every time you block an asynchronous method.
I have created an application that update the logs form whenever the email are successfully send. My code is something like this:
mailSender.cs
void Serche()
{
{
//perform thread background ip scanner
}
if (InvokeRequired){
this.Invoke(new MethodInvoker(delegate
{
sendReport();
}));
}
}
public void sendReport()
{
//some codes to trigger time schedule to send report
ExportToExcel(filePath);
int milliseconds = 2000;
Thread.Sleep(milliseconds);
sendMail(filename);
}
private void sendMail(string filename)
{
string getFilePath = #"D:\Report\" + filename;
string status = "send";
try
{
// send email filename as attachment
}
catch (Exception ex)
{
status = "Fail";
}
sendMailReport(filename, DateTime.Now, mailStat);
}
private void sendMailReport(string fileName, DateTime dateDelivered, string status)
{
//mailLog updateLogs = new mailLog();
updateLogs.updateMailLogs(fileName,dateDelivered,status);
}
mailLog.cs
public void updateMailLogs(string _fileName, DateTime _dateDelivered, string _status)
{
int num = dataGridView1.Rows.Add();
dataGridView1.Rows[num].Cells[0].Value = _fileName;
dataGridView1.Rows[num].Cells[1].Value = _dateDelivered;
dataGridView1.Rows[num].Cells[2].Value = _status;
dataGridView1.Refresh();
}
I have debug the code line by line and found out that all parameters successfully retrieve in my updateMailLogs method, but not sure why it didnt update my datagridview. Does anyone have any idea why? Please advise.
SOLVED
credit to #shell who enlightened me the answer to this question.
problem:-
1- If the form is already open, then I cannot create another object of mailLog form and call updateMailLogs method.
2- This will not update your grid data. Because the both object reference are different.
solution:-
1- Need to call that method from object of mailLog form which is currently loaded.
private void sendMailReport(string fileName, DateTime dateDelivered,string status)
{
if (Application.OpenForms["mailLog"] != null)
((mailLog)Application.OpenForms["mailLog"]).updateLogs.updateMailLogs(fileName,dateDelivered,status);
}
Provided code does not help to understand what you have done exactly. I mean you are executing method sendMailReport. That method will creates object of mailLog class on every execution. this may lost your existing data. It is better to create your mailLog class object out side of your sendMailReport method block and just execute updateMailLogs method only.
mailLog updateLogs = new mailLog();
private void sendMailReport(string fileName, DateTime dateDelivered,string status)
{
updateLogs.updateMailLogs(fileName,dateDelivered,status);
}
EDITED:
if the form is already loaded then you should call method like this. here, you don't need to create a new object of mailLog class.
private void sendMailReport(string fileName, DateTime dateDelivered,string status)
{
((mailLog)Application.OpenForms["mailLog"]).updateMailLogs(fileName,dateDelivered,status);
}
Usually this happens coz of CrossThread exception, so i guess u need to add try catch to check out, if tho u will need to invoke the grid
Edit: just noticed you asked where to put try catch
you can put it at any of your both voids , try this
try{
updateLogs.updateMailLogs(fileName,dateDelivered,status);
}
catch (Exception ex) {MessageBox.Show(ex.ToString());}