Como utilizar o cliente gerido para Mobile Apps do Azure

Descrição Geral

Este guia mostra-lhe como realizar cenários comuns utilizando a biblioteca de clientes gerido para Serviço de Aplicações do Azure aplicações móveis para aplicações Windows e Xamarin. Se é novo em Aplicações Móveis, deve considerar primeiro completar o tutorial de arranque rápido do Azure Mobile Apps . Neste guia, focamo-nos na SDK gerida pelo lado do cliente. Para saber mais sobre os SDKs do lado do servidor para aplicações móveis, consulte a documentação para o SDK do servidor .NET ou o SDK do servidor deNode.js.

Documentação de referência

A documentação de referência para o cliente SDK está localizada aqui: Referência do cliente Azure Mobile Apps .NET. Também pode encontrar várias amostras de clientes no repositório Azure-Samples GitHub.

Plataformas Suportadas

A Plataforma .NET suporta as seguintes plataformas:

  • Xamarin Android lança para API 19 a 24 (KitKat através de Nougat)
  • Xamarin iOS lança para versões iOS 8.0 e posterior
  • Plataforma Universal do Windows
  • Windows Phone 8.1
  • Windows Phone 8.0 com exceção das aplicações Silverlight

A autenticação "servidor-flow" utiliza um WebView para a UI apresentada. Se o dispositivo não for capaz de apresentar um UI WebView, então são necessários outros métodos de autenticação. Este SDK não é, portanto, adequado para dispositivos do tipo Watch ou dispositivos igualmente restritos.

Configuração e Pré-Requisitos

Assumimos que já criou e publicou o seu projeto de backend Mobile App, que inclui pelo menos uma tabela. No código utilizado neste tópico, a tabela é nomeada TodoItem e tem as seguintes colunas: Id, Texte Complete. Esta tabela é a mesma tabela criada quando completa o quickstart Azure Mobile Apps.

O tipo de cliente dactilografado correspondente em C# é a seguinte classe:

public class TodoItem
{
    public string Id { get; set; }

    [JsonProperty(PropertyName = "text")]
    public string Text { get; set; }

    [JsonProperty(PropertyName = "complete")]
    public bool Complete { get; set; }
}

O JsonPropertyAttribute é usado para definir o mapeamento propertyName entre o campo cliente e o campo de mesa.

Para aprender a criar tabelas no backend de Aplicações Móveis, consulte o tópico SDK do Servidor .NET ou o tópico SDK do servidorNode.js. Se criou o backend da sua Aplicação Móvel no portal do Azure utilizando o QuickStart, também pode utilizar a definição de tabelas Easy no portal do Azure.

Como: Instalar o pacote SDK do cliente gerido

Utilize um dos seguintes métodos para instalar o pacote SDK do cliente gerido para aplicações móveis a partir de NuGet:

  • Visual Studio clique com o direito do seu projeto, clique em Gerir Pacotes NuGet, procure o Microsoft.Azure.Mobile.Client pacote e, em seguida, clique em Instalar.
  • Estúdio Xamarin Clique com o botão direito no seu projeto, clique em AddAdd>NuGet Packages, procure o Microsoft.Azure.Mobile.Client pacote e, em seguida, clique em Adicionar Pacote.

No seu ficheiro de atividade principal, lembre-se de adicionar a seguinte declaração:

using Microsoft.WindowsAzure.MobileServices;

Nota

Note que todos os pacotes de suporte referenciados no projeto Android têm de ter a mesma versão. O SDK tem Xamarin.Android.Support.CustomTabs dependência para a plataforma Android, por isso se o seu projeto utilizar pacotes de suporte mais recentes, precisa de instalar este pacote com a versão necessária diretamente para evitar conflitos.

Como: Trabalhar com símbolos de depurg em Visual Studio

Os símbolos do espaço de nomes Microsoft.Azure.Mobile estão disponíveis no SymbolSource. Consulte as instruções SymbolSource para integrar o SymbolSource com Visual Studio.

Criar o cliente de Aplicações Móveis

O código a seguir cria o objeto MobileServiceClient que é utilizado para aceder ao backend da sua Aplicação Móvel.

var client = new MobileServiceClient("MOBILE_APP_URL");

No código anterior, substitua-o MOBILE_APP_URL pelo URL do backend da Aplicação Móvel, que se encontra na lâmina para o seu backend da Aplicação Móvel no portal do Azure. O objeto MobileServiceClient deve ser um singleton.

Trabalhar com tabelas

A secção seguinte detalha como pesquisar e recuperar registos e modificar os dados dentro da tabela. Os seguintes tópicos são abordados:

Como: Criar uma referência de tabela

Todo o código que acede ou modifica dados numa tabela de backend funções no MobileServiceTable objeto. Obtenha uma referência à tabela chamando o método GetTable , da seguinte forma:

IMobileServiceTable<TodoItem> todoTable = client.GetTable<TodoItem>();

O objeto devolvido utiliza o modelo de serialização dactilografado. Um modelo de serialização não modelado também é suportado. O exemplo a seguir cria uma referência a uma tabela não estalada:

// Get an untyped table reference
IMobileServiceTable untypedTodoTable = client.GetTable("TodoItem");

Em consultas não escritas, deve especificar a cadeia de consulta OData subjacente.

Como: Consulta de dados a partir da sua Aplicação Móvel

Esta secção descreve como emitir consultas para o backend da Aplicação Móvel, que inclui a seguinte funcionalidade:

Nota

O tamanho da página acionada pelo servidor é aplicado para evitar que todas as linhas sejam devolvidas. O paging mantém os pedidos predefinidos para grandes conjuntos de dados de impactar negativamente o serviço. Para devolver mais de 50 linhas, utilize o método e Take o Skip método, conforme descrito nos dados de retorno em páginas.

Como: Filtrar dados devolvidos

O código que se segue ilustra como filtrar os dados, incluindo uma Where cláusula numa consulta. Devolve todos os itens de todoTable cuja Complete propriedade é igual a false. A função Onde a função aplica um predicado de filtragem de linha à consulta contra a mesa.

// This query filters out completed TodoItems and items without a timestamp.
List<TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false)
    .ToListAsync();

Pode ver o URI do pedido enviado para o backend utilizando software de inspeção de mensagens, como ferramentas de desenvolvedor de navegador ou Fiddler. Se olhar para o pedido URI, note que a cadeia de consulta é modificada:

GET /tables/todoitem?$filter=(complete+eq+false) HTTP/1.1

Este pedido de OData é traduzido numa consulta SQL pelo Server SDK:

SELECT *
    FROM TodoItem
    WHERE ISNULL(complete, 0) = 0

A função que é passada ao Where método pode ter um número arbitrário de condições.

// This query filters out completed TodoItems where Text isn't null
List<TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false && todoItem.Text != null)
    .ToListAsync();

Este exemplo seria traduzido numa consulta SQL pelo Server SDK:

SELECT *
    FROM TodoItem
    WHERE ISNULL(complete, 0) = 0
          AND ISNULL(text, 0) = 0

Esta consulta também pode ser dividida em várias cláusulas:

List<TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false)
    .Where(todoItem => todoItem.Text != null)
    .ToListAsync();

Os dois métodos são equivalentes e podem ser utilizados intercambiavelmente. A primeira opção - de concatenar vários predicados em uma consulta - é mais compacta e recomendada.

A Where cláusula suporta operações que sejam traduzidas para o subconjunto OData. As operações incluem:

  • Operadores relacionais (==, !=, <<=, =, >>=),
  • Operadores aritméticos (+, -, *, %),
  • Precisão do número (Math.Floor, Math.Ceiling),
  • Funções de corda (Comprimento, Substring, Substituição, IndexOf, StartsWith, EndsWith),
  • Propriedades da data (Ano, Mês, Dia, Hora, Minuto, Segundo),
  • Propriedades de acesso de um objeto, e
  • Expressões combinando qualquer uma destas operações.

Ao considerar o que o Server SDK suporta, pode considerar a documentação OData v3.

Como: Ordenar dados devolvidos

O código a seguir ilustra como classificar os dados incluindo uma função OrderBy ou OrderByDescending na consulta. Devolve itens de todoTable subida ordenada pelo Text campo.

// Sort items in ascending order by Text field
MobileServiceTableQuery<TodoItem> query = todoTable
                .OrderBy(todoItem => todoItem.Text)
List<TodoItem> items = await query.ToListAsync();

// Sort items in descending order by Text field
MobileServiceTableQuery<TodoItem> query = todoTable
                .OrderByDescending(todoItem => todoItem.Text)
List<TodoItem> items = await query.ToListAsync();

Como: Devolver dados em páginas

Por predefinição, o backend devolve apenas as primeiras 50 linhas. Pode aumentar o número de linhas devolvidas chamando o método Take . Utilize Take juntamente com o método Skip para solicitar uma "página" específica do conjunto de dados total devolvido pela consulta. A seguinte consulta, quando executada, devolve os três primeiros itens da tabela.

// Define a filtered query that returns the top 3 items.
MobileServiceTableQuery<TodoItem> query = todoTable.Take(3);
List<TodoItem> items = await query.ToListAsync();

A seguinte consulta revista ignora os três primeiros resultados e devolve os três resultados seguintes. Esta consulta produz a segunda "página" de dados, onde o tamanho da página é de três itens.

// Define a filtered query that skips the top 3 items and returns the next 3 items.
MobileServiceTableQuery<TodoItem> query = todoTable.Skip(3).Take(3);
List<TodoItem> items = await query.ToListAsync();

O método IncludeTotalCount solicita a contagem total de todos os registos que teriam sido devolvidos, ignorando qualquer cláusula de paging/limite especificada:

query = query.IncludeTotalCount();

Numa aplicação do mundo real, pode utilizar consultas semelhantes ao exemplo anterior com um controlo de pager ou UI comparável para navegar entre páginas.

Nota

Para anular o limite de 50 linhas num backend da Aplicação Móvel, também deve aplicar o EnableQueryAttribute ao método GET público e especificar o comportamento de paging. Quando aplicado ao método, o seguinte define as linhas máximas devolvidas a 1000:

[EnableQuery(MaxTop=1000)]

Como: Selecione colunas específicas

Pode especificar qual o conjunto de propriedades a incluir nos resultados adicionando uma cláusula Select à sua consulta. Por exemplo, o seguinte código mostra como selecionar apenas um campo e também como selecionar e formatar vários campos:

// Select one field -- just the Text
MobileServiceTableQuery<TodoItem> query = todoTable
                .Select(todoItem => todoItem.Text);
List<string> items = await query.ToListAsync();

// Select multiple fields -- both Complete and Text info
MobileServiceTableQuery<TodoItem> query = todoTable
                .Select(todoItem => string.Format("{0} -- {1}",
                    todoItem.Text.PadRight(30), todoItem.Complete ?
                    "Now complete!" : "Incomplete!"));
List<string> items = await query.ToListAsync();

Todas as funções descritas até agora são aditivas, por isso podemos continuar a acorrá-las. Cada chamada acorrentada afeta mais a consulta. Mais um exemplo:

MobileServiceTableQuery<TodoItem> query = todoTable
                .Where(todoItem => todoItem.Complete == false)
                .Select(todoItem => todoItem.Text)
                .Skip(3).
                .Take(3);
List<string> items = await query.ToListAsync();

Como: Procurar dados por ID

A função LookupAsync pode ser usada para procurar objetos da base de dados com um ID específico.

// This query filters out the item with the ID of 37BBF396-11F0-4B39-85C8-B319C729AF6D
TodoItem item = await todoTable.LookupAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D");

Como: Executar consultas não estadudas

Ao executar uma consulta utilizando um objeto de mesa não estancado, deve especificar explicitamente a cadeia de consulta OData chamando ReadAsync, como no seguinte exemplo:

// Lookup untyped data using OData
JToken untypedItems = await untypedTodoTable.ReadAsync("$filter=complete eq 0&$orderby=text");

Você recebe de volta valores JSON que você pode usar como um saco de propriedade. Para mais informações sobre jToken e Newtonsoft Json.NET, consulte o site Json.NET .

Como: Inserir dados num backend de aplicações móveis

Todos os tipos de clientes devem conter um ID de membro, que é por defeito uma cadeia. Este Id é necessário para realizar operações CRUD e para sincronização offline. O código a seguir ilustra como utilizar o método InsertAsync para inserir novas linhas numa tabela. O parâmetro contém os dados a inserir como um objeto .NET.

await todoTable.InsertAsync(todoItem);

Se um valor de ID personalizado único não for incluído no todoItem durante uma inserção, um GUID é gerado pelo servidor. Pode recuperar o ID gerado inspecionando o objeto após a chamada.

Para inserir dados não estalidos, poderá aproveitar Json.NET:

JObject jo = new JObject();
jo.Add("Text", "Hello World");
jo.Add("Complete", false);
var inserted = await table.InsertAsync(jo);

Aqui está um exemplo usando um endereço de e-mail como um id de corda único:

JObject jo = new JObject();
jo.Add("id", "myemail@emaildomain.com");
jo.Add("Text", "Hello World");
jo.Add("Complete", false);
var inserted = await table.InsertAsync(jo);

Trabalhar com valores de ID

Mobile Apps suporta valores de cadeia personalizados únicos para a coluna id da tabela. Um valor de cadeia permite que as aplicações utilizem valores personalizados, como endereços de e-mail ou nomes de utilizador para o ID. Os IDs de cordas proporcionam-lhe os seguintes benefícios:

  • As identificações são geradas sem fazer uma ida e volta à base de dados.
  • Os registos são mais fáceis de fundir a partir de diferentes tabelas ou bases de dados.
  • Os valores de IDs podem integrar-se melhor com a lógica de uma aplicação.

Quando um valor de ID de cadeia não é definido num registo inserido, o backend da Aplicação Móvel gera um valor único para o ID. Pode utilizar o método Guid.NewGuid para gerar os seus próprios valores de ID, quer no cliente, quer no backend.

JObject jo = new JObject();
jo.Add("id", Guid.NewGuid().ToString("N"));

Como: Modificar dados num backend de aplicações móveis

O código que se segue ilustra como utilizar o método UpdateAsync para atualizar um registo existente com o mesmo ID com novas informações. O parâmetro contém os dados a atualizar como um objeto .NET.

await todoTable.UpdateAsync(todoItem);

Para atualizar dados não estalidos, poderá aproveitar Json.NET da seguinte forma:

JObject jo = new JObject();
jo.Add("id", "37BBF396-11F0-4B39-85C8-B319C729AF6D");
jo.Add("Text", "Hello World");
jo.Add("Complete", false);
var inserted = await table.UpdateAsync(jo);

Um id campo deve ser especificado ao fazer uma atualização. O backend utiliza o id campo para identificar qual linha a atualizar. O id campo pode ser obtido a partir do resultado da InsertAsync chamada. Um ArgumentException é levantado se tentar atualizar um item sem fornecer o id valor.

Como: Eliminar dados num backend de aplicações móveis

O código que se segue ilustra como utilizar o método DeleteAsync para eliminar uma instância existente. O caso é identificado pelo id campo definido no todoItem.

await todoTable.DeleteAsync(todoItem);

Para eliminar dados não estalidos, poderá aproveitar Json.NET da seguinte forma:

JObject jo = new JObject();
jo.Add("id", "37BBF396-11F0-4B39-85C8-B319C729AF6D");
await table.DeleteAsync(jo);

Quando então fizer um pedido de eliminação, deve ser especificado um ID. Outras propriedades não são passadas ao serviço ou são ignoradas no serviço. O resultado de uma DeleteAsync chamada é geralmente null. A identificação a passar pode ser obtida a partir do resultado da InsertAsync chamada. A MobileServiceInvalidOperationException é lançado quando tenta apagar um item sem especificar o id campo.

Como: Usar a Concordância Otimista para a resolução de conflitos

Dois ou mais clientes podem escrever alterações ao mesmo item ao mesmo tempo. Sem a deteção de conflitos, a última escrita substituiria quaisquer atualizações anteriores. O controlo otimista da concordância pressupõe que cada transação pode efetindo e, portanto, não utiliza qualquer bloqueio de recursos. Antes de efetivo de uma transação, o controlo otimista da concordância verifica que nenhuma outra transação modificou os dados. Se os dados forem modificados, a transação de emissão é revertida.

As Aplicações Móveis suportam um controlo otimista de concordância, rastreando alterações a cada item utilizando a coluna de propriedade do version sistema que é definida para cada tabela no backend da Sua Aplicação Móvel. Cada vez que um disco é atualizado, as Aplicações Móveis definem a version propriedade para esse recorde para um novo valor. Durante cada pedido de atualização, a version propriedade do registo incluído com o pedido é comparada com a mesma propriedade para o registo no servidor. Se a versão aprovada com o pedido não corresponder ao backend, então a biblioteca do cliente levanta uma MobileServicePreconditionFailedException<T> exceção. O tipo incluído com a exceção é o registo do backend que contém a versão dos servidores do registo. A aplicação pode então utilizar esta informação para decidir se executa novamente o pedido de atualização com o valor correto version do backend para cometer alterações.

Defina uma coluna na classe de mesa para a propriedade do version sistema para permitir a concordância otimista. Por exemplo:

public class TodoItem
{
    public string Id { get; set; }

    [JsonProperty(PropertyName = "text")]
    public string Text { get; set; }

    [JsonProperty(PropertyName = "complete")]
    public bool Complete { get; set; }

    // *** Enable Optimistic Concurrency *** //
    [JsonProperty(PropertyName = "version")]
    public string Version { set; get; }
}

As aplicações que utilizam tabelas não estanifadas permitem uma concordância otimista, colocando a VersionSystemProperties bandeira na tabela da seguinte forma.

//Enable optimistic concurrency by retrieving version
todoTable.SystemProperties |= MobileServiceSystemProperties.Version;

Além de permitir uma concordância otimista, também deve apanhar a exceção no seu código ao MobileServicePreconditionFailedException<T> ligar para UpdateAsync. Resolva o conflito aplicando o correto version ao registo atualizado e ligue para UpdateAsync com o registo resolvido. O seguinte código mostra como resolver um conflito de escrita uma vez detetado:

private async void UpdateToDoItem(TodoItem item)
{
    MobileServicePreconditionFailedException<TodoItem> exception = null;

    try
    {
        //update at the remote table
        await todoTable.UpdateAsync(item);
    }
    catch (MobileServicePreconditionFailedException<TodoItem> writeException)
    {
        exception = writeException;
    }

    if (exception != null)
    {
        // Conflict detected, the item has changed since the last query
        // Resolve the conflict between the local and server item
        await ResolveConflict(item, exception.Item);
    }
}


private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem)
{
    //Ask user to choose the resolution between versions
    MessageDialog msgDialog = new MessageDialog(
        String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n",
        serverItem.Text, localItem.Text),
        "CONFLICT DETECTED - Select a resolution:");

    UICommand localBtn = new UICommand("Commit Local Text");
    UICommand ServerBtn = new UICommand("Leave Server Text");
    msgDialog.Commands.Add(localBtn);
    msgDialog.Commands.Add(ServerBtn);

    localBtn.Invoked = async (IUICommand command) =>
    {
        // To resolve the conflict, update the version of the item being committed. Otherwise, you will keep
        // catching a MobileServicePreConditionFailedException.
        localItem.Version = serverItem.Version;

        // Updating recursively here just in case another change happened while the user was making a decision
        UpdateToDoItem(localItem);
    };

    ServerBtn.Invoked = async (IUICommand command) =>
    {
        RefreshTodoItems();
    };

    await msgDialog.ShowAsync();
}

Para mais informações, consulte o Sincronização de Dados offline no tópico Azure Mobile Apps.

Como: Ligar os dados das Aplicações Móveis a uma interface de utilizador Windows

Esta secção mostra como exibir objetos de dados devolvidos utilizando elementos UI numa aplicação Windows. O seguinte código de exemplo liga-se à origem da lista com uma consulta para itens incompletos. O MobileServiceCollection cria uma coleção de ligação para aplicações móveis.

// This query filters out completed TodoItems.
MobileServiceCollection<TodoItem, TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false)
    .ToCollectionAsync();

// itemsControl is an IEnumerable that could be bound to a UI list control
IEnumerable itemsControl  = items;

// Bind this to a ListBox
ListBox lb = new ListBox();
lb.ItemsSource = items;

Alguns controlos no suporte gerido ao tempo de execução uma interface chamada ISupportIncrementalLoading. Esta interface permite que os controlos solicitem dados extra quando o utilizador desloca. Existe suporte incorporado para esta interface para aplicações de Windows universal via MobileServiceIncrementalLoadingCollection, que lida automaticamente com as chamadas a partir dos controlos. Utilizar MobileServiceIncrementalLoadingCollection em Windows aplicações da seguinte forma:

MobileServiceIncrementalLoadingCollection<TodoItem,TodoItem> items;
items = todoTable.Where(todoItem => todoItem.Complete == false).ToIncrementalLoadingCollection();

ListBox lb = new ListBox();
lb.ItemsSource = items;

Para utilizar a nova coleção nas aplicações Windows Phone 8 e "Silverlight", utilize os ToCollection métodos de extensão ligados IMobileServiceTableQuery<T> e IMobileServiceTable<T>. Para carregar dados, ligue LoadMoreItemsAsync().

MobileServiceCollection<TodoItem, TodoItem> items = todoTable.Where(todoItem => todoItem.Complete==false).ToCollection();
await items.LoadMoreItemsAsync();

Quando utiliza a coleção criada por chamada ToCollectionAsync ou ToCollection, obtém-se uma coleção que pode ser ligada aos controlos de UI. Esta coleção está ciente de paging. Uma vez que a recolha está a carregar dados da rede, o carregamento às vezes falha. Para lidar com estas falhas, substitua o OnException método MobileServiceIncrementalLoadingCollection para lidar com exceções resultantes de chamadas para LoadMoreItemsAsync.

Considere se a sua mesa tem muitos campos, mas só quer exibir alguns deles no seu controlo. Pode utilizar a orientação na secção anterior "Selecione colunas específicas" para selecionar colunas específicas para visualizar na UI.

Alterar o tamanho da página

A Azure Mobile Apps devolve um máximo de 50 itens por padrão. Pode alterar o tamanho da paging aumentando o tamanho máximo da página tanto no cliente como no servidor. Para aumentar o tamanho da página solicitada, especifique PullOptions ao utilizar PullAsync():

PullOptions pullOptions = new PullOptions
    {
        MaxPageSize = 100
    };

Assumindo que fez o PageSize igual ou superior a 100 dentro do servidor, um pedido devolve até 100 itens.

Trabalhar com tabelas offline

As tabelas offline utilizam uma loja SQLite local para armazenar dados para utilização quando offline. Todas as operações de mesa são feitas contra a loja LOCAL SQLite em vez da loja de servidores remotos. Para criar uma tabela offline, prepare primeiro o seu projeto:

  1. Em Visual Studio, clique com Visual Studio à direita na solução >Gerir pacotes NuGet para solução..., em seguida, procurar e instalar o pacote Microsoft.Azure.Mobile.Client.SQLiteStore NuGet para todos os projetos na solução.

  2. (Opcional) Para suportar Windows dispositivos, instale um dos seguintes pacotes de tempo de execução SQLite:

  3. (Opcional). Para Windows dispositivos, clique em ReferencesAdd>Reference..., expanda as extensõesde pasta > Windows e, em seguida, ative o SQLite apropriado para Windows SDK juntamente com o Tempo de Execução Visual C++ 2013 para Windows SDK. Os nomes SQLite SDK variam ligeiramente com cada Windows plataforma.

Antes de ser criada uma referência de mesa, a loja local deve ser preparada:

var store = new MobileServiceSQLiteStore(Constants.OfflineDbPath);
store.DefineTable<TodoItem>();

//Initializes the SyncContext using the default IMobileServiceSyncHandler.
await this.client.SyncContext.InitializeAsync(store);

A inicialização da loja é normalmente feita imediatamente após a criação do cliente. O OfflineDbPath deve ser um nome de ficheiro adequado para utilização em todas as plataformas que suporta. Se o caminho é um caminho totalmente qualificado (isto é, começa com um corte), então esse caminho é usado. Se o caminho não estiver totalmente qualificado, o ficheiro é colocado num local específico da plataforma.

  • Para dispositivos iOS e Android, o caminho padrão é a pasta "Ficheiros Pessoais".
  • Para Windows dispositivos, o caminho predefinido é a pasta "AppData" específica para aplicações.

Uma referência de tabela pode ser obtida utilizando o GetSyncTable<> método:

var table = client.GetSyncTable<TodoItem>();

Não é necessário autenticar para utilizar uma tabela offline. Só precisa de autenticar quando estiver a comunicar com o serviço de backend.

Sincronizando uma tabela offline

As tabelas offline não são sincronizadas com o backend por defeito. A sincronização é dividida em duas partes. Pode empurrar alterações separadamente do descarregamento de novos itens. Aqui está um método típico de sincronização:

public async Task SyncAsync()
{
    ReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;

    try
    {
        await this.client.SyncContext.PushAsync();

        await this.todoTable.PullAsync(
            //The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
            //Use a different query name for each unique query in your program
            "allTodoItems",
            this.todoTable.CreateQuery());
    }
    catch (MobileServicePushFailedException exc)
    {
        if (exc.PushResult != null)
        {
            syncErrors = exc.PushResult.Errors;
        }
    }

    // Simple error/conflict handling. A real application would handle the various errors like network conditions,
    // server conflicts and others via the IMobileServiceSyncHandler.
    if (syncErrors != null)
    {
        foreach (var error in syncErrors)
        {
            if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
            {
                //Update failed, reverting to server's copy.
                await error.CancelAndUpdateItemAsync(error.Result);
            }
            else
            {
                // Discard local change.
                await error.CancelAndDiscardItemAsync();
            }

            Debug.WriteLine(@"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
        }
    }
}

Se o primeiro argumento PullAsync for nulo, então não é utilizada sincronização incremental. Cada operação de sincronização recupera todos os registos.

O SDK executa um implícito PushAsync() antes de puxar os registos.

O tratamento de conflitos acontece num PullAsync() método. Pode lidar com conflitos da mesma forma que as tabelas online. O conflito é produzido quando PullAsync() é chamado em vez de durante a inserção, atualização ou eliminação. Se vários conflitos acontecerem, eles são agregados em um único MobileServicePushFailedException. Manuseie cada falha separadamente.

Trabalhar com uma API personalizada

Uma API personalizada permite-lhe definir pontos finais personalizados que expõem a funcionalidade do servidor que não mapeia para uma operação de inserção, atualização, exclusão ou leitura. Ao utilizar uma API personalizada, pode ter mais controlo sobre mensagens, incluindo ler e definir cabeçalhos de mensagens HTTP e definir um formato corporal de mensagens diferente do JSON.

Você chama uma API personalizada, chamando um dos métodos InvokeApiAsync no cliente. Por exemplo, a seguinte linha de código envia um pedido DEM para a API completa All no backend:

var result = await client.InvokeApiAsync<MarkAllResult>("completeAll", System.Net.Http.HttpMethod.Post, null);

Este formulário é uma chamada de método dactilografada e requer que o tipo de retorno MarkAllResult seja definido. Os métodos dactilografados e não-tipitados são suportados.

O método InvokeApiAsync() prepara -/api/' para a API que deseja ligar a menos que a API comece com um "/". Por exemplo:

  • InvokeApiAsync("completeAll",...) chamadas /api/completeTo no backend
  • InvokeApiAsync("/.auth/me",...) chamadas /.auth/me no backend

Você pode usar InvokeApiAsync para ligar para qualquer WebAPI, incluindo os WebAPIs que não são definidos com Aplicações Móveis Azure. Quando utilizar o InvokeApiAsync(), os cabeçalhos apropriados, incluindo cabeçalhos de autenticação, são enviados com o pedido.

Autenticar utilizadores

As Aplicações Móveis suportam a autenticação e a autorização de utilizadores de aplicações utilizando vários fornecedores de identidade externas: Facebook, Google, Microsoft Account, Twitter e Azure Ative Directory. Pode definir permissões em tabelas para restringir o acesso a operações específicas apenas a utilizadores autenticados. Também pode utilizar a identidade dos utilizadores autenticados para implementar regras de autorização em scripts de servidores. Para obter mais informações, consulte o tutorial Add authentication to your app (Adicionar autenticação à sua aplicação).

Dois fluxos de autenticação são suportados: fluxo gerido pelo cliente e gerido pelo servidor . O fluxo gerido pelo servidor proporciona a experiência de autenticação mais simples, uma vez que se baseia na interface de autenticação web do fornecedor. O fluxo gerido pelo cliente permite uma integração mais profunda com capacidades específicas do dispositivo, uma vez que se baseia em SDKs específicos do fornecedor.

Nota

Recomendamos a utilização de um fluxo gerido pelo cliente nas suas aplicações de produção.

Para configurar a autenticação, tem de registar a sua aplicação junto de um ou mais fornecedores de identidade. O fornecedor de identidade gera um ID do cliente e um segredo de cliente para a sua aplicação. Estes valores são então definidos no seu backend para permitir Serviço de Aplicações do Azure autenticação/autorização. Para mais informações, siga as instruções detalhadas no tutorial Adicione autenticação à sua aplicação.

Os seguintes tópicos são abordados nesta secção:

Autenticação gerida pelo cliente

A sua aplicação pode contactar o fornecedor de identidade de forma independente e, em seguida, fornecer o token devolvido durante o login com o seu backend. Este fluxo de cliente permite-lhe fornecer uma única experiência de login para os utilizadores ou para recuperar dados adicionais do utilizador do fornecedor de identidade. A autenticação do fluxo do cliente é preferível a usar um fluxo de servidor, uma vez que o fornecedor de identidade SDK fornece uma sensação de UX mais nativa e permite uma personalização adicional.

São fornecidos exemplos para os seguintes padrões de autenticação do fluxo de clientes:

Autenticar os utilizadores com a Biblioteca de Autenticação de Diretório Ativo

Pode utilizar a Biblioteca de Autenticação do Diretório Ativo (ADAL) para iniciar a autenticação do utilizador a partir do cliente utilizando Azure Ative Directory autenticação.

  1. Configure o backend da sua aplicação móvel para AAD login seguindo o como configurar Serviço de Aplicações para tutorial de login do Ative Directory. Certifique-se de completar o passo opcional de registo de uma aplicação de cliente nativo.

  2. Em Visual Studio ou Xamarin Studio, abra o seu projeto e adicione uma referência ao Microsoft.IdentityModel.Clients.ActiveDirectory pacote NuGet. Ao pesquisar, inclua versões pré-lançamento.

  3. Adicione o seguinte código à sua aplicação, de acordo com a plataforma que está a utilizar. Em cada uma, faça as seguintes substituições:

    • Substitua INSERT-AUTHORITY-HERE pelo nome do arrendatário em que aprovisionou o seu pedido. O formato deve ser https://login.microsoftonline.com/contoso.onmicrosoft.com. Este valor pode ser copiado a partir do separador Domínio no seu Azure Ative Directory no portal do Azure.

    • Substitua o INSERT-RESOURCE-ID-HERE pelo ID do cliente para o seu backend da aplicação móvel. Pode obter o ID do cliente a partir do separador Avançadosob Azure Ative Directory Definições no portal.

    • Substitua INSERT-CLIENT-ID-HERE pelo ID do cliente que copiou da aplicação do cliente nativo.

    • Substitua o INSERT-REDIRECT-URI-HERE pelo ponto final do seu site /.auth/login/done, utilizando o esquema HTTPS. Este valor deve ser semelhante a https://contoso.azurewebsites.net/.auth/login/done.

      Segue-se o código necessário para cada plataforma:

      Windows:

      private MobileServiceUser user;
      private async Task AuthenticateAsync()
      {
      
         string authority = "INSERT-AUTHORITY-HERE";
         string resourceId = "INSERT-RESOURCE-ID-HERE";
         string clientId = "INSERT-CLIENT-ID-HERE";
         string redirectUri = "INSERT-REDIRECT-URI-HERE";
         while (user == null)
         {
             string message;
             try
             {
                 AuthenticationContext ac = new AuthenticationContext(authority);
                 AuthenticationResult ar = await ac.AcquireTokenAsync(resourceId, clientId,
                     new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto, false) );
                 JObject payload = new JObject();
                 payload["access_token"] = ar.AccessToken;
                 user = await App.MobileService.LoginAsync(
                     MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
                 message = string.Format("You are now logged in - {0}", user.UserId);
             }
             catch (InvalidOperationException)
             {
                 message = "You must log in. Login Required";
             }
             var dialog = new MessageDialog(message);
             dialog.Commands.Add(new UICommand("OK"));
             await dialog.ShowAsync();
         }
      }
      

      Xamarin.iOS

      private MobileServiceUser user;
      private async Task AuthenticateAsync(UIViewController view)
      {
      
         string authority = "INSERT-AUTHORITY-HERE";
         string resourceId = "INSERT-RESOURCE-ID-HERE";
         string clientId = "INSERT-CLIENT-ID-HERE";
         string redirectUri = "INSERT-REDIRECT-URI-HERE";
         try
         {
             AuthenticationContext ac = new AuthenticationContext(authority);
             AuthenticationResult ar = await ac.AcquireTokenAsync(resourceId, clientId,
                 new Uri(redirectUri), new PlatformParameters(view));
             JObject payload = new JObject();
             payload["access_token"] = ar.AccessToken;
             user = await client.LoginAsync(
                 MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
         }
         catch (Exception ex)
         {
             Console.Error.WriteLine(@"ERROR - AUTHENTICATION FAILED {0}", ex.Message);
         }
      }
      

      Xamarin.Android

      private MobileServiceUser user;
      private async Task AuthenticateAsync()
      {
      
         string authority = "INSERT-AUTHORITY-HERE";
         string resourceId = "INSERT-RESOURCE-ID-HERE";
         string clientId = "INSERT-CLIENT-ID-HERE";
         string redirectUri = "INSERT-REDIRECT-URI-HERE";
         try
         {
             AuthenticationContext ac = new AuthenticationContext(authority);
             AuthenticationResult ar = await ac.AcquireTokenAsync(resourceId, clientId,
                 new Uri(redirectUri), new PlatformParameters(this));
             JObject payload = new JObject();
             payload["access_token"] = ar.AccessToken;
             user = await client.LoginAsync(
                 MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
         }
         catch (Exception ex)
         {
             AlertDialog.Builder builder = new AlertDialog.Builder(this);
             builder.SetMessage(ex.Message);
             builder.SetTitle("You must log in. Login Required");
             builder.Create().Show();
         }
      }
      protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
      {
      
         base.OnActivityResult(requestCode, resultCode, data);
         AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
      }
      

Sign-On único usando um token do Facebook ou Google

Pode utilizar o fluxo do cliente como mostrado neste corte para facebook ou Google.

var token = new JObject();
// Replace access_token_value with actual value of your access token obtained
// using the Facebook or Google SDK.
token.Add("access_token", "access_token_value");

private MobileServiceUser user;
private async Task AuthenticateAsync()
{
    while (user == null)
    {
        string message;
        try
        {
            // Change MobileServiceAuthenticationProvider.Facebook
            // to MobileServiceAuthenticationProvider.Google if using Google auth.
            user = await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
            message = string.Format("You are now logged in - {0}", user.UserId);
        }
        catch (InvalidOperationException)
        {
            message = "You must log in. Login Required";
        }

        var dialog = new MessageDialog(message);
        dialog.Commands.Add(new UICommand("OK"));
        await dialog.ShowAsync();
    }
}

Autenticação gerida pelo servidor

Assim que tiver registado o seu fornecedor de identidade, ligue para o método LoginAsync no [MobileServiceClient] com o valor MobileServiceAuthenticationProvider do seu fornecedor. Por exemplo, o seguinte código inicia um início de sção de fluxo de servidor utilizando o Facebook.

private MobileServiceUser user;
private async System.Threading.Tasks.Task Authenticate()
{
    while (user == null)
    {
        string message;
        try
        {
            user = await client
                .LoginAsync(MobileServiceAuthenticationProvider.Facebook);
            message =
                string.Format("You are now logged in - {0}", user.UserId);
        }
        catch (InvalidOperationException)
        {
            message = "You must log in. Login Required";
        }

        var dialog = new MessageDialog(message);
        dialog.Commands.Add(new UICommand("OK"));
        await dialog.ShowAsync();
    }
}

Se estiver a utilizar um fornecedor de identidade diferente do Facebook, altere o valor do MobileServiceAuthenticationProvider para o valor para o seu fornecedor.

Num fluxo de servidor, Serviço de Aplicações do Azure gere o fluxo de autenticação OAuth exibindo a página de entrada do fornecedor selecionado. Uma vez que o fornecedor de identidade retorne, Serviço de Aplicações do Azure gera um token de autenticação Serviço de Aplicações. O método LoginAsync devolve um MobileServiceUser, que fornece tanto o UserId do utilizador autenticado como o MobileServiceAuthenticationToken, como um token web JSON (JWT). Este token pode ser colocado em cache e reutilizado até expirar. Para mais informações, consulte Colocação em Cache o token de autenticação.

Ao utilizar xamarin (Android ou iOS), Xamarin.Essentials WebAuthenticator é usado. Tem de passar o contexto padrão (Android) ou UIViewController (iOS) para o LoginAsync método. Além disso, tem de lidar com a devolução do autenticador da web. No Android, isto é tratado dentro de MainActivity.cs:

public override void OnResume()
{
    base.OnResume();
    Xamarin.Essentials.Platform.OnResume();
}

No iOS, isto é tratado dentro do AppDeegate.cs':

public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
    if (client.ResumeWithURL(app, url, options))
        return true;
    return base.OpenUrl(app, url, options);
}

Colocação em Cache o símbolo de autenticação

Em alguns casos, a chamada para o método de login pode ser evitada após a primeira autenticação bem sucedida, armazenando o token de autenticação do fornecedor. Microsoft Store e aplicações UWP podem usar o PasswordVault para cache o token de autenticação atual após um sismo bem-sucedido, da seguinte forma:

await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook);

PasswordVault vault = new PasswordVault();
vault.Add(new PasswordCredential("Facebook", client.currentUser.UserId,
    client.currentUser.MobileServiceAuthenticationToken));

O valor UserId é armazenado como o Nome de Utilizador da credencial e o token é armazenado como palavra-passe. Nas start-ups subsequentes, pode verificar as credenciais passwordVault . O exemplo a seguir utiliza credenciais em cache quando são encontradas, e de outra forma tenta autenticar novamente com o backend:

// Try to retrieve stored credentials.
var creds = vault.FindAllByResource("Facebook").FirstOrDefault();
if (creds != null)
{
    // Create the current user from the stored credentials.
    client.currentUser = new MobileServiceUser(creds.UserName);
    client.currentUser.MobileServiceAuthenticationToken =
        vault.Retrieve("Facebook", creds.UserName).Password;
}
else
{
    // Regular login flow and cache the token as shown above.
}

Quando assinar um utilizador, deve também remover a credencial armazenada, da seguinte forma:

client.Logout();
vault.Remove(vault.Retrieve("Facebook", client.currentUser.UserId));

As aplicações Xamarin usam as APIs Xamarin.Auth para armazenar credenciais de forma segura num objeto de Conta . Para um exemplo de utilização destas APIs, consulte o ficheiro de código .cs AuthStore na amostra de partilha de fotos ContosoMoments.

Quando utiliza a autenticação gerida pelo cliente, também pode cache o token de acesso obtido pelo seu fornecedor, como o Facebook ou o Twitter. Este token pode ser fornecido para solicitar um novo token de autenticação a partir do backend, da seguinte forma:

var token = new JObject();
// Replace <your_access_token_value> with actual value of your access token
token.Add("access_token", "<your_access_token_value>");

// Authenticate using the access token.
await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);

Notificações Push

Os seguintes tópicos abrangem notificações push:

Como: Registar-se para notificações push

O cliente de Aplicações Móveis permite-lhe registar-se para notificações push com os Hubs de Notificação Azure. Ao registar-se, obtém uma pega que obtém do Serviço de Notificação push (PNS) específico da plataforma. Em seguida, forneça este valor juntamente com quaisquer tags quando criar o registo. O seguinte código regista a sua aplicação Windows para notificações push com o Serviço de Notificação de Windows (WNS):

private async void InitNotificationsAsync()
{
    // Request a push notification channel.
    var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

    // Register for notifications using the new channel.
    await MobileService.GetPush().RegisterNativeAsync(channel.Uri, null);
}

Se estiver a empurrar para a WNS, deve obter um Microsoft Store pacote SID. Para obter mais informações sobre Windows aplicações, incluindo como se registar para registos de modelos, consulte adicionar notificações push à sua aplicação.

O pedido de etiquetas do cliente não é suportado. Os pedidos de etiqueta são silenciosamente retirados do registo. Se desejar registar o seu dispositivo com etiquetas, crie uma API personalizada que utilize a API de Centros de Notificação para efetuar o registo em seu nome. Ligue para a API personalizada em vez do RegisterNativeAsync() método.

Como: Obter um pacote de Microsoft Store SID

Um pacote SID é necessário para permitir notificações push em aplicações Microsoft Store. Para receber um pacote SID, registe a sua candidatura com o Microsoft Store.

Para obter este valor:

  1. Em Visual Studio Explorador de Soluções, clique com o botão direito no projeto de aplicação Microsoft Store, clique na App StoreAssociate> com a Loja....
  2. No assistente, clique em 'Seguinte', inscreva-se na sua conta Microsoft, escreva um nome para a sua aplicação em Reserva um novo nome de aplicação e, em seguida, clique em Reserva.
  3. Depois de o registo da aplicação ter sido criado com sucesso, selecione o nome da aplicação, clique em 'Next' e, em seguida, clique em Associate.
  4. Faça login no Windows Dev Center utilizando a sua Conta Microsoft. Nas minhas aplicações, clique no registo da aplicação que criou.
  5. Clique na identidade do App managementApp> e, em seguida, desloque-se para baixo para encontrar o seu pacote SID.

Muitas utilizações do pacote SID tratam-no como um URI, caso em que você precisa usar ms-app:// como o esquema. Tome nota da versão do seu pacote SID formado pela concatenação deste valor como prefixo.

As aplicações Xamarin requerem algum código adicional para poderem registar uma aplicação em execução nas plataformas iOS ou Android. Para mais informações, consulte o tópico para a sua plataforma:

Como: Registar modelos de pressão para enviar notificações de plataformas cruzadas

Para registar modelos, utilize o RegisterAsync() método com os modelos, da seguinte forma:

JObject templates = myTemplates();
MobileService.GetPush().RegisterAsync(channel.Uri, templates);

Os seus modelos devem ser JObject tipos e podem conter vários modelos no seguinte formato JSON:

public JObject myTemplates()
{
    // single template for Windows Notification Service toast
    var template = "<toast><visual><binding template=\"ToastText01\"><text id=\"1\">$(message)</text></binding></visual></toast>";

    var templates = new JObject
    {
        ["generic-message"] = new JObject
        {
            ["body"] = template,
            ["headers"] = new JObject
            {
                ["X-WNS-Type"] = "wns/toast"
            },
            ["tags"] = new JArray()
        },
        ["more-templates"] = new JObject {...}
    };
    return templates;
}

O método RegisterAsync também aceita azulejos secundários:

MobileService.GetPush().RegisterAsync(string channelUri, JObject templates, JObject secondaryTiles);

Todas as etiquetas são retiradas durante o registo por segurança. Para adicionar tags a instalações ou modelos dentro de instalações, consulte [Trabalhar com o servidor de backend SDK .NET para aplicações móveis Azure].

Para enviar notificações utilizando estes modelos registados, consulte as APIs dos Centros de Notificação.

Tópicos Diversos

Como: Lidar com erros

Quando ocorre um erro no backend, o cliente SDK levanta um MobileServiceInvalidOperationException. O exemplo a seguir mostra como lidar com uma exceção que é devolvida pelo backend:

private async void InsertTodoItem(TodoItem todoItem)
{
    // This code inserts a new TodoItem into the database. When the operation completes
    // and App Service has assigned an Id, the item is added to the CollectionView
    try
    {
        await todoTable.InsertAsync(todoItem);
        items.Add(todoItem);
    }
    catch (MobileServiceInvalidOperationException e)
    {
        // Handle error
    }
}

Outro exemplo de lidar com as condições de erro pode ser encontrado na Amostra de Ficheiros de Aplicações Móveis. O exemplo LoggingHandler fornece um manipulador de delegado de registo para registar os pedidos que estão a ser feitos para o backend.

Como: Personalizar cabeçalhos de pedido

Para suportar o seu cenário específico de aplicação, poderá necessitar de personalizar a comunicação com o backend da Aplicação Móvel. Por exemplo, pode querer adicionar um cabeçalho personalizado a cada pedido de saída ou até mesmo alterar códigos de estado de resposta. Pode utilizar um DelegadoHandler personalizado, como no seguinte exemplo:

public async Task CallClientWithHandler()
{
    MobileServiceClient client = new MobileServiceClient("AppUrl", new MyHandler());
    IMobileServiceTable<TodoItem> todoTable = client.GetTable<TodoItem>();
    var newItem = new TodoItem { Text = "Hello world", Complete = false };
    await todoTable.InsertAsync(newItem);
}

public class MyHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage>
        SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Change the request-side here based on the HttpRequestMessage
        request.Headers.Add("x-my-header", "my value");

        // Do the request
        var response = await base.SendAsync(request, cancellationToken);

        // Change the response-side here based on the HttpResponseMessage

        // Return the modified response
        return response;
    }
}