Xamarin forms , Sqlite wait for DeleteAsync - c#

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

Related

Concurrent entrancy occurs despite of use of SemaphoreSlim

I expected the following Connected and Disconnected handlers are called alternately.
But it wasn't. It is not 100% reproducible but sometimes the test failed. Repository is here.
I attach simplified code here because full code will be long. Please refer to the repository for the reproducible example.
public class ClientStressTest3 {
[Fact]
public async Task TestAsync() {
var client = new Client();
int openCloseDifference = 0;
var failures = Channel.CreateUnbounded<string>();
client.Connected += () => {
Interlocked.Increment(ref openCloseDifference);
int difference = openCloseDifference;
Debug.WriteLine("Connected: {}", difference);
if (Math.Abs(difference) > 1) {
// Failure point. Why enter here?
_ = failures.Writer.WriteAsync($"open close difference {difference}");
}
};
client.Disconnected += () => {
Interlocked.Decrement(ref openCloseDifference);
int difference = openCloseDifference;
Debug.WriteLine("Disconnected: {}", difference);
if (Math.Abs(difference) > 1) {
_ = failures.Writer.WriteAsync($"open close difference {difference}");
}
};
var tasks = new List<Task>();
for (int i = 0; i < 625; i++) {
tasks.Add(Task.Run(async () => {
try {
await client.ConnectAsync().ConfigureAwait(false);
}
catch (Exception ex) { }
}));
tasks.Add(Task.Run(async () => {
try {
await client.CloseAsync().ConfigureAwait(false);
}
catch (Exception ex) { }
}));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
while (await failures.Reader.WaitToReadAsync().ConfigureAwait(false)) {
string failure = await failures.Reader.ReadAsync().ConfigureAwait(false);
throw new Exception(failure);
}
}
}
class Client {
public event Action Connected = delegate { };
public event Action Disconnected = delegate { };
private readonly SemaphoreSlim _connectSemaphore = new(1);
private Task _dispatch = Task.CompletedTask;
private WebSocket _clientSocket;
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
// _dispatch may be same among multiple threads if restored at the same time.
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = _dispatch.ContinueWith((_) => DispatchEventAsync(), TaskScheduler.Default);
}
}
finally {
_connectSemaphore.Release();
}
}
private async Task DispatchEventAsync() {
try {
Connected();
}
catch (Exception exception) { }
try {
while (await events.WaitToReadAsync().ConfigureAwait(false)) {
DispatchEvent(await events.ReadAsync().ConfigureAwait(false));
}
}
catch (Exception exception) { }
try {
Disconnected();
}
catch (Exception ex) { }
}
}
I think _connectSemaphore will guard the inner code execution, and only one thread will execute DispatchEventAsync.
But it seems sometimes two threads enter DispatchEventAsync at the same time. I can't understand this.
ContinueWith is a low-level method with dangerous default behavior (link is to my blog). In this case, your code is not behaving how you think it should because ContinueWith (like StartNew) doesn't understand asynchronous delegates.
Specifically, the task returned from ContinueWith (the same task stored in _dispatch) will complete when DispatchEventAsync asynchronously yields (i.e., hits its first await that asynchronously waits). This is likely the call to WaitToReadAsync, which is after Connected and before Disconnected. So the _dispatch task completes after Connected and before Disconnected.
Ideally, you should avoid ContinueWith completely. Sometimes a local asynchronous method helps, e.g.:
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = ChainAsync(_dispatch);
}
}
finally {
_connectSemaphore.Release();
}
static async Task ChainAsync(Task dispatch)
{
await dispatch;
await DispatchEventAsync();
}
}
If you do want to continue using ContinueWith for some reason, then you can use Unwrap:
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = _dispatch.ContinueWith(_ => DispatchEventAsync(), TaskScheduler.Default)
.Unwrap();
}
}
finally {
_connectSemaphore.Release();
}
}
Using either of these approaches, the _dispatch task will now complete at the end of DispatchEventAsync.
I think _connectSemaphore will guard the inner code execution, and only one thread will execute DispatchEventAsync.
Nope. Because you don't wait or await _dispatch. The semaphore only protects starting the task. The execution of the task happens in the background sometime after you release the semaphore.
But I'm chaining it with ContinueWith. Isn't it sufficien
No. That just adds another task that runs after; it doesn't actually run the task or wait for it to complete. If you want to run the _dispatch and then another task while holding the semaphore, just
await _dispatch;
await DispatchEventAsync();

C# Singleton - Fire and forget with Async await

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?

Cannot await async task

I have a method called StartAsync which await different task, this is the design:
public async Task StartAsync(int instance)
{
await SomeController.Foo();
await AnotherController.Foo2();
}
Now I need to run the StartAsync method multiple times, so I have created different Task, in this way I can manage a single execution of the Task of StartAsync:
Task bot1 = new Task(async () => { await new Bot().StartAsync(1); });
Task bot2 = new Task(async () => { await new Bot().StartAsync(2); });
these Tasks can be started by the input, essentially, if the user press 1 then the bot1 Task will start:
public async Task<bool> StartBotInstanceAsync(int instance)
{
try
{
switch(instance)
{
case 1:
await bot1.Start(); //<- Problem here
break;
case 2:
bot2.Start();
break;
}
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
Essentially I need to write a log when an exception happen in side the try catch block, but for doing this I need to await the Task result, unfortunately I get this error:
Cannot await void
on await bot1.Start();
How can I manage the exception in this type of situation?
If you wish to do it with your current structure then you will need 2 separate statements.
case 1:
bot1.Start();
await bot1;
break;
If you want to use multiple bot instances, store them in an array and use the instance parameter as the index, eg :
_bots=new[]{ new Bot(),new Bot()};
public async Task<bool> StartBotInstanceAsync(int instance)
{
try
{
await _bots[instance-1].StartAsync();
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
Or even better :
public async Task<bool> StartBotInstanceAsync(Bot bot)
{
try
{
await bot.StartAsync();
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
var myBot=_bots[instance-1];
var started=await StartBotInstanceAsync(myBot);
More complex flows can be created easily by using async functions that take the bot as a parameter, eg :
public async Task SomeComplexFlow(Bot bot)
{
try
{
await bot.StartAsync();
await bot.DoSomething();
...
}
catch(Exception exc)
{
...
}
}
var myBot=_bots[instance-1];
await SomeComplexFlow(myBot);
It's possible to await multiple tasks for completion too. One coud use Task.WhenAll to wait for all bots to terminate, eg :
await Task.WhenAll(_bots[0].StopAsync()...);
Or, better yet :
var tasks=_bots.Select(bot=>bot.StopAsync());
await Tasks.WhenAll(tasks);

Wrap async lambda to the async method

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.

Different behavior of exception with async method

Supposed you have 2 async method define as bellow:
public async Task<TResult> SomeMethod1()
{
throw new Exception();
}
public async Task<TResult> SomeMethod2()
{
await Task.Delay(50);
throw new Exception();
}
Now if you await on those 2 methods the behavior will be pretty much the same. But if you are getting the task the behavior is different.
If I want to cache the result of such a computation but only when the task run to completion.
I have to take care of the 2 situation:
First Situation:
public Task<TResult> CachingThis1(Func<Task<TResult>> doSomthing1)
{
try
{
var futur = doSomthing1()
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
}
catch ()
{
// ... Remove from the pending cache
throw;
}
}
Second Situation
public Task<TResult> CachingThis2(Func<Task<TResult>> doSomthing)
{
var futur = SomeMethod2();
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
futur.ContinueWith(
t =>
{
// ... Remove from the pending cache
},
TaskContinuationOptions.OnlyOnFaulted);
}
Now I pass to my caching system the method that will execute the computation to cache.
cachingSystem.CachingThis1(SomeMethod1);
cachingSystem.CachingThis2(SomeMethod2);
Clearly I need to duplicate code in the "ConinueWith on faulted" and the catch block.
Do you know if there is a way to make the exception behave the same whether it is before or after an await?
There's no difference in the exception handling required for both SomeMethod1 and SomeMethod2. They run exactly the same way and the exception would be stored in the returned task.
This can easily be seen in this example;
static void Main(string[] args)
{
try
{
var task = SomeMethod1();
}
catch
{
// Unreachable code
}
}
public static async Task SomeMethod1()
{
throw new Exception();
}
No exception would be handled in this case since the returned task is not awaited.
There is however a distinction between a simple Task-returning method and an async method:
public static Task TaskReturning()
{
throw new Exception();
return Task.Delay(1000);
}
public static async Task Async()
{
throw new Exception();
await Task.Delay(1000);
}
You can avoid code duplication by simply having an async wrapper method that both invokes the method and awaits the returned task inside a single try-catch block:
public static async Task HandleAsync()
{
try
{
await TaskReturning();
// Add to cache.
}
catch
{
// handle exception from both the synchronous and asynchronous parts.
}
}
In addition to what I3arnon said in his answer, in case you ContinueWith on async method without the TaskContinuationOptions you specify, exception captured by the Task parameter you receive in the continuation handler can be handled in the following way:
SomeMethod1().ContinueWith(ProcessResult);
SomeMethod2().ContinueWith(ProcessResult);
With ProcessResult handler which looks like:
private void ProcessResult<TResult>(Task<TResult> task)
{
if (task.IsFaulted)
{
//remove from cahe
}
else if (task.IsCompleted)
{
//add to cache
}
}

Categories

Resources