尝试与Azure移动服务同步时IMobileServiceClient.PullAsync死锁 [英] IMobileServiceClient.PullAsync deadlock when trying to sync with Azure Mobile Services

查看:58
本文介绍了尝试与Azure移动服务同步时IMobileServiceClient.PullAsync死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下课程.

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个不同实体存储库的基类.

I use this singleton from a BaseRepository I have that is a base class for 5 different entity repositories.

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类.

A DataService Facade class containing the GetUserById method.

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调用.

Call from an iOS UIViewController.

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

            UserModel user = await ViewModel.GetUsersAsync ();
        }

AzureMobileDataContext更多地用作IMobileServiceClient上下文操作的线程安全包装器,请确保没有多个线程将尝试初始化数据库(之前直接用于BaseRepository<T>时,我有一个例外).

The AzureMobileDataContext serves more as a thread safe wrapper to the IMobileServiceClient context operations, making sure not multiple threads will try to initialize the database (I had an exception when using it directly to the BaseRepository<T> before).

我从这里不确定问题可能出在哪里.我怀疑包装器不是最好的解决方案,欢迎任何建议.

I'm not so sure from here where might the problem is. I suspect that the wrapper is not the best solution and any recommendations are welcome.

还有其他方法可以调试PullAsync方法吗?

Any other ways to debug the PullAsync method?

本地SQLite数据库同步了来自远程服务的表数据,但调用仍未返回.

The local SQLite database syncs the table data from the remote service but still the call doesn't return.

推荐答案

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

The problem was in the Azure Mobile Service, server side.

我从TableController返回了一个IEnumerable,但是SDK使用OData查询表达式来完成自己的工作,返回IEnumerable是不够的,更改为IQueryable可以解决此问题,即在提取数据时会出现连续循环的情况.

I was returning from the TableController an IEnumerable but the SDK uses OData query expressions to do it's own job, returning an IEnumerable is not sufficient, changing to IQueryable fixed this issue of a continuous looping when pulling data.

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

I strongly believe that the server return type shouldn't be related to the SDK but this is the way it works.

这篇关于尝试与Azure移动服务同步时IMobileServiceClient.PullAsync死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆