IMobileServiceClient.PullAsync-Deadlock beim Synchronisieren mit Azure Mobile Services

Ich habe die folgenden Klassen.

public class AzureMobileDataContext : IAsyncInitialization
    {
        private static readonly Lazy<AzureMobileDataContext> lazy =
            new Lazy<AzureMobileDataContext> (() => 
                new AzureMobileDataContext(
                    new MobileServiceClient(
                                "http://myservice.azure-mobile.net/",
                                "123456789ABCDEFGHIJKLMNOP")));

        public static AzureMobileDataContext Instance { get { return lazy.Value; } }
        public Task Initialization { get; private set; }
        public IMobileServiceClient Context { get; private set; }

        private Object lockObj = new Object ();
        private static MobileServiceSQLiteStore store;

        public AzureMobileDataContext (IMobileServiceClient context)
        {
            Context = context;
            Initialization = Init ();
            Initialization.ContinueWith (async (antecedent) => {
                await Context.SyncContext.InitializeAsync (store, new MobileServiceSyncHandler ());
            });
        }

        private Task Init ()
        {
            return Task.Run (() => {
                lock (lockObj) {
                    if (!Context.SyncContext.IsInitialized) {
                        try {
                            store = new MobileServiceSQLiteStore ("mysqlite.db3");

                            store.DefineTable<Post> ();
                            store.DefineTable<PostPhotoUrl> ();
                            store.DefineTable<User> ();
                            store.DefineTable<Club> ();
                            store.DefineTable<District> ();
                        } catch (Exception ex) {
                            Debug.WriteLine ("Init: {0}", ex.Message);
                        }
                    }
                }
            });
        }

        public async Task<IMobileServiceSyncTable<TEntity>> GetTableAsync<TEntity> ()
        {
            await Initialization;
            return Context.GetSyncTable<TEntity> ();
        }

        public async Task PushAsync ()
        {
            try {
                await Initialization;
                await Context.SyncContext.PushAsync ();
            } catch (MobileServiceInvalidOperationException invalidOperationEx) {
                Debug.WriteLine (invalidOperationEx.Message);
            } catch (MobileServicePushFailedException pushFailedException) {
                Debug.WriteLine (pushFailedException.Message);
            }
        }

        public async Task PullAsync<TEntity> (IMobileServiceTableQuery<TEntity> query)
        {
            try {
                await Initialization;
                IMobileServiceSyncTable<TEntity> entityTable = await GetTableAsync<TEntity> ();
                await entityTable.PullAsync (typeof(TEntity).ToString (), query); // Never returns, no exception is caught or thrown.
                await entityTable.PurgeAsync ();
            } catch (MobileServiceInvalidOperationException preconditionFailedEx) {
                Debug.WriteLine (preconditionFailedEx.Message);
            } catch (Exception ex) {
                Debug.WriteLine (ex.Message);
            }
        }

        public async Task SyncAsync<TEntity> ()
        {
            await PushAsync ();
            IMobileServiceSyncTable<TEntity> syncTable = await GetTableAsync<TEntity> ();
            await PullAsync (syncTable.CreateQuery ());
        }
    }

Ich verwende diesen Singleton aus einem BaseRepository, das eine Basisklasse für 5 verschiedene Entitätsrepositorys ist.

public abstract class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected AzureMobileDataContext MobileServiceContext { get { return AzureMobileDataContext.Instance; } }

        protected virtual Task PushAsync ()
        {
            return MobileServiceContext.PushAsync ();
        }

        protected virtual Task PullAsync (IMobileServiceTableQuery<TEntity> query)
        {
            return MobileServiceContext.PullAsync (query);
        }

        public virtual async Task<DataObjectResponse<IEnumerable<TEntity>>> FindAsync (Expression<Func<TEntity, bool>> predicate)
        {
            IMobileServiceSyncTable<TEntity> syncTable = await MobileServiceContext.GetTableAsync<TEntity> ();
            await PullAsync (syncTable.CreateQuery ());
            IEnumerable<TEntity> entities = await syncTable.Where (predicate).ToEnumerableAsync ();
            return new DataObjectResponse<IEnumerable<TEntity>> (entities);
        }
}

Das Benutzer-Repository.

public class UsersAzureRepository : BaseRepository<User>, IUsersRepository
    {
        public UsersAzureRepository ()
        {
        }

        public async Task<DataObjectResponse<User>> FindByIdAsync (string entityId)
        {
            DataObjectResponse<IEnumerable<User>> users = await FindAsync (p => p.Id == entityId);
            return new DataObjectResponse<User>(users.Data.FirstOrDefault ());
        }
    }

Eine DataService Facade-Klasse mit demGetUserById Methode

public async Task<UserModel> GetUserById (string userId)
        {
            DataObjectResponse<User> users = await UsersRepository.FindByIdAsync (userId);
            UserModel userModel = Mapper.Map<User, UserModel> (users.Data);
            return userModel;
        }

Benutzer sehen Modellmethode an.

public async Task<UserModel> GetUsersAsync() // testing purposes
        {
            UserModel user = await _dataService.GetUserById("032beb3b-1cbf-4a0d-809c-a25c71139c55"); 
            if (user != null) {
                Debug.WriteLine ("User loaded: {0}", user.Id);
            }
            return user;
        }

Anrufen von einem iOS UIViewController.

public async override void ViewDidLoad ()
        {
            base.ViewDidLoad ();

            UserModel user = await ViewModel.GetUsersAsync ();
        }

DasAzureMobileDataContext dient eher als threadsicherer Wrapper für dasIMobileServiceClientKontextoperationen, stellen Sie sicher, dass nicht mehrere Threads versuchen, die Datenbank zu initialisieren (ich hatte eine Ausnahme, als ich sie direkt auf @ verwendetBaseRepository<T> Vor)

Ich bin mir da nicht so sicher wo das problem liegen könnte. Ich vermute, dass der Wrapper nicht die beste Lösung ist und alle Empfehlungen sind willkommen.

Alle anderen Möglichkeiten zum Debuggen desPullAsync Methode

[BEARBEITEN

Die lokale SQLite-Datenbank synchronisiert die Tabellendaten vom Remote-Service, der Aufruf kehrt jedoch nicht zurück.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage