I am having an issue using the SendMailAsync When I change it out for a normal Send it works fine. When I use SendMailAsync I get a connection has timed out and no mail is sent. Am I setting it up the wrong way?
edit:
1. I changed the variable name!!!
2.The exact error happens here await smtpClient.SendMailAsync(mailMessage); It just hangs until the connection times out, and I get an error description of "Connection has timed out" The status code returned is General Error
I'm calling the sendEmail from form the onClick event of a button for now. await unitOfWork.Instance.SendEmail(...) It's a single line of code in the onclick event
public async Task SendEmail(List<string> notificationList, string emailSubject, string emailMessage)
{
var message = new MailMessage()
{
IsBodyHtml = true,
BodyEncoding = System.Text.Encoding.UTF8,
Subject = emailSubject,
From = new MailAddress(FromEmailAddress),
Body = emailMessage
};
try
{
foreach (var emailAddress in notificationList)
{
message.To.Add(emailAddress);
}
var people = string.Join(Environment.NewLine, message.To);
MessageBox.Show(#"from: " + unitOfWork.CurrentSystemUser.Person.Email + "To: " + people);
await SendMessage(message);
}
catch (Exception E)
{
...
}
finally
{
...
}
}
private async Task SendMessage(MailMessage mailMessage)
{
if (!CanSendSmtpEmail) return;
await SendSmtpMail(mailMessage);
}
public async Task SendSmtpMail(MailMessage mailMessage)
{
SmtpClient smtpClient = new SmtpClient(SmtpClientHost, int.Parse(SmtpClientPort));
smtpClient.SendCompleted += (s, e) => {
smtpClient.Dispose();
mailMessage.Dispose();
};
await smtpClient.SendMailAsync(mailMessage);
//smtpClient.Send(mailMessage) works fine
}
Increasing SmtpClient.Timeout won't work because it only applies to the synchronous Send method (see here).
Your code is working fine for me when I test it using GMail (like explained here) Can you give it a try with GMail or a different SMTP server? I think maybe your SMTP server is "acting funny". Also maybe you can show us how are you calling SendEmail method ?
Side notes:
Your question is really interesting (at least to me) but because of the way you wrote your code (property has the same name as method) people have down-voted it (you should fix that).
Also, can I see the full exception you get? (btw: catch (Exception E) - not good, use lowercase "e" for the exception argument name).
Make sure you understand exactly how asynchronous it really is.
Related
I've encountered a few dificulties trying to handle some errors in the best way possible. One of my cases for example is NullReferenceException.
To be more clear let me explain it in a few words. When I make a call to a server to receive some pieces of information, in some cases the server might have some problems and it will return of course null.
What I've done is I display an alert to let the user know that he can try later again. After this I try to send him in the previous page for example. And after all this my app still crushes.
What I'd like to do is to simply display the alert and then let the user stay in the same page without the application crushing.
These are a few pieces of my code:
tasks.cs
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
await Application.Current.MainPage.Navigation.PopAsync(); //actually I would like to stay in the same page
}
}
return ideas;
}
view.xaml.cs
private async void Button_Clicked(object sender, EventArgs e)
{
Tasks ts = new Tasks();
var ideas = await ts.GetIdeasAsync();
if (ideas == null)
{
Debug.WriteLine("hello");
//do nothing since the display alert is already shown
}
else
{
//code here
}
I would really appreciate if anyone can guide me to a "best-practice" approach. Thanks :)
You are declaring ideas in the try block, and then trying to access it in the catch block, where it is out of scope. (Visual Studio should give an Intellisense error)
Also, whenever manipulating the UI, you should always do it on the main thread. so move your DisplayAlert() code into
Device.BeginInvokeOnMainThread(async () =>
{
// await DisplayAlert(); move it into here
});
In addition, any PopAsync or PushAsync calls should also be done on the main UI thread. But calling PopAsync after an asynchronous call to an API not be a good idea, as the user may have already pressed the back button by the time the call returns.
As for the NullReferenceException, check to see if json is null before passing it to the DeserializeObject() function.
The problem it was pretty obvious actually since I after catch-ing the exception I would later continue with the code. So what I did was :
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
//actually I would like to stay in the same page
return null; //-- added this line
}
}
return ideas;
}
Maybe it's not the best idea but it's working for me. Any other approach would be highly appreciated. :)
My Xamarin Android app utilizes a Web service, which it connects to using HttpClient. On no connection (for exmaple when the user has no cell nor WiFi connection), an excpetion is thrown. I'm using async/await to get the data from the server. Here is an excerpt from my code:
public async Task<String> doLogin(string username, string password)
{
String url = Constants.loginEndpoint + username + "/" + password + "/";
var uri = new Uri(string.Format(url, string.Empty));
return_string = "";
try
{
var response = await GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return_string = "success";
// Process the positive response here
else
{ }
}
catch (Exception ex)
{
throw new ConnectionException();
}
return return_string;
}
I defined a custon ConnectionException and want to show an AlertDialog to the user to inform them, that the request failed due to no connection. After the user clicks OK I want to close the app. I tried to show the alert dialog in the following way, but it's not working:
public class ConnectionException : Exception
{
public ConnectionException()
{
AlertDialog.Builder alert = new AlertDialog.Builder(myApp.Context);
alert.SetTitle("Failure");
alert.SetMessage("Request failed. No connection.");
alert.SetPositiveButton("OK", (senderAlert, args) =>
{
});
Dialog dialog = alert.Create();
dialog.Show();
}
public ConnectionException(string message)
: base(message)
{ }
public ConnectionException(string message, Exception innerException)
: base(message, innerException)
{ }
}
Is this the right approach? Probably not, as it's not working. I would appreciate any help on how to achieve this. Also, I've not given it too much thought, but is this a preferred way to handle such exceptions?
Assuming that your myApp.Context is an Activity and it has no back stack, you can just call Finish()
var context = myApp.Context; // this needs to be an Activity-based context...
context.RunOnUiThread(() =>
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed. No connection.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
context.Finish();
})
.Create();
alertDialog.Show();
});
Are you reusing this exception in several places, or is this a one off?
If your only using this exception once, there is no real reason to build your own.
You may as well just capture the exception and post your alert from inside your catch.
I know that's not as pretty of a way to write the catch, but if it works why not use it.
Side note:
DisplayAlert may be easier for you as well. It'll be a one liner.
Example:
await DisplayAlert("Failure","Request failed. No connection.", "Ok");
The way you are handling possible errors contains multiple issues and is not the right approach for several reasons.
First: Your code doesn't follow C-Sharp conventions and contains several code-smells. I show you a better and more accepted style.
1) Methods in C# normally starts with an uppercase letter. doLogin becomes Login
2) To create a new Uri instance you do not need to format your url-string. The string.Empty won't be used. So the code can be simplified into await GetAsync(new Uri(...));
3) The return_string seems not to be used in any way outside the method. It is string.Empty or "success". Why not switch it to bool? That way you can easily check if the login was successful. The return-type becomes bool instead of string.
The method looks now like this:
public async Task<bool> Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
throw new ConnectionException();
}
return false;
}
Second, as mentioned by #Jason, an exception should not contain any UI or business logic. Consider the following, which will break your current implementation.
public async Task<bool> Login(string username, string password)
{
var connectionEx = new ConnectionException();
try
{
...
}
catch (Exception ex)
{
throw connectionEx;
}
...
}
Now your user will see the exception even so there wasn't any.
The last thing is that I recommend not to catch the exception just to throw your custom exception. The reason is, that there might be other things that raise an exception too. For example something is null in the positive response handling.
Depending on how the Login method is used, for example directly in an Android Activity, I would do something like that:
public async Task Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
}
else
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}
catch (Exception ex)
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Something went wrong (" + ex.Message +")")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}
I have a fairly weird issue that I can't wrap my head around. So here is the code:
[HttpGet]
[AllowAnonymous]
public ActionResult SendEmail()
{
SendRegisterEmail("Test", "Testagain", "lastTest");
return View("Index");
}
public async Task<bool> SendRegisterEmail(string subject, string message, string to)
{
var email = new Email();
var mailAddress = new MailAddress(to);
email.Subject = subject;
email.Body = message;
var mailAddressCollection = new MailAddressCollection();
mailAddressCollection.Add(mailAddress);
email.To = (mailAddressCollection);
return await Send(email);
}
Now to prove my problem I broke down the code into single lines so I can see on which line it breaks. Here is what I found:
When I debug this and I step into the SendRegisterEmail method, the line that says var mailAddress = new MailAddress(to); gets run and then it exits out the function and runs the line return View("Index"); and the page loads up. The weird thing is I added a logging method on to Send at the very end and that logging never gets hit. I put a breakpoint on Send and it never got hit. It is as if creation of email crashed, decided to exit out to the caller function and continued with the code.
I don't have a faintest clue as to why.
SendRegisterEmail method is asynchronous, you're not awaiting for it.
Currently, you start a fire-and-forget operation and return View instruction is executed while you're creating a new MailAddress instance.
Most probably it throws FormatException which you can't catch because the exception is thrown in another thread.
update your action method to
public async Task <ActionResult> SendEmail()
{
await SendRegisterEmail("Test", "Testagain", "lastTest");
return View("Index");
}
Also awaiting before the return is not really needed, you can change it to
public Task<bool> SendRegisterEmail(string subject, string message, string to)
{
var email = new Email();
// ...
return Send(email);
}
but that's not related to the problem you've got.
I'm using C# for Visual studio, and i wanted to send an Email through my app, but i always get an error
here's my code
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate
{
var email = new Intent(Android.Content.Intent.ActionSend);
email.PutExtra(Android.Content.Intent.ExtraEmail, new string[] { "person1#gmail.com", "person2#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraCc, new string[] { "person3#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraSubject, "Hello Email");
email.PutExtra(Android.Content.Intent.ExtraText, "Hello user");
email.SetType("message/rfc822");
StartActivity(email);
};
and i always get this error
Android.Content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.SEND typ=message/rfc822 flg=0x1 (has clip) (has extras) }
can someone help me?
I means that it can't find an activity to handle the action SEND.
Are you running this on code in a simulator? Try it on a physical device (make sure it has a mail client installed).
Also, I would surround your code in a try/catch block, to avoid crashing if there's no activity available to handle your intent.
** Update **
Here's an example. Put the try/catch block inside your delegate.
try
{
var email = new Intent(Android.Content.Intent.ActionSend);
email.PutExtra(Android.Content.Intent.ExtraEmail, new string[] { "person1#gmail.com", "person2#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraCc, new string[] { "person3#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraSubject, "Hello Email");
email.PutExtra(Android.Content.Intent.ExtraText, "Hello user");
email.SetType("message/rfc822");
StartActivity(email);
}
catch (Exception ex)
{
// Either ignore or log the error.
}
I'm working on WinRT. If an unhandled exception is thrown I want to write the message text to the storage.
I added an Event handler in 'App.xaml.cs', see the code.
The exception is caught but the last line, where the file is written, crashes again -> 'exception'!
Why? Any idea?
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
this.UnhandledException += App_UnhandledException;
}
async void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file= await folder.CreateFileAsync("crash.log",CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(file, e.Message); // <----- crash again -----
}
Thanks
Sunny
I've been wondering the same thing and stumbled across this quite early on in my search. I've figured out a way, hopefully this will prove useful to someone else too.
The problem is that await is returning control of the UI thread and the app's crashing. You need a deferral but there's no real way to get one.
My solution is to use the settings storage, instead. I'm assuming most people wanting to do this want to do something LittleWatson style, so here's some code modified from http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on-windows-phone-7.aspx for your convenience:
namespace YourApp
{
using Windows.Storage;
using Windows.UI.Popups;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
public class LittleWatson
{
private const string settingname = "LittleWatsonDetails";
private const string email = "mailto:?to=you#example.com&subject=YourApp auto-generated problem report&body=";
private const string extra = "extra", message = "message", stacktrace = "stacktrace";
internal static void ReportException(Exception ex, string extraData)
{
ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;
exceptionValues[extra] = extraData;
exceptionValues[message] = ex.Message;
exceptionValues[stacktrace] = ex.StackTrace;
}
internal async static Task CheckForPreviousException()
{
var container = ApplicationData.Current.LocalSettings.Containers;
try
{
var exceptionValues = container[settingname].Values;
string extraData = exceptionValues[extra] as string;
string messageData = exceptionValues[message] as string;
string stacktraceData = exceptionValues[stacktrace] as string;
var sb = new StringBuilder();
sb.AppendLine(extraData);
sb.AppendLine(messageData);
sb.AppendLine(stacktraceData);
string contents = sb.ToString();
SafeDeleteLog();
if (stacktraceData != null && stacktraceData.Length > 0)
{
var dialog = new MessageDialog("A problem occured the last time you ran this application. Would you like to report it so that we can fix the error?", "Error Report")
{
CancelCommandIndex = 1,
DefaultCommandIndex = 0
};
dialog.Commands.Add(new UICommand("Send", async delegate
{
var mailToSend = email.ToString();
mailToSend += contents;
var mailto = new Uri(mailToSend);
await Windows.System.Launcher.LaunchUriAsync(mailto);
}));
dialog.Commands.Add(new UICommand("Cancel"));
await dialog.ShowAsync();
}
}
catch (KeyNotFoundException)
{
// KeyNotFoundException will fire if we've not ever had crash data. No worries!
}
}
private static void SafeDeleteLog()
{
ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;
exceptionValues[extra] = string.Empty;
exceptionValues[message] = string.Empty;
exceptionValues[stacktrace] = string.Empty;
}
}
}
To implement it, you need to do the same as the link above says, but to ensure the data's here in case the url ever goes down:
App.xaml.cs Constructor (BEFORE the call to this.InitializeComponent()):
this.UnhandledException += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
Obviously if you already have an UnhandledException method you can throw the call to LittleWatson in there.
If you're on Windows 8.1, you can add a NavigationFailed call too. This needs to be in an actual page (typically MainPage.xaml.cs or whatever page is first opened):
xx.xaml.cs Constructor (any given page):
rootFrame.NavigationFailed += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
Lastly, you need to ask the user if they want to send the e-mail when the app re-opens. In your app's default Page's constructor (default: the page App.xaml.cs initializes):
this.Loaded += async (s, e) => await LittleWatson.CheckForPreviousException();
Or add the call to your OnLoad method if you already use it.
In this situation, await could be loosely translated to "do this job on another thread, and continue what you were doing while you wait for it to finish". Given that what your app was doing was crashing, you probably don't want it to continue doing that until you're done logging the problem. I'd suggest running your file IO synchronously in this case.
This may come a bit too late for the original question but...
as #Hans Passant suggested, avoiding await (i.e., running the FileIO.AppendTextAsync() synchronously), also seconded by #Jon, I would opt for this rather than the relatively too heavy code for LittleWatson. As the app is in some error handing state anyway (this should be a rare occurrence) I wouldn't put any blocking arising from synchronous (due to removing await) as a major downside.
Leaving the synchronous option to one side, the following await implementation worked for me:
Change await FileIO.AppendTextAsync(file, e.Message); to:
Task task = LogErrorMessage(file, e.Message)
task.Wait(2000); // adjust the ms value as appropriate
...
private async Task LogErrorMessage(StorageFile file, string errorMessage)
{
await FileIO.AppendTextAsync(file, errorMessage); // this shouldn't crash in App_UnhandledException as it did before
}