Akavache GetObject <T> hangs while waiting. Any idea what is wrong here?

I have a Xamarin.Forms application, with this code in my App class (yes, this is just a sample to demonstrate the problem):

    public App()
    {
        BlobCache.ApplicationName = "MyApp";
        BlobCache.EnsureInitialized();


        // The root page of your application
        MainPage = GetMainPage();
    }

    public object BlockingGetExternalUser()
    {
        return GetExternalUser().Result;
    }

    private async Task<object> GetExternalUser()
    {
        try
        {
            return await BlobCache.LocalMachine.GetObject<object>("user");
        }
        catch (KeyNotFoundException)
        {
            return null;
        }
    }

      

The user key does not exist, so I expect to get a KeyNotFoundException. However, I never see this exception being thrown. Instead, it just hangs and never returns from a pending GetObject call.

I am running this on my Android 5.0 phone.

Any ideas how to fix this? Am I doing something fundamentally wrong?

Update: on the side of a note: instead of trying GetObject right away, one can try to check if the key actually exists in the cache and only then retrieve it from the cache. However, if I'm not mistaken, there is no other way to do the check other than calling GetObject and catching the exception like in the example above. For a scenario where you just need to know if an item exists, this doesn't seem ideal. Maybe the "Exists ()" method would be nice in Akavache? Or maybe I am missing something?

Update2: Modifying the example to not use the async method in the constructor. Just to prove it's not a problem.

Update3: Remove call from constructor. When I call BlockingGetExternalUser from anywhere in my code, the wait will still hang.

+3


source to share


2 answers


You probably have a dead castle. Quoting Synchronously waiting for an async operation and why Wait () freeze the program here :

The wait inside your async method is trying to get back to the UI thread.

Since the UI thread is busy waiting for the entire task to complete, you have a "dead end".

Note that your call .Result

means Task.Wait()

somewhere.

There are two solutions, either avoid methods entirely async

, or wrap your code Task.Run

like this:



public object BlockingGetExternalUser()
{
    return Task.Run<object>(() => GetExternalUser().Result);
}

      

(I hope this is a compilation I haven't checked in VS :)

From experience I try to avoid methods async

combined with SQLite these days. This is because most of the SQLite wrapper libraries use Task.Run

anti-patterns to provide wrappers async

around their methods, while SQLite does not have any internal asynchronous notation. Note that it is ideal for you to wrap things in Task.Run

to make them asynchronous and that this is only an anti-pattern for library designers to suggest to their users that methods are asynchronous when they are not. You can read more about this here: Task. Running as an anti-template?

+4


source


Using async methods in constructors ( var externalUser = GetExternalUser().Result;

) is considered bad code. You shouldn't use async methods in class constructors. Read this: Can Constructors Be Asynchronous?

You can try to change it to avoid deadlocks:



Func<Task> task = async () => { await GetExternalUser().ConfigureAwait(false); };
task().Wait();

      

... but I won't recommend it.

+1


source







All Articles