无法同时使用MobileServiceClient的多个实例

时间:2015-09-29 18:28:38

标签: azure xamarin azure-mobile-services

我将项目组织成多个移动服务,按应用程序类型分组,例如:

my-core.azure-mobile.net(用户,设备) my-app-A.azure-mobile.net(销售,订单,发票) my-app-B.azure-mobile.net(库存和零件) 我正在为我的所有服务使用自定义身份验证,并通过为所有3个服务设置相同的主密钥来实现自己的SSO。

当我使用REST客户端测试时,情况进展顺利,例如。通过my-core.azure-mobile.net上的自定义api“登录”的用户能够使用返回的JWT令牌访问其他移动服务的受限API。

但是,在我的xamarin项目中,只有第一个(注意,按创建顺序)MobileServiceClient对象正常工作(例如,返回给定表的结果)。客户端对象分别使用自己的url和key创建,并存储在字典中。

如果我为app-A创建了客户端对象,那么只为app-B创建,我将能够在销售/订单/发票实体上执行CRUD + Sync,而库存/零件实体上的CRUD +同步操作将挂起那里。如果我交换客户端对象创建顺序,则情况相反。

我想知道在MobileServiceClient中是否使用了导致此类行为的内部静态变量,或者它是否是一个有效的错误?

=== code snippet ===

public class AzureService
{
    IDictionary<String, MobileServiceClient> services = new Dictionary<String, MobileServiceClient>();

    public MobileServiceClient Init (String key, String applicationURL, String applicationKey)
    {
        return services[key] = new MobileServiceClient (applicationURL, applicationKey);
    }

    public MobileServiceClient Get(String key) 
    {
        return services [key];
    }

    public void InitSyncContext(MobileServiceSQLiteStore offlineStore)
    {
        // Uses the default conflict handler, which fails on conflict
        // To use a different conflict handler, pass a parameter to InitializeAsync. 
        // For more details, see http://go.microsoft.com/fwlink/?LinkId=521416
        var syncHandler = new MobileServiceSyncHandler ();

        foreach(var client in services) {
            client.Value.SyncContext.InitializeAsync (offlineStore, syncHandler);
        }
    }

    public void SetAuthenticationToken(String uid, String token)
    {
        var user = new MobileServiceUser(uid);
        foreach(var client in services) {
            client.Value.CurrentUser = user;
            client.Value.CurrentUser.MobileServiceAuthenticationToken = token;
        }
    }

    public void ClearAuthenticationToken() 
    {
        foreach(var client in services) {
            client.Value.CurrentUser = null;
        }
    }
}

===更多代码===

public class DatabaseService
{
    public static MobileServiceSQLiteStore LocalStore = null;
    public static string Path { get; set; }
    public static ISet<IEntityMappingProvider> Providers = new HashSet<IEntityMappingProvider> ();

    public static void Init (String dbPath)
    {
        LocalStore = new MobileServiceSQLiteStore(dbPath);

        foreach(var provider in Providers) {
            var types = provider.GetSupportedTypes ();
            foreach(var t in types) {
                JObject item = null;

                // omitted detail to create JObject using reflection on given type

                LocalStore.DefineTable(tableName, item);
            }
        }
    }
}

===仍然代码===

public class AzureDataSyncService<T> : IAzureDataSyncService<T>
{
    public MobileServiceClient ServiceClient { get; set; }

    public virtual Task<List<T>> GetAll()
    {
        try
        {
            var theTable = ServiceClient.GetSyncTable<T>();
            return theTable.ToListAsync();
        }
        catch (MobileServiceInvalidOperationException msioe)
        {
            Debug.WriteLine("GetAll<{0}> EXCEPTION TYPE: {1}, EXCEPTION:{2}", typeof(T).ToString(), msioe.GetType().ToString(), msioe.ToString());
        }
        catch (Exception e)
        {
            Debug.WriteLine("GetAll<{0}> EXCEPTION TYPE: {1}, EXCEPTION:{2}", typeof(T).ToString(), e.GetType().ToString(), e.ToString());
        }
        List<T> theCollection = Enumerable.Empty<T>().ToList();
        return Task.FromResult(theCollection);
    }
}

=== code ===

public class UserService : AzureDataSyncService<User>
{
}

public class PartService : AzureDataSyncService<Part>
{
}

const string coreApiURL = @"https://my-core.azure-mobile.net/";
const string coreApiKey = @"XXXXX";

const string invApiURL = @"https://my-inventory.azure-mobile.net/";
const string invApiKey = @"YYYYY";

public async void Foo ()
{
    DatabaseService.Providers.Add (new CoreDataMapper());
    DatabaseService.Providers.Add (new InvDataMapper ());
    DatabaseService.Init (DatabaseService.Path);

    var coreSvc = AzureService.Instance.Init ("Core", coreApiURL, coreApiKey);
    var invSvc = AzureService.Instance.Init ("Inv", invApiURL, invApiKey);
    AzureService.Instance.InitSyncContext (DatabaseService.LocalStore);
    AzureService.Instance.SetAuthenticationToken("AAA", "BBB");

    UserService.Instance.ServiceClient = coreSvc;
    PartService.Instance.ServiceClient = invSvc;

    var x = await UserService.GetAll(); // this will work
    var y = await PartService.GetAll(); // but not this
}

1 个答案:

答案 0 :(得分:0)

可以使用多个MobileServiceClient对象,但不能使用相同的本地数据库。脱机同步功能使用特定的系统表来跟踪表操作和错误,并且不支持跨多个同步上下文使用相同的本地存储。

我不完全确定它为什么会挂在你的测试中,但是本地数据库文件可能存在锁定而另一个同步上下文正在等待访问。

您应该为每个服务使用不同的本地数据库文件,并对每个同步上下文进行推送和拉取。根据您的特定示例,您只需将 LocalStore 移出 DatabaseService 并移至 AzureService 中的字典。

通常,使用来自同一客户端应用程序的多个服务似乎是一种不寻常的设计。是否有特殊原因要求服务相互分离?