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 相关,但这就是它的工作方式。
我有下面的 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 相关,但这就是它的工作方式。