Context - I am using Azure CosmosDB GraphAPI - and azure doesn't have logging for it, Sad!
So we came up with using our own logging of queries and its metaproperties
I implemented a Singleton class that is used by other services for DB Calls and I want to have a fire and forget the call to the logger.
public class GraphDatabaseQuery : IGraphDatabaseQuery
{
private readonly GremlinClient gremlinClient;
public GraphDatabaseQuery()
{
if (gremlinClient == null)
{
gremlinClient = CosmosDbClient.Instance;
}
else
{
if (gremlinClient.NrConnections == 0)
{
gremlinClient = CosmosDbClient.Instance;
}
}
}
public async Task<ResultSet<dynamic>> SubmitQueryAsync(string query, string fullName)
{
var response = await gremlinClient.SubmitAsync<dynamic>(query);
_ = SendQueryAnalytics(response, query, fullName);
return response;
}
public async Task<dynamic> SubmitQueryWithSingleSingleResultAsync(string query, string fullName)
{
var response = await gremlinClient.SubmitWithSingleResultAsync<dynamic>(query);
return response;
}
private async Task<bool> SendQueryAnalytics(ResultSet<dynamic> response, string query, string fullName)
{
try
{
await new QueryAnalytics().pushData(response, query, fullName); // breakpoint here
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return true;
}
}
in startup.cs
services.AddSingleton<IGraphDatabaseQuery, GraphDatabaseQuery>();
and the interface
public interface IGraphDatabaseQuery
{
public Task<ResultSet<dynamic>> SubmitQueryAsync(string query, string fullName);
public Task<dynamic> SubmitQueryWithSingleSingleResultAsync(string query, string fullName);
}
Whenever I test it the breakpoint hits "SendQueryAnalytics" but i assume at that moment the response from the calling function would be sent back
is this not working because I am using the singleton pattern?
Whenever I test it the breakpoint hits "SendQueryAnalytics" but i assume at that moment the response from the calling function would be sent back
No. All asynchronous methods begin executing synchronously.
So what happens is that SendQueryAnalytics is called and begins executing, enters the try block, creates a new QueryAnalytics instance, and calls pushData. pushData also begins executing and (presumably) returns an incomplete Task. Then the await in SendQueryAnalytics observes that that Task is incomplete and returns an incomplete Task to SubmitQueryAsync, which discards that Task instance and then returns the response.
Thus, seeing the response returned after that breakpoint is normal.
I don't think that the singleton pattern has something to do with it. Since you want a 'fire and forget' why don't you use something like hangfire?
Related
I learning Xamarin, I use a function to delete all my sqlite database, add new elements and refesh my listview.
the code stop, I thinks it is because it has not time for delete first,add in database then refresh thelist view. When I add a timer (2 seconds) it works.
Here is the error at the line "mywordsdatabase.DeleteAllWords();" : System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index'
I am using this sqlite plugin enter link description here
Here is the function :
public async void DeleteAndUpdate(){
mywordsdatabase.DeleteAllWords(); // the error occur here
List<MyWords> WordUserList = await wordmanager.GetWordAsync(UserConnected.MyId);
foreach (MyWords w in WordUserList)
{
mywordsdatabase.AddWord(w);
}
var WordList = await mywordsdatabase.GetWords();
Device.BeginInvokeOnMainThread(() => { WordSList.ItemsSource = WordList; });
}
Here is the Delete function for sqlite:
class MyWordsDatabase
{
private SQLiteAsyncConnection conn;
//CREATE
public MyWordsDatabase()
{
conn = DependencyService.Get<ISQLite>().GetConnection();
conn.CreateTableAsync<MyWords>();
}
public string AddWord(MyWords mywords)
{
conn.InsertAsync(mywords);
return "success";
}
public string DeleteAllWords()
{
conn.DeleteAllAsync<MyWords>();
return "success";
}
}
Thanks for your help
Few things to check, first of ensure the database actually has words to delete, as the error would suggest that it tried to operate upon a null element. Secondly, since the method is async, see if changing the signature will help.
So instead of public string DeleteAllWords() it becomes public async string DeleteAllWords() then inside of the method we can say await conn.DeleteAllAsync<MyWords>(); and where we call it we can then do await mywordsdatabase.DeleteAllWords();
As jmcil said, async code won't wait for its operation to stop execution unless told to do so.
The whole point of asynchronous methods is that they are asynchronous, i.e. they return immediately while the work they initiated continues in the background. If you call that DeleteAllAsync method and then immediately start executing code as though it has finished deleting everything then of course you'll have issues. If you need the deletion to be completed before subsequent code is executed then you need to wait until the deletion is completed before executing that subsequent code. The way to do that is most likely to await the awaitable methods in your code. This:
public string DeleteAllWords()
{
conn.DeleteAllAsync<MyWords>();
return "success";
}
probably ought to be this:
public async Task<string> DeleteAllWordsAsync()
{
await conn.DeleteAllAsync<MyWords>();
return "success";
}
That means that you can either await the call to that method:
await mywordsdatabase.DeleteAllWordsAsync();
or you can trap the Task it returns, execute some code that doesn't depend on it completing and then await the Task later:
var t = mywordsdatabase.DeleteAllWordsAsync();
// ...
await t;
Please change your DeleteAllWords method like following method.
public Task<int> DeleteAllNotesAsync()
{
return conn.DeleteAllAsync<MyWords>();
}
Then judge the return value if it greater than 0, it the return value greater than 0, it means delete success like this simple code.
private async void Button_Clicked(object sender, EventArgs e)
{
int deleteRecord= await App.Database.DeleteAllNotesAsync();
if (deleteRecord>0)
{
await DisplayAlert("Success", "delete" + deleteRecord, "OK");
}
}
public async Task<string> AddWord(MyWords mywords)
{
string result=string.Empty;
try
{
await conn.InsertAsync(mywords);
}
catch (Exception ex)
{
}
return result;
}
public async Task<string> DeleteAllWords()
{
string result = string.Empty;
try
{
await conn.DeleteAllAsync<MyWords>();
}
catch (Exception ex)
{
}
return result;
}
public async void DeleteAndUpdate()
{
try
{
await mywordsdatabase.DeleteAllWords(); // the error occur here
List<MyWords> WordUserList = await wordmanager.GetWordAsync(UserConnected.MyId);
foreach (MyWords w in WordUserList)
{
await mywordsdatabase.AddWord(w);
}
var WordList = await mywordsdatabase.GetWords();
Device.BeginInvokeOnMainThread(() => { WordSList.ItemsSource = WordList; });
}
catch (Exception ex)
{
}
}
In my application I need to call a method before all the API request. If a specific condition met then I need to execute set of statements in that method.
In order to generalize this I created a helper class something like this.
public class CertificateValidator {
readonly IDependencyService _serviceLocator;
public CertificateValidator(IDependencyService serviceLocator) {
_serviceLocator = serviceLocator;
}
public async Task <T> TryExecuteWithCertificateValidationAsync <T> (Task <T> operation) {
var service = _serviceLocator.Get <IDeviceService> ();
if (service.CertificateValidationRequired()) {
// My Code.
}
T actualResult = await operation;
return actualResult;
}
}
And In my viewmodel I have done something like this.
public CertificateValidator ValidateCertificate => new CertificateValidator(_serviceLocator);
var response = await ValidateCertificate
.TryExecuteWithCertificateValidationAsync(MyMethodAsync());
private async Task<RequestResult<Response>> MyMethodAsync()
{
// Some code
}
But when I implement like this the execution flow is
First MyMethodAsync() will be called.
And when it reaches the await method it the executes the
TryExecuteWithCertificateValidationAsync method and runs the remaining code there.
And then when it reaches T actualResult = await operation; return
actualResult; the control go back to MyMethodAsync() - await statement.
And my doubt here is,
I need to execute the TryExecuteWithCertificateValidationAsync completely and then followed by MyMethodAsync.
In short as I said early, I need to execute set of code before I call all my API calls. How I can achieve something similar using async an await.
Rather than passing a Task pass a function:
public async Task<T> TryExecuteWithCertificateValidationAsync<T>(Func<Task<T>> operation)
{
var service = _serviceLocator.Get<IDeviceService>();
if (service.CertificateValidationRequired())
{
// My Code.
}
T actualResult = await operation();
return actualResult;
}
var response = await ValidateCertificate
.TryExecuteWithCertificateValidationAsync(MyMethodAsync);
Update as per comment
If the method requires arguments, the types need to be prepended as additional generic arguments to Func:
private async Task<RequestResult<Response>> MyMethodAsync(int i)
{
// Some code
}
public async Task<T> TryExecuteWithCertificateValidationAsync<T>(Func<int, Task<T>> operation) // Add int as second generic argument
{
T actualResult = await operation(1); // Can now be called with an integer
return actualResult;
}
I'm using HttpClient trying to execute a POST method in Web API controller. The controller method is synchronous. I'm doing so this way:
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
After that I'm calling Wait:
var result = response.Wait(15000);
When running this code, I see the http finish executing, yet the result value is always false. What can I be missing?
Edit:
I now tried an async approach yet it didn't help me as well
public IHttpActionResult Add(Item item)
{
var result = _db.AddItem(item);
return Ok(result);
}
Test project:
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await
_owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
Method2(item).Continue with(task => {// Never reach here }
}
What am I doing wrong?
When debugging I see that the controller method returns a good response, yet it never reaches back to my test
You are mixing async and blocking calls (ie .Result, .Wait()) which are leading to a deadlock.
This looks more like it is a blocking issue on the test client side.
You need to make the test async all the way through in this case if you want to await on results from the server.
Convert test method to async
[TestMethod]
public async Task test1() {
//Arrange
Item item = new Item(...);
//Act
var preview = await Method2(item);
//Assert
Assert.IsNotNull(preview);
}
And update the methods to not mix async and blocking calls.
Method1 does not need asyn/await if it is not using the task after the call so it can be removed and just have the method return the Task that can be awaited
TestServer _owinTestServer;
public Task<HttpResponse> Method1(string url, object body) {
return _owinTestServer.HttpClient.PostAsJsonAsync(url, body);
}
Method2 needs to await the response from Method1 and then get its content.
public async Task<ItemPreview> Method2(object body) {
var response = await Method1("..", body );
return await response.Content.ReadAsAsync<ItemPreview>();
}
It doesn't matter if the controller method is synchronous or not. That is purely a concern on the server code. Since the PostAsJsonAsync method is asynchronous, you need to await it:
var response = await owin.HttpClient.PostAsJsonAsync(uri, body);
Which will allow your code to wait for the response from the server.
I assume your initial code looked something like this...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var result = response.Wait(15000);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
...
And yes, result will be false after waiting the 15 seconds. This is because you have setup the request, A.K.A response, but you haven't actually made the called and any response.Wait(n) will return false.
You just need to start the ReadAsAsync...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
However, I think you will find you can skip the response.Wait(n) all together, as it will return true because the ReadAsAsync() will wait to return or fail. If you're looking to configure the request timeout, you can do that in the HttpClient and your ReadAsAsync will throw an AggregateException with an InnerException of TaskCanceledException.
On a side note, you don't need to use owin.HttpClient you can just instantiate a new HttpClient. I believe the owin object you are referring to is for self hosting your WebApi, I don't know if that matters. But let's say you are calling Add(Item item) on your WebApi and that db.AddItem(item) will return and ItemPreview object, your code could look like this:
[TestMethod]
public void test1()
{
Item item = new(...);
var uri = "..";
var client = new HttpClient();
var response = client.PostAsJsonAsync(uri, item);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
/* The things to happen once you have item preview */
}
The result may always be false because the _db.AddItem is returning false all the time. If not, I've made a change in your code ideally which should work for you
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
await Method2(item).ContinueWith(task => {// Never reach here }
}
Since Method2 returns an Async Task, the ContinueWith will not wait for it to complete and hence it may required await on the method invocation.
I'm trying to wrap my Operations Contracts to the try-catch block. The problem is that my EF context destroyed in the same time scope. I guess the problem compiler doesn't know what to do properly. Also my catch block doesn't handle any exceptions. Sorry for my English, I hope my code show what I mean:
private static async Task<T> SurroundWithTryCatch<T>(Action t, T response) where T : BaseResponse
{
try
{
await Task.Run(t);
}
catch (Exception e)
{
response.Success = false;
response.Errors.Add(e.Message);
}
return response;
}
public async Task<CreateResponse> CreateAsync(CreateRequest request)
{
var response = new CreateResponse();
return await SurroundWithTryCatch(async delegate
{
var newUser = new ApplicationUser { UserName = request.UserName, Email = request.Email };
await Database.UserManager.CreateAsync(newUser, request.Password);
//Some another logic...
await Database.CommitAsync();
}, response);
}
The problem in the second line of CreateAsync method. UserManager was cleaned by GC earlier. So I have ObjectDisposedException. Database is the IUnitOfWork implementation and injected by Autofac.
You're breaking the await chain - t no longer returns a task. Since you no longer have a task, the execution will continue after the first await, rather than after the whole method is done. Think of await as return - if you don't return (and await/wait for) a Task, you lose your chance at synchronization.
Instead, you want to pass Func<Task>, and await it directly:
private static async Task<T> SurroundWithTryCatch<T>(Func<Task> t, T response) where T : BaseResponse
{
try
{
await t();
}
catch (Exception e)
{
response.Success = false;
response.Errors.Add(e.Message);
}
return response;
}
public async Task<CreateResponse> CreateAsync(CreateRequest request)
{
var response = new CreateResponse();
return await SurroundWithTryCatch(async () =>
{
var newUser = new ApplicationUser { UserName = request.UserName, Email = request.Email };
await Database.UserManager.CreateAsync(newUser, request.Password);
//Some another logic...
await Database.CommitAsync();
}, response);
}
Task.Run also works, but you probably don't want that anyway - your code is asynchronous, and (a guess) you're running in a ASP.NET request, so there's no benefit to launching a task just to wait on the result of another task. Task.Run is used for doing CPU work in a separate thread, something you usually want to avoid in ASP.NET.
I have identified a bottleneck in my TCP application that I have simplified for the sake of this question.
I have a MyClient class, that represents when a client connects; also I have a MyWrapper class, that represents a client that fulfill some conditions. If a MyClientfulfill some conditions, it qualifies for wrapper.
I want to expose an method that allows the caller to await a MyWrapper, and that method should handle the negotiation and rejection of invalid MyClients:
public static async Task StartAccepting(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var wrapper = await AcceptWrapperAsync(token);
HandleWrapperAsync(wrapper);
}
}
Therefore AcceptWrapperAsync awaits a valid wrapper, and HandleWrapperAsync handles the wrapper asynchronously without blocking the thread, so AcceptWrapperAsync can get back to work as fast as it can.
How that method works internally is something like this:
public static async Task<MyWrapper> AcceptWrapperAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var client = await AcceptClientAsync();
if (IsClientWrappable(client))
return new MyWrapper(client);
}
return null;
}
public static async Task<MyClient> AcceptClientAsync()
{
await Task.Delay(1000);
return new MyClient();
}
private static Boolean IsClientWrappable(MyClient client)
{
Thread.Sleep(500);
return true;
}
This code simulates that there is a client connection every second, and that it takes half a second to checkout if the connection is suitable for a wrapper. AcceptWrapperAsync loops till a valid wrapper is generated, and then returns.
This approach, that works well, has a flaw. During the time that IsClientWrappable is executing, no further clients can be accepted, creating a bottleneck when lot of clients are trying to connect at the same time. I am afraid that in real life, if the server goes down while having lot of clients connected, the going up is not gonna be nice because all of them will try to connect at the same time. I know that is very difficult to connect all of them at the same time, but I would like to speed up the connection process.
Making IsClientWrappable async, would just ensure that the executing thread is not blocked till the negotiation finishes, but the execution flow is blocked anyway.
How could I improve this approach to continuously accept new clients but still be able of awaiting a wrapper using AcceptWrapperAsync?
//this loop must never be blocked
while (!token.IsCancellationRequested)
{
var client = await AcceptClientAsync();
HandleClientAsync(client); //must not block
}
Task HandleClientAsync(Client client) {
if (await IsClientWrappableAsync(client)) //make async as well, don't block
await HandleWrapperAsync(new MyWrapper(client));
}
This way you move the IsClientWrappable logic out of the accept loop and into the background async workflow.
If you do not wish to make IsClientWrappable non-blocking, just wrap it with Task.Run. It is essential that HandleClientAsync does not block so that its caller doesn't either.
TPL Dataflow to the rescue. I have created a "producer/consumer" object with two queues that:
accepts inputs from "producer" and stores it in the "in" queue.
a internal asynchronous task read from the "in" queue and process the input in parallel with a given maximum degree of parallelism.
put the processed item in the "out" queue afterwards. Result or Exception.
accepts a consumer to await an item. Then can check if the processing was successful or not.
I have done some testing and it seems to work fine, I want to do more testing though:
public sealed class ProcessingResult<TOut>
where TOut : class
{
public TOut Result { get; internal set; }
public Exception Error { get; internal set; }
}
public abstract class ProcessingBufferBlock<TIn,TOut>
where TIn:class
where TOut:class
{
readonly BufferBlock<TIn> _in;
readonly BufferBlock<ProcessingResult<TOut>> _out;
readonly CancellationToken _cancellation;
readonly SemaphoreSlim _semaphore;
public ProcessingBufferBlock(Int32 boundedCapacity, Int32 degreeOfParalellism, CancellationToken cancellation)
{
_cancellation = cancellation;
_semaphore = new SemaphoreSlim(degreeOfParalellism);
var options = new DataflowBlockOptions() { BoundedCapacity = boundedCapacity, CancellationToken = cancellation };
_in = new BufferBlock<TIn>(options);
_out = new BufferBlock<ProcessingResult<TOut>>(options);
StartReadingAsync();
}
private async Task StartReadingAsync()
{
await Task.Yield();
while (!_cancellation.IsCancellationRequested)
{
var incoming = await _in.ReceiveAsync(_cancellation);
ProcessThroughGateAsync(incoming);
}
}
private async Task ProcessThroughGateAsync(TIn input)
{
_semaphore.Wait(_cancellation);
Exception error=null;
TOut result=null;
try
{
result = await ProcessAsync(input);
}
catch (Exception ex)
{
error = ex;
}
finally
{
if(result!=null || error!=null)
_out.Post(new ProcessingResult<TOut>() { Error = error, Result = result });
_semaphore.Release(1);
}
}
protected abstract Task<TOut> ProcessAsync(TIn input);
public void Post(TIn item)
{
_in.Post(item);
}
public Task<ProcessingResult<TOut>> ReceiveAsync()
{
return _out.ReceiveAsync();
}
}
So the example I used on the OP would be something like this:
public class WrapperProcessingQueue : ProcessingBufferBlock<MyClient, MyWrapper>
{
public WrapperProcessingQueue(Int32 boundedCapacity, Int32 degreeOfParalellism, CancellationToken cancellation)
: base(boundedCapacity, degreeOfParalellism, cancellation)
{ }
protected override async Task<MyWrapper> ProcessAsync(MyClient input)
{
await Task.Delay(5000);
if (input.Id % 3 == 0)
return null;
return new MyWrapper(input);
}
}
And then I could add MyClient objects to that queue as fast as I get them, they would be processed in parallel, and the consumer would await for the ones that pass the filter.
As I said, I want to do more testing but any feedback will be very welcomed.
Cheers.