Do some work after the response in ASP.NET Core - entity-framework

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.

Related

Rx.Net - Publish method missing first few items when subscribing to Cold Observable

Inspired by Akavache I am trying to create a solution that provides me with an IObservable<IArticle>. The method essentially first try to get all the articles that are present in the database, then it tries to fetch updated articles from the webservice and as it is getting the latest articles from webservice it tries to save them back to the database.
Since the webservice is essentially a cold observable and I don't want to subscribe twice, I used Publish to connect to it. My understanding is that I am using the correct version of the Publish method, however, many times the method tend to miss first couple of Articles from the GetNewsArticles. This was observed through the UI and also the Trace calls added in the call below.
Apart from solving the problem, it would be great to also understand how to debug/test this code (apart from introducing DI to inject NewsService).
public IObservable<IArticle> GetContents(string newsUrl, IScheduler scheduler)
{
var newsService = new NewsService(new HttpClient());
scheduler = scheduler ?? TaskPoolScheduler.Default;
var fetchObject = newsService
.GetNewsArticles(newsUrl,scheduler)
.Do(x => Trace.WriteLine($"Parsing Articles {x.Title}"));
return fetchObject.Publish(fetchSubject =>
{
var updateObs = fetchSubject
.Do( x =>
{
// Save to database, all sync calls
})
.Where(x => false)
.Catch(Observable.Empty<Article>());
var dbArticleObs = Observable.Create<IArticle>(o =>
{
return scheduler.ScheduleAsync(async (ctrl, ct) =>
{
using (var session = dataBase.GetSession())
{
var articles = await session.GetArticlesAsync(newsUrl, ct);
foreach (var article in articles)
{
o.OnNext(article);
}
}
o.OnCompleted();
});
});
return
dbArticleObs // First get all the articles from dataBase cache
.Concat(fetchSubject // Get the latest articles from web service
.Catch(Observable.Empty<Article>())
.Merge(updateObs)) // Update the database with latest articles
.Do(x => Trace.WriteLine($"Displaying {x.Title}"));
});
}
UPDATE - Added GetArticles
public IObservable<IContent> GetArticles(string feedUrl, IScheduler scheduler)
{
return Observable.Create<IContent>(o =>
{
scheduler = scheduler ?? DefaultScheduler.Instance;
scheduler.ScheduleAsync(async (ctrl, ct) =>
{
try
{
using (var inputStream = await Client.GetStreamAsync(feedUrl))
{
var settings = new XmlReaderSettings
{
IgnoreComments = true,
IgnoreProcessingInstructions = true,
IgnoreWhitespace = true,
Async = true
};
//var parsingState = ParsingState.Channel;
Article article = null;
Feed feed = null;
using (var reader = XmlReader.Create(inputStream, settings))
{
while (await reader.ReadAsync())
{
ct.ThrowIfCancellationRequested();
if (reader.IsStartElement())
{
switch (reader.LocalName)
{
...
// parsing logic goes here
...
}
}
else if (reader.LocalName == "item" &&
reader.NodeType == XmlNodeType.EndElement)
{
o.OnNext(article);
}
}
}
o.OnCompleted();
}
}
catch (Exception e)
{
o.OnError(e);
}
});
return Disposable.Empty;
});
}
UPDATE 2
Sharing the link to source code here.
There's a few things I don't like about your code. I assume NewsService is an IDisposable as it takes an HttpClient (which is disposable). You're not doing a proper clean up.
Also, you haven't provided a complete method - because you've tried cutting it down for the question - but that makes it hard to reason about how to rewrite the code.
That said, the one thing that sticks out to me as quite horrid looking is the Observable.Create. Can you please try this code instead and see if it helps things work for you?
var dbArticleObs =
Observable
.Using(
() => dataBase.GetSession(),
session =>
from articles in Observable.FromAsync(ct => session.GetArticlesAsync(newsUrl, ct))
from article in articles
select article);
Now, if that does, try rewriting fetchObject to use the same Observable.Using when newing up the `NewService.
In any case, it would be good if you could provide a complete implementation of GetContents, NewsService and your dataBase code in your question.

Unable to set values in the map using webclient call response

I am unable to get values filled in the map after making a web client call and using the response of the previous Mono.Here is the code I have tried.The value of parameters.size() comes out to zero.Not able to get the reason as to why the value is not filled.I basically want to return age ( and not Mono object)
from this method.Using block gives an error block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3.
Map<String, String> parameters = new HashMap<String,String>();
Mono<Person> obj = webClient
.post()
.uri("dummy url")
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(Person.class)
.flatMap(resp -> {
parameters.put("name", resp.getName());
parameters.put("age", resp.getAge());
return Mono.just(new Person(resp.getName(),resp.getAge()));
}
);
System.out.println(parameters.size());
Please suggest where I am wrong and solution to fix the same.
Since this is about collecting and using a token of some sort collected from a previous HTTP call, your best bet is to delegate all that to an ExchangeFilterFunction.
An ExchangeFilterFunction is a filter that is executed on the client side for each outgoing request. Here is a very, very naïve implementation of such a filter:
class TokenFilterFunction implements ExchangeFilterFunction {
private final AtomicReference<String> token = new AtomicReference<>();
#Override
public Mono<ClientResponse> filter(ClientRequest req, ExchangeFunction next) {
if (this.token.get() == null) {
return fetchToken(next).then(sendRequest(req, next));
}
else {
return sendRequest(req, next);
}
}
private Mono<ClientResponse> sendRequest(ClientRequest req, ExchangeFunction next) {
ClientRequest request = ClientRequest.from(req)
.header("Token", this.token.get()).build();
return next.exchange(request);
}
private Mono<Void> fetchToken(ExchangeFunction next) {
ClientRequest tokenRequest = ClientRequest.create(HttpMethod.GET,
URI.create("https://example.com/token")).build();
return next.exchange(tokenRequest).doOnNext(res -> {
this.token.set(res.headers().header("Token").get(0));
}).then();
}
}
This could automatically call the token endpoint to fetch a token when needed and directly chain with the request you asked in the first place. Again, such an implementation should be much more complex than that, handling domains, errors, and more.
If you're using some authentication technology, such a filter might be implemented already in Spring Security in a much, much better way.
You can configure it on your client during the building phase, like:
WebClient webClient = WebClient.builder().filter(new TokenFilterFunction()).build();

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

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);
}
});

CodeFluentDuplicateException what i'm i missing

I have problem getting a "CodeFluent.Runtime.CodeFluentDuplicateException" and i'm probably missing something fundamental.
However i first followed this blog about using Servicestack and codefluents and made my own template.
I have no problems to get entities but doing a put give me an exception mentioned.
Ok maybe i have done some wrong in my template so i took another approach looking for answers i found a "complete" project using Webapi and a template, ready to use. Generate ASP .NET Web API Controllers using Templates.
This generates all the controllers and seems to work. However i have the same exeption when using the "put".
This is an example of generated controller code for Put
public HttpResponseMessage Put([FromBody]Country value)
{
if (value == null || !value.Save())
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
return Request.CreateResponse(HttpStatusCode.OK, value);
}
This is how i use the controller above inside a Xamarin.Forms solution.
public async Task UpdateAsync(Country update, bool isNewItem=false)
{
HttpClient client = new HttpClient();
// RestUrl = http://developer.xamarin.com:8081/api/todoitems{0}
var uri = new Uri(string.Format(Constants.RestUrl2, update.Id));
try
{
var json = JsonConvert.SerializeObject(update);
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
if (isNewItem)
{
response = await client.PostAsync(uri, content);
}
else
{
response = await client.PutAsync(uri, content);
}
if (response.IsSuccessStatusCode)
{
Debug.WriteLine(#" TodoItem successfully saved.");
}
}
catch (Exception ex)
{
Debug.WriteLine(#" ERROR {0}", ex.Message);
}
}
Any suggestions of what i'm missing?
Thanks for any help
//Greg
I got the answer from the softfluent support
"..
Another case of duplicate exception is when you enable optimistic concurrency and the RowVersion of the instance you are updating is null. In this case, the stored procedure will insert the row instead of updating it.
.."
Changeing the concurrencyMode=None did the trick

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.