我有以下類。嘗試與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數據庫同步從遠程服務,但仍然調用不返回表數據。
我們希望協助調查Azure移動服務產品團隊。您介意通過電子郵件發送到[email protected],以便我們可以嘗試重現此問題嗎? –
@ lindydonna-msft當然會儘快完成。我也想改變一點點AzureMobileDataContext單體的架構,因爲它感覺不太好,我將它作爲一個單例移動到DI容器中,並移除將它轉換爲靜態方法並在應用程序啓動時初始化的鎖。無論如何,似乎我到了那裏的某個地方。我會發送所有的信息。 –
@ lindydonna-msft我發送了電子郵件,感謝您對此的支持。希望我們能夠快速解決它:-) –