2015-07-21 65 views
1

我有以下類。嘗試與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個不同的實體存儲庫的基類。

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類。

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數據庫同步從遠程服務,但仍然調用不返回表數據。

+0

我們希望協助調查Azure移動服務產品團隊。您介意通過電子郵件發送到[email protected],以便我們可以嘗試重現此問題嗎? –

+0

@ lindydonna-msft當然會儘快完成。我也想改變一點點AzureMobileDataContext單體的架構,因爲它感覺不太好,我將它作爲一個單例移動到DI容器中,並移除將它轉換爲靜態方法並在應用程序啓動時初始化的鎖。無論如何,似乎我到了那裏的某個地方。我會發送所有的信息。 –

+1

@ lindydonna-msft我發送了電子郵件,感謝您對此的支持。希望我們能夠快速解決它:-) –

回答

3

問題出在Azure移動服務,服務器端。

我從TableController返回一個IEnumerable,但SDK使用OData查詢表達式來完成它自己的工作,返回一個IEnumerable是不夠的,改爲IQueryable修復了這個拉數據時連續循環的問題。

我堅信服務器返回類型不應該與SDK相關,但這是它的工作方式。

+0

感謝您報告回來。這確實是令人困惑的行爲。 IQueryable行爲不同的原因是附加子句可以添加到它,而IEnumerable不會。但是,返回類型會導致這種行爲上的差異可能會非常令人驚訝! –

+0

嗨唐娜,我今天看到的代碼工作正常返回IEnumerable。我想嘗試一下這個架構,我從來沒有做過正確的同步。我向您發送了在我們的電子郵件討論中重現問題的代碼,但我想您錯過了它。當我也有一段時間的時候,我正準備提出另一個問題。 –

+0

我確實看過你的代碼,但我找不出問題的根源。如果您返回IEnumerable,它肯定不會工作,因爲OData查詢選項將不會應用。這些查詢選項對於脫機同步的正確行爲是必需的。否則,就像你看到的那樣,你會以無限循環結束,你會一遍又一遍地得到相同的結果。 –

相關問題