IMobileServiceClient.PullAsync 尝试与 Azure 移动服务同步时出现死锁

IMobileServiceClient.PullAsync deadlock when trying to sync with Azure Mobile Services

我有下面的 classes。

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

我使用我拥有的 BaseRepository 中的这个单例,它是 5 个不同实体存储库的基础 class。

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

用户存储库。

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

包含 GetUserById 方法的 DataService Facade class。

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

用户查看模型方法。

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

从 iOS UIViewController 调用。

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

            UserModel user = await ViewModel.GetUsersAsync ();
        }

AzureMobileDataContext 更像是 IMobileServiceClient 上下文操作的线程安全包装器,确保不会有多个线程尝试初始化数据库(我在直接使用它时出现异常BaseRepository<T>之前)。

从这里我不太确定问题出在哪里。我怀疑包装器不是最佳解决方案,欢迎提出任何建议。

还有其他调试 PullAsync 方法的方法吗?

[编辑]

本地 SQLite 数据库从远程服务同步 table 数据,但仍然没有调用 return。

问题出在服务器端的 Azure 移动服务中。

我从 TableController returning 一个 IEnumerable 但 SDK 使用 OData 查询表达式来完成它自己的工作,returning 一个 IEnumerable 是不够的,更改为 IQueryable 解决了这个问题拉取数据时不断循环。

我坚信服务器 return 类型不应该与 SDK 相关,但这就是它的工作方式。