Sending an async email without await from a .Net core web service - entity-framework

I have a webservice .Net core2 that has certain methods that send an email. I have it working fine using smtpclient.sendemailasync.
public async Task<bool> SendEmailAsync(MailMessage email)
{
try
{
if (string.IsNullOrWhiteSpace(emailFrom)) email.From = new MailAddress(emailFrom);
using (SmtpClient client = getSMTPClientInstance())
{
await client.SendMailAsync(email);
}
return true;
}
catch (Exception ex)
{
Log.Error(ex, "Error sending email in EmailService.SendEmailAsync");
return false;
}
}
The only issue is that some SMTP servers take a little too long to respond. I want to set up the email, queue it and return without waiting for the result.
Just using an unawaited async is out for 2 reasons;
It is not reliable to continue a method outside a request context in asp
I need access to the database context of my entity framework to write a log
I have to allow for external or internal SMTP (my client specifies), so a collection folder is not a possibility - at least not without a service that manages it.
How could I achieve this? Do I need to write a service that manages this? If so, how would I do that inside my .Net Core App, keeping in mind that the service also needs to access the EF context to write a log
UPDATE
There is plumbing available in .NetCore DI especially for this. Refer to my additional answer below. Use IServiceScopeFactory

You can call the RegisterAsyncTask method on the Page object. That will signal the ASP.NET runtime you want to make sure these are finished before terminating the request context:
Example:
public void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}
public async Task LoadSomeData()
{
var clientcontacts = Client.DownloadStringTaskAsync("api/contacts");
var clienttemperature = Client.DownloadStringTaskAsync("api/temperature");
var clientlocation = Client.DownloadStringTaskAsync("api/location");
await Task.WhenAll(clientcontacts, clienttemperature, clientlocation);
var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Contact>>(await clientcontacts);
var location = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clientlocation);
var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clienttemperature);
listcontacts.DataSource = contacts;
listcontacts.DataBind();
Temparature.Text = temperature;
Location.Text = location;
}
https://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx

So, while I have marked an answer, there are a couple of options that are better solutions for my specific example. First is the option to use a library like hangfire to schedule tasks - although that is not technically an answer to the question.
The better solution in .net core is to use IServiceScopeFactory
With IServiceScopeFactory you can rescope a task so it doesnt go out of scope when the request is complete. I did the following directly in a controller (I later moved to using the hangfire approach, but this works). As you can see, the async task is fired off in a new unawaited thread while the controller code continues.
var task = Task.Run(async () =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<ApprovalService>();
await service.sendResponseEmailAsync(approvalInfo.ApprovalId, userID, approvalInfo.emailTo, approvalInfo.ccTo);
}
});

Related

How to make a rest api call in microsoft bot framework

I need a bot that takes users input, uses it as an id to some third party rest api call and posts back a response. I've looked through Microsoft documentation but didn't find any examples on how to program that request-response process.
Any examples or useful links would be appreciated
Adding to Jason's answer, since you wanted to make a REST api call, take a look at this code :
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
// User message
string userMessage = activity.Text;
try
{
using (HttpClient client = new HttpClient())
{
//Assuming that the api takes the user message as a query paramater
string RequestURI = "YOUR_THIRD_PARTY_REST_API_URL?query=" + userMessage ;
HttpResponseMessage responsemMsg = await client.GetAsync(RequestURI);
if (responsemMsg.IsSuccessStatusCode)
{
var apiResponse = await responsemMsg.Content.ReadAsStringAsync();
//Post the API response to bot again
await context.PostAsync($"Response is {apiResponse}");
}
}
}
catch (Exception ex)
{
}
context.Wait(MessageReceivedAsync);
}
}
Once you get the input from user, you can make a REST call and then after you get the response back from API, post it back to the user using the context.PostAsync method.
As Ashwin said, A bot is just a web API and you are just sending/receiving requests as you would with any web API. Below is some documentation that should help get you started.
Basic Overview
Create a bot with the Bot Connector service
API Reference

Email Sending .NET Core

I just want to have a function, which will be sending mail to different people.
I have written a Email Service, just like this:
public void SendEMail(EMail mail)
{
var body = JObject.FromObject(settings);
body.Merge(JObject.FromObject(mail));
GlobalTelemetry.TrackDependency("E-Mail Service", "Send", () =>
{
var result = AsyncHelper.RunSync(() => httpClient.PostAsync(azureFunctionUrl,
new StringContent(body.ToString(), Encoding.UTF8, "application/json")));
if (!result.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"E-Mail Service failed to send mails. Http Status {result.StatusCode}. Please check if the Url '{azureFunctionUrl}' is correct. Env: {Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}");
}
});
}
and by every other functions, which want to use this mail service, do just like this:
SendMail = mail =>
{
var fireAndForgetTask = Task.Run(() =>
{
try
{
eMailService.SendEMail(mail);
}
catch (Exception e)
{
Log.Fatal(e, $"Fail to send E-Mail to: '{mail.To}'");
}
});
};
it does work, but fireAndForgetTask is not used, I have no idea how to do it better.
Is it necessary to write a Email Job???
Some better suggestion is welcome :-)
I had a similar issue in my app for sending a password reset email, I did not want the delay of waiting for the email to send because if a hacker is trying to find out if an account exists for an email address the delay could indicate that the account does exist. So I wanted to fire and forget the email with no delay.
So I implemented a simple extension method named forget like this in a separate static class based on this so question:
public static void Forget(this Task task)
{
}
and then I send my email async and use that .Forget extension to not wait for it like this:
emailSender.SendPasswordResetEmailAsync(
Site,
model.Email,
sr["Reset Password"],
resetUrl).Forget();
note that I am not using "await" when I call this async method and the .Forget gets rid of the warning that would otherwise appear in Visual Studio when calling an async method without the await keyword

Do some work after the response in ASP.NET Core

I have an ASP.NET Core website, using EFCore.
I would like to do some work like logging to the database, but after having sent the response to the user in order to answer faster.
I could do it in a different thread, but due to async access of the DbContext I am not sure it is safe. Is there any recommended way to do that?
public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
try
{
var newModel = new ResponseViewModel(model);
// Some work
return View("RequestView",newModel)
}
finally
{
// Some analysis on the request
// I would like to defer this part
await Log(model);
}
}
One of the reason is that I would like to call a web-service (geocoding), which is not needed to answer, but good to work on the log (I need the city/country of coordinates).
I see this has never been answered, but actually have a solution.
The simple solution:
public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
try
{
var newModel = new ResponseViewModel(model);
// Some work
return View("RequestView",newModel)
}
finally
{
Response.OnCompleted(async () =>
{
// Do some work here
await Log(model);
});
}
}
The secure solution, as OnCompleted used to be called before the response being sent, so delaying the response:
public static void OnCompleted2(this HttpResponse resp, Func<Task> callback)
{
resp.OnCompleted(() =>
{
Task.Run(() => { try { callback.Invoke(); } catch {} });
return Task.CompletedTask;
});
}
and call Response.OnCompleted2(async () => { /* some async work */ })
Building on Jeans answer and a question and answer on the try - return - finally pattern, the try and finally blocks can be removed (if you don't really want to catch an exception).
This leads to the following code:
public async Task<IActionResult> Request([FromForm] RequestViewModel model, string returnUrl = null)
{
var newModel = new ResponseViewModel(model);
// Some work
Response.OnCompleted(async () =>
{
// Do some work here
await Log(model);
});
return View("RequestView", newModel);
}
There's no out of the box way to do what you want.
But, here's a possible approach:
Have a queue and a worker (thread or process)
Just before the request is sent back to the client, add a message in that queue
The worker will pick up that message at some point in the future, and process it.
Since the worked runs somewhere else and not on the request thread, the server can complete the request thread and the worker can do what's left.
Try using Hangfire. Hangfire is an easy way to perform background processing in .NET and .NET Core applications. No Windows Service or separate process required.
Backed by persistent storage. Open and free for commercial use.
You could do something like
var jobId = BackgroundJob.Enqueue(() => Log(model));
And here is my blog post on using HangFire in ASP.NET Core
Create a new class that inherits from ActionFilterAttribute, overwrite the OnResultExecuted method to perform the logging and then apply your attribute class to the controller actions you want to do logging.

NullReferenceException occurs during offline sync to Azure Mobile Service

I am trying to make offline sync to table from azure mobile service. My Xamarin Form version is 1.4.2.6359.
I try to test my code in OnCreate method of MainActivity. All preparation steps such as MobileServiceClient initialization, MobileServiceSQLiteStore initialization, SyncTable creation, etc are ok.
When I try to call PullAsync, I am getting NullReferenceException. I capture the package using Package Capture App from mobile. The request goes to Azure Mobile service and it returns the correct json data successfully.
When I try the same code in Xamarin Android project (not Xamarin Form), it is working fine.
To reproduce the issue.
Just create Xamarin Form (Portable) project and use my code.
My Code
private async Task Test() {
const string applicationURL = #"https://xxxx.azure-mobile.net/";
const string applicationKey = #"xxxx";
CurrentPlatform.Init();
var client = new MobileServiceClient(applicationURL, applicationKey);
string path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "store.db");
if (!File.Exists(path)) {
File.Create(path).Dispose();
}
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<Product>();
await client.SyncContext.InitializeAsync(store);
var productTable = client.GetSyncTable<Product>();
try {
await client.SyncContext.PushAsync();
await productTable.PullAsync("allProducts", productTable.CreateQuery());
var t = await productTable.ToListAsync();
Console.WriteLine("Product Count : " + t.Count);
}
catch (Java.Net.MalformedURLException ex) {
Console.WriteLine(ex.Message);
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
References:
http://azure.microsoft.com/en-us/documentation/articles/mobile-services-xamarin-android-get-started-offline-data/
http://blogs.msdn.com/b/carlosfigueira/archive/2014/04/07/deep-dive-on-the-offline-support-in-the-azure-mobile-service-managed-client-sdk.aspx
I got the solution for this case.
As far as my understanding, this is what is happening. During the application is loading, I call PullAsync. It is async call and during this call, application keeps loading other components. The actual NullReferenceException is coming from OnPrepareOptionsMenu function (Xamarin.Forms.Platform.Android.AndroidActivity.OnPrepareOptionsMenu). The exception is happening on other thread and the thread simply dies. That's why I cannot get stack trace from my main thread.
This NullReferenceException issue is totally not related to Azure Mobile Service.
I override OnPrepareOptionsMenu in MainActivity and add try-catch block to base class function call. The problem is solved. Here is my code in MainActivity class.
public override bool OnPrepareOptionsMenu(IMenu menu) {
try {
// I am always getting menu.HasVisibleItems = false in my app
if (menu != null && menu.HasVisibleItems) {
// Exception is happening when the following code is executed
var result = base.OnPrepareOptionsMenu(menu);
return result;
}
}
catch{
}
return true;
}
I don't really understand why it is happening. Please point me out if you have better understanding of this case and better solution.
I think my issue is same as this : http://forums.xamarin.com/discussion/23579/exception-whilte-trying-to-open-activity-from-xamarin-forms-page

Possible to force the C# Facebook SDK to use HTTP instead of HTTPS?

I need to do some connectivity simulations to see that my code handles various connectivity errors to Facebook. I want to be able to simulate 500s, timeouts etc.
The easiest way to do that is to use Fiddler, but it seems to not be working with HTTPS (I get 403s when I try).
Is ther a way to force the SDK to work with HTTP instead of HTTPS for debugging purposes?
Facebook C# SDK supports your scenario for mocking the entire HttpWebRequest and HttpWebResponse. In fact we actually use that internally in our unit tests so that every single line of the code in Facebook C# SDK actually gets executed and the result is always the same. https://github.com/facebook-csharp-sdk/facebook-csharp-sdk/blob/v5/Source/Facebook.Tests/TestExtensions.cs For now you will need to check these tests in v5 branch as we haven't yet migrated those tests to v6.
For v5, you will need to override the protected CreateHttpWebRequest method in FacebookClient.
Here is an example for v5 when there is no internet connection. There are three hidden classes HttpWebRequestWrapper, HttpWebResponseWrapper and WebExceptionWrapper that you will need to make use of.
public static void NoInternetConnection(this Mock<Facebook.FacebookClient> facebookClient, out Mock<HttpWebRequestWrapper> mockRequest, out Mock<WebExceptionWrapper> mockWebException)
{
mockRequest = new Mock<HttpWebRequestWrapper>();
mockWebException = new Mock<WebExceptionWrapper>();
var mockAsyncResult = new Mock<IAsyncResult>();
var request = mockRequest.Object;
var webException = mockWebException.Object;
var asyncResult = mockAsyncResult.Object;
mockRequest.SetupProperty(r => r.Method);
mockRequest.SetupProperty(r => r.ContentType);
mockRequest.SetupProperty(r => r.ContentLength);
mockAsyncResult
.Setup(ar => ar.AsyncWaitHandle)
.Returns((ManualResetEvent)null);
mockWebException
.Setup(e => e.GetResponse())
.Returns<HttpWebResponseWrapper>(null);
mockRequest
.Setup(r => r.GetResponse())
.Throws(webException);
mockRequest
.Setup(r => r.EndGetResponse(It.IsAny<IAsyncResult>()))
.Throws(webException);
AsyncCallback callback = null;
mockRequest
.Setup(r => r.BeginGetResponse(It.IsAny<AsyncCallback>(), It.IsAny<object>()))
.Callback<AsyncCallback, object>((c, s) =>
{
callback = c;
})
.Returns(() =>
{
callback(asyncResult);
return asyncResult;
});
var mockRequestCopy = mockRequest;
var mockWebExceptionCopy = mockWebException;
facebookClient.Protected()
.Setup<HttpWebRequestWrapper>("CreateHttpWebRequest", ItExpr.IsAny<Uri>())
.Callback<Uri>(uri =>
{
mockRequestCopy.Setup(r => r.RequestUri).Returns(uri);
mockWebExceptionCopy.Setup(e => e.Message).Returns(string.Format("The remote name could not be resolved: '{0}'", uri.Host));
})
.Returns(request);
}
You can then write your tests as below.
[Fact]
public void SyncWhenThereIsNotInternetConnectionAndFiddlerIsNotOpen_ThrowsWebExceptionWrapper()
{
var mockFb = new Mock<FacebookClient> { CallBase = true };
Mock<HttpWebRequestWrapper> mockRequest;
Mock<WebExceptionWrapper> mockWebException;
mockFb.NoInternetConnection(out mockRequest, out mockWebException);
Exception exception = null;
try
{
var fb = mockFb.Object;
fb.Get(_parameters);
}
catch (Exception ex)
{
exception = ex;
}
mockFb.VerifyCreateHttpWebRequest(Times.Once());
mockRequest.VerifyGetResponse();
mockWebException.VerifyGetReponse();
Assert.IsAssignableFrom<WebExceptionWrapper>(exception);
}
In v6 we have made mocking the HttpWebRequest and HttpWebResponse much easier.
Create your custom HttpWebRequest and HttpWebResponse by inheriting HttpWebRequestWrapper and HttpWebReponseWrapper.
Then change the default http web request factory for Facebook C# SDK. Here is the sample of the default factory.
FacebookClient.SetDefaultHttpWebRequestFactory(uri => new HttpWebRequestWrapper((HttpWebRequest)WebRequest.Create(uri)));
If you want to change the HttpWebRequestFactor per FacebookClient instance then use the following code.
var fb = new FacebookClient();
fb.HttpWebRequestFactory = uri=> new MyHttpWebRequestWrapper(uri);
Note: HttpWebRequestWrapper, HttpWebResponseWrapper, WebExceptionWrapper, FacebookClient.SetDefaultHttpWebRequestFactory and FacebookClient.HttpWebRequestFactory has the attribute [EditorBrowsable(EditorBrowsableState.Never)] so you might not see it in the intellisense.
Things like no internet connection that you mention should actually be a part of facebook c# sdk tests and not your app unit tests. The sdk should guarantee that when there is not internet conenction it always throws WebExceptionWrapper and your app unit tests should actually be handling the WebExceptionWrapper exception and not mocking the entire httpwebrequest and httpwebresponse.
I'd suggest you introduce another level of abstraction to your code and code to that abstraction rather than the implementation. Eg.
public interface IFacebookClient {
IEnumerable<Friend> GetFriends();
}
public class HttpsClient : IFacebookClient {
public IEnumerable<Friend> GetFriends() {
// Make a call out to the Facebook API, as per usual
};
}
In your consuming code you'd do something like;
public class ConsumingCode {
private IFacebookClient _client;
public ConsumingCode(IFacebookClient client) {
_client = client;
foreach (Friend friend in _client.GetFriends()) {
// Do something with each Friend
}
}
}
If you're using an IoC container this can all get wired up for you automatically. MVVM frameworks like Caliburn.Micro tend to support this as well.
Then when it comes to unit testing (or manual testing) you can change the implementation of your interface;
public class Http403Client : IFacebookClient {
public IEnumerable<Friend> GetFriends() {
throw new HttpException(403, "Forbidden");
}
}
Obviously this is just a mock up example but I think it demonstrates the concept that you want to implement.