Udostępnij za pośrednictwem


Jak używać biblioteki klienta usługi Azure Mobile Apps w wersji 4.2.0 dla platformy .NET

Nuta

Ten produkt jest wycofany. Aby zastąpić projekty przy użyciu platformy .NET 8 lub nowszej, zobacz bibliotekę datasync zestawu narzędzi Community Toolkit.

W tym przewodniku przedstawiono sposób wykonywania typowych scenariuszy przy użyciu biblioteki klienta platformy .NET dla usługi Azure Mobile Apps. Użyj biblioteki klienta platformy .NET w aplikacjach systemu Windows (WPF, UWP) lub Xamarin (natywnych lub formularzy). Jeśli dopiero zaczynasz korzystać z usługi Azure Mobile Apps, rozważ najpierw ukończenie Przewodnik Szybki start dotyczący platformy Xamarin.Forms samouczka.

Ostrzeżenie

W tym artykule opisano informacje dotyczące wersji 4.2.0 biblioteki, która jest zastępowana przez bibliotekę v5.0.0. Aby uzyskać najbardziej aktualne informacje, zobacz artykuł dotyczący najnowszej wersji

Obsługiwane platformy

Biblioteka klienta platformy .NET obsługuje platformę .NET Standard 2.0 i następujące platformy:

  • Xamarin.Android z poziomu interfejsu API 19 do poziomu 30 interfejsu API.
  • Xamarin.iOS w wersji 8.0 do 14.3.
  • Platforma uniwersalna systemu Windows tworzy 16299 i nowsze.
  • Dowolna aplikacja .NET Standard 2.0.

Uwierzytelnianie "przepływ serwera" używa elementu WebView dla przedstawionego interfejsu użytkownika i może nie być dostępne na każdej platformie. Jeśli nie jest dostępna, musisz podać uwierzytelnianie "klient-przepływ". Ta biblioteka klienta nie jest odpowiednia dla czynników formularzy zegarka ani IoT podczas korzystania z uwierzytelniania.

Konfiguracja i wymagania wstępne

Załóżmy, że projekt zaplecza usługi Azure Mobile Apps został już utworzony i opublikowany, który zawiera co najmniej jedną tabelę. W kodzie użytym w tym temacie tabela nosi nazwę TodoItem i ma ciąg Id, a Text pola oraz kolumnę Complete wartości logicznej. Ta tabela jest tą samą tabelą utworzoną podczas wykonywania Przewodnik Szybki start.

Odpowiedni typ po stronie klienta w języku C# to ta klasa:

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

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

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

JsonPropertyAttribute służy do definiowania PropertyName mapowania między polem klienta a polem tabeli.

Aby dowiedzieć się, jak tworzyć tabele w zapleczu usługi Mobile Apps, zobacz temat .NET Server SDK tematu zestawu SDK serwera Node.js server.

Instalowanie pakietu zestawu SDK klienta zarządzanego

Kliknij prawym przyciskiem myszy projekt, naciśnij Zarządzaj pakietami NuGet, wyszukaj pakiet Microsoft.Azure.Mobile.Client, a następnie naciśnij przycisk Zainstaluj. W przypadku możliwości trybu offline zainstaluj również pakiet Microsoft.Azure.Mobile.Client.SQLiteStore.

Tworzenie klienta usługi Azure Mobile Apps

Poniższy kod tworzy obiekt MobileServiceClient używany do uzyskiwania dostępu do zaplecza aplikacji mobilnej.

var client = new MobileServiceClient("MOBILE_APP_URL");

W poprzednim kodzie zastąp MOBILE_APP_URL adresem URL zaplecza usługi App Service. Obiekt MobileServiceClient powinien być pojedynczym obiektem.

Praca z tabelami

W poniższej sekcji opisano sposób wyszukiwania i pobierania rekordów oraz modyfikowania danych w tabeli. Omówiono następujące tematy:

Tworzenie odwołania do tabeli

Cały kod, który uzyskuje dostęp do lub modyfikuje dane w tabeli zaplecza, wywołuje funkcje obiektu MobileServiceTable. Uzyskaj odwołanie do tabeli, wywołując metodę GetTable w następujący sposób:

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

Zwrócony obiekt używa modelu serializacji typizowanej. Obsługiwany jest również nietypowy model serializacji. Poniższy przykład tworzy odwołanie do nietypowej tabeli:

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

W nietypowych zapytaniach należy określić źródłowy ciąg zapytania OData.

Wykonywanie zapytań dotyczących danych z aplikacji mobilnej

W tej sekcji opisano sposób wystawiania zapytań do zaplecza aplikacji mobilnej, która obejmuje następujące funkcje:

Nuta

Rozmiar strony opartej na serwerze jest wymuszany, aby zapobiec zwracaniu wszystkich wierszy. Stronicowanie utrzymuje domyślne żądania dla dużych zestawów danych z negatywnego wpływu na usługę. Aby zwrócić więcej niż 50 wierszy, użyj metody Skip i Take, zgodnie z opisem w Zwracanie danych na stronach.

Filtr zwracanych danych

Poniższy kod ilustruje sposób filtrowania danych przez dołączenie klauzuli Where w zapytaniu. Zwraca wszystkie elementy z todoTable, których właściwość Complete jest równa false. Funkcja Where stosuje predykat filtrowania wierszy do zapytania względem tabeli.

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

Identyfikator URI żądania wysłanego do zaplecza można wyświetlić przy użyciu oprogramowania do inspekcji komunikatów, takiego jak narzędzia deweloperskie przeglądarki lub Fiddler. Jeśli spojrzysz na identyfikator URI żądania, zwróć uwagę, że ciąg zapytania został zmodyfikowany:

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

To żądanie OData jest tłumaczone na zapytanie SQL przez zestaw SDK serwera:

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

Funkcja przekazywana do metody Where może mieć dowolną liczbę warunków.

// 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();

Ten przykład zostanie przetłumaczony na zapytanie SQL przez zestaw SDK serwera:

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

To zapytanie można również podzielić na wiele klauzul:

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

Dwie metody są równoważne i mogą być używane zamiennie. Pierwsza opcja — łączenia wielu predykatów w jednym zapytaniu — jest bardziej kompaktowa i zalecana.

Klauzula Where obsługuje operacje, które można przetłumaczyć na podzbiór OData. Operacje obejmują:

  • Operatory relacyjne (==, !=, <, <=, >, >=),
  • Operatory arytmetyczne (+, -, /, *, %),
  • Precyzja liczb (Math.Floor, Math.Ceiling),
  • Funkcje ciągów (Length, Substring, Replace, IndexOf, StartsWith, EndsWith),
  • Właściwości daty (Year, Month, Day, Hour, Minute, Second),
  • Uzyskiwanie dostępu do właściwości obiektu i
  • Wyrażenia łączące dowolną z tych operacji.

Biorąc pod uwagę obsługę zestawu SDK serwera, możesz rozważyć dokumentacji OData v3.

Sortowanie zwróconych danych

Poniższy kod ilustruje sposób sortowania danych, uwzględniając funkcję OrderBy lub OrderByDescending w zapytaniu. Zwraca elementy z todoTable posortowane rosnąco według pola Text.

// 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();

Zwracanie danych na stronach

Domyślnie zaplecze zwraca tylko pierwsze 50 wierszy. Liczbę zwracanych wierszy można zwiększyć, wywołując metodę Take. Użyj Take wraz z metodą Skip, aby zażądać określonej "strony" całkowitego zestawu danych zwróconego przez zapytanie. Następujące zapytanie po wykonaniu zwraca trzy najważniejsze elementy w tabeli.

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

Poniższe poprawione zapytanie pomija pierwsze trzy wyniki i zwraca kolejne trzy wyniki. To zapytanie tworzy drugą "stronę" danych, gdzie rozmiar strony to trzy elementy.

// 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();

Metoda IncludeTotalCount żąda łącznej liczby wszystkich rekordów, które zostałyby zwrócone, ignorując dowolną klauzulę stronicowania/limitu określoną:

query = query.IncludeTotalCount();

W rzeczywistej aplikacji można użyć zapytań podobnych do poprzedniego przykładu z kontrolką pager lub porównywalnym interfejsem użytkownika, aby nawigować między stronami.

Nuta

Aby zastąpić limit 50 wierszy w zapleczu aplikacji mobilnej, należy również zastosować EnableQueryAttribute do publicznej metody GET i określić zachowanie stronicowania. Po zastosowaniu do metody następujące ustawia maksymalnie zwracane wiersze na 1000:

[EnableQuery(MaxTop=1000)]

Wybieranie określonych kolumn

Możesz określić, który zestaw właściwości uwzględnić w wynikach, dodając do zapytania klauzulę Select. Na przykład poniższy kod pokazuje, jak wybrać tylko jedno pole, a także jak wybrać i sformatować wiele pól:

// 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();

Wszystkie opisane do tej pory funkcje są addytywne, więc możemy zachować ich łańcuch. Każde wywołanie łańcuchowe ma wpływ na więcej zapytania. Jeszcze jeden przykład:

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

Wyszukiwanie danych według identyfikatora

Funkcja LookupAsync może służyć do wyszukiwania obiektów z bazy danych o określonym identyfikatorze.

// 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");

Wykonywanie nietypowych zapytań

Podczas wykonywania zapytania przy użyciu nietypowego obiektu tabeli należy jawnie określić ciąg zapytania OData, wywołując readAsync, jak w poniższym przykładzie:

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

Zwracasz wartości JSON, których można użyć jak torba właściwości. Aby uzyskać więcej informacji na temat plików JToken i Newtonsoft Json, zobacz witrynę Newtonsoft JSON.

Wstawianie danych

Wszystkie typy klientów muszą zawierać element członkowski o nazwie id, który jest domyślnie ciągiem. Ten identyfikator jest wymagany do wykonywania operacji CRUD i synchronizacji w trybie offline. Poniższy kod ilustruje sposób użycia metody InsertAsync w celu wstawienia nowych wierszy do tabeli. Parametr zawiera dane do wstawienia jako obiekt .NET.

await todoTable.InsertAsync(todoItem);

Jeśli unikatowa wartość identyfikatora niestandardowego nie jest uwzględniona w todoItem podczas wstawiania, identyfikator GUID jest generowany przez serwer. Wygenerowany identyfikator można pobrać, sprawdzając obiekt po powrocie wywołania.

Aby wstawić nietypowe dane, możesz skorzystać z Json.NET:

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

Oto przykład użycia adresu e-mail jako unikatowego identyfikatora ciągu:

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);

Praca z wartościami identyfikatorów

Usługa Mobile Apps obsługuje unikatowe niestandardowe wartości ciągów dla kolumny identyfikatora tabeli. Wartość ciągu umożliwia aplikacjom używanie niestandardowych wartości, takich jak adresy e-mail lub nazwy użytkowników dla identyfikatora. Identyfikatory ciągów zapewniają następujące korzyści:

  • Identyfikatory są generowane bez wprowadzania rundy do bazy danych.
  • Rekordy są łatwiejsze do scalenia z różnych tabel lub baz danych.
  • Wartości identyfikatorów mogą lepiej integrować się z logiką aplikacji.

Jeśli wartość identyfikatora ciągu nie jest ustawiona na wstawiony rekord, zaplecze aplikacji mobilnej generuje unikatową wartość identyfikatora. Możesz użyć metody Guid.NewGuid, aby wygenerować własne wartości identyfikatorów na kliencie lub w zapleczu.

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

Aktualizowanie danych

Poniższy kod ilustruje sposób użycia metody UpdateAsync w celu zaktualizowania istniejącego rekordu przy użyciu tego samego identyfikatora przy użyciu nowych informacji. Parametr zawiera dane, które mają zostać zaktualizowane jako obiekt platformy .NET.

await todoTable.UpdateAsync(todoItem);

Aby zaktualizować nietypowe dane, możesz skorzystać z Newtonsoft JSON w następujący sposób:

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);

Podczas wprowadzania aktualizacji należy określić pole id. Zaplecze używa pola id, aby zidentyfikować wiersz do zaktualizowania. Pole id można uzyskać z wyniku wywołania InsertAsync. Jeśli spróbujesz zaktualizować element bez podawania wartości id, zostanie zgłoszony ArgumentException.

Usuwanie danych

Poniższy kod ilustruje sposób użycia metody DeleteAsync w celu usunięcia istniejącego wystąpienia. Wystąpienie jest identyfikowane przez pole id ustawione na todoItem.

await todoTable.DeleteAsync(todoItem);

Aby usunąć nietypowe dane, możesz skorzystać z Json.NET w następujący sposób:

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

Po wysłaniu żądania usunięcia należy określić identyfikator. Inne właściwości nie są przekazywane do usługi lub są ignorowane w usłudze. Wynikiem wywołania DeleteAsync jest zwykle null. Identyfikator do przekazania można uzyskać z wyniku wywołania InsertAsync. MobileServiceInvalidOperationException jest zgłaszany podczas próby usunięcia elementu bez określania pola id.

Rozwiązywanie konfliktów i optymistyczna współbieżność

Co najmniej dwóch klientów może zapisywać zmiany w tym samym elemencie w tym samym czasie. Bez wykrywania konfliktów ostatni zapis zastąpi wszystkie poprzednie aktualizacje. optymistyczna kontrola współbieżności zakłada, że każda transakcja może zatwierdzić i dlatego nie używa żadnych blokad zasobów. Przed zatwierdzeniem transakcji optymistyczna kontrola współbieżności sprawdza, czy żadna inna transakcja nie zmodyfikowała danych. Jeśli dane zostały zmodyfikowane, transakcja zatwierdzania zostanie wycofana.

Usługa Mobile Apps obsługuje optymistyczną kontrolę współbieżności, śledząc zmiany w każdym elemencie przy użyciu kolumny właściwości systemu version zdefiniowanej dla każdej tabeli w zapleczu aplikacji mobilnej. Za każdym razem, gdy rekord jest aktualizowany, usługa Mobile Apps ustawia właściwość version dla tego rekordu na nową wartość. Podczas każdego żądania aktualizacji właściwość version rekordu dołączonego do żądania jest porównywana z tą samą właściwością rekordu na serwerze. Jeśli wersja przekazana z żądaniem jest niezgodna z zapleczem, biblioteka kliencka zgłasza wyjątek MobileServicePreconditionFailedException<T>. Typ dołączony do wyjątku to rekord z zaplecza zawierającego wersję serwera rekordu. Następnie aplikacja może użyć tych informacji, aby zdecydować, czy ponownie wykonać żądanie aktualizacji z poprawną wartością version z zaplecza w celu zatwierdzenia zmian.

Zdefiniuj kolumnę w klasie tabeli dla właściwości systemu version, aby umożliwić optymistyczną współbieżność. Na przykład:

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; }
}

Aplikacje korzystające z nietypowych tabel umożliwiają optymistyczną współbieżność, ustawiając flagę Version na SystemProperties tabeli w następujący sposób.

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

Oprócz włączenia optymistycznej współbieżności należy również przechwycić wyjątek MobileServicePreconditionFailedException<T> w kodzie podczas wywoływania UpdateAsync. Rozwiąż konflikt, stosując poprawną version do zaktualizowanego rekordu i wywołaj updateAsync z rozpoznaną rekordem. Poniższy kod pokazuje, jak rozwiązać konflikt zapisu po wykryciu:

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();
}

Aby uzyskać więcej informacji, zobacz temat Offline Data Sync w usłudze Azure Mobile Apps.

Wiązanie danych z interfejsem użytkownika systemu Windows

W tej sekcji przedstawiono sposób wyświetlania zwracanych obiektów danych przy użyciu elementów interfejsu użytkownika w aplikacji systemu Windows. Poniższy przykładowy kod wiąże się ze źródłem listy z zapytaniem dla niekompletnych elementów. MobileServiceCollection tworzy kolekcję powiązań obsługujących usługę Mobile Apps.

// 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;

Niektóre kontrolki w środowisku uruchomieniowym zarządzanym obsługują interfejs o nazwie ISupportIncrementalLoading. Ten interfejs umożliwia kontrolkom żądanie dodatkowych danych podczas przewijania użytkownika. Istnieje wbudowana obsługa tego interfejsu dla uniwersalnych aplikacji systemu Windows za pośrednictwem MobileServiceIncrementalLoadingCollection, która automatycznie obsługuje wywołania z kontrolek. Użyj MobileServiceIncrementalLoadingCollection w aplikacjach systemu Windows w następujący sposób:

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

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

Aby użyć nowej kolekcji w aplikacjach systemu Windows Phone 8 i "Silverlight", użyj metod rozszerzeń ToCollection w IMobileServiceTableQuery<T> i IMobileServiceTable<T>. Aby załadować dane, wywołaj metodę LoadMoreItemsAsync().

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

Gdy używasz kolekcji utworzonej przez wywołanie ToCollectionAsync lub ToCollection, uzyskasz kolekcję, która może być powiązana z kontrolkami interfejsu użytkownika. Ta kolekcja uwzględnia stronicowanie. Ponieważ kolekcja ładuje dane z sieci, ładowanie czasami kończy się niepowodzeniem. Aby obsłużyć takie błędy, zastąpij metodę OnException w MobileServiceIncrementalLoadingCollection w celu obsługi wyjątków wynikających z wywołań do LoadMoreItemsAsync.

Zastanów się, czy tabela ma wiele pól, ale chcesz wyświetlić tylko niektóre z nich w kontrolce. Możesz użyć wskazówek w poprzedniej sekcji "Wybierz określone kolumny", aby wybrać określone kolumny do wyświetlenia w interfejsie użytkownika.

Zmienianie rozmiaru strony

Usługa Azure Mobile Apps domyślnie zwraca maksymalnie 50 elementów na żądanie. Rozmiar stronicowania można zmienić, zwiększając maksymalny rozmiar strony zarówno na kliencie, jak i serwerze. Aby zwiększyć żądany rozmiar strony, określ PullOptions podczas korzystania z PullAsync():

PullOptions pullOptions = new PullOptions
    {
        MaxPageSize = 100
    };

Zakładając, że PageSize równa lub większa niż 100 w obrębie serwera, żądanie zwraca maksymalnie 100 elementów.

Praca z tabelami offline

Tabele offline używają lokalnego magazynu SQLite do przechowywania danych do użycia w trybie offline. Wszystkie operacje tabel są wykonywane względem lokalnego magazynu SQLite zamiast magazynu serwera zdalnego. Aby utworzyć tabelę offline, najpierw przygotuj projekt.

  • W programie Visual Studio kliknij prawym przyciskiem myszy rozwiązanie >Zarządzanie pakietami NuGet dla rozwiązania..., a następnie wyszukaj i zainstaluj pakiet NuGet Microsoft.Azure.Mobile.Client.SQLiteStore pakiet NuGet dla wszystkich projektów w rozwiązaniu.
  • W przypadku urządzeń z systemem Windows naciśnij ReferencesAdd Reference... (Dodaj odwołanie...), rozwiń folder Windows Extensions Extensions, a następnie włącz odpowiednie SQLite for Windows SDK wraz z zestawem Visual C++ 2013 Runtime for Windows SDK. Nazwy zestawu SDK SQLite różnią się nieco w zależności od poszczególnych platform systemu Windows.

Przed utworzeniem odwołania do tabeli należy przygotować magazyn lokalny:

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

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

Inicjowanie magazynu jest zwykle wykonywane natychmiast po utworzeniu klienta. OfflineDbPath powinna być nazwą pliku odpowiednią do użycia na wszystkich platformach, które są obsługiwane. Jeśli ścieżka jest w pełni kwalifikowaną ścieżką (czyli zaczyna się od ukośnika), używana jest ta ścieżka. Jeśli ścieżka nie jest w pełni kwalifikowana, plik zostanie umieszczony w lokalizacji specyficznej dla platformy.

  • W przypadku urządzeń z systemami iOS i Android domyślną ścieżką jest folder "Pliki osobiste".
  • W przypadku urządzeń z systemem Windows domyślną ścieżką jest folder "AppData" specyficzny dla aplikacji.

Odwołanie do tabeli można uzyskać przy użyciu metody GetSyncTable<>:

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

Nie musisz uwierzytelniać się, aby używać tabeli offline. Podczas komunikacji z usługą zaplecza trzeba uwierzytelniać tylko wtedy, gdy użytkownik będzie komunikował się z usługą zaplecza.

Synchronizowanie tabeli offline

Tabele offline nie są domyślnie synchronizowane z zapleczem. Synchronizacja jest podzielona na dwie części. Zmiany można wypychać oddzielnie od pobierania nowych elementów. Oto typowa metoda synchronizacji:

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"]);
        }
    }
}

Jeśli pierwszy argument PullAsync ma wartość null, synchronizacja przyrostowa nie jest używana. Każda operacja synchronizacji pobiera wszystkie rekordy.

Zestaw SDK wykonuje niejawne PushAsync() przed ściąganiem rekordów.

Obsługa konfliktów odbywa się w metodzie PullAsync(). Konflikty można radzić sobie w taki sam sposób jak tabele online. Konflikt jest generowany, gdy PullAsync() jest wywoływany zamiast podczas wstawiania, aktualizowania lub usuwania. Jeśli wystąpi wiele konfliktów, są one powiązane z jednym elementem MobileServicePushFailedException. Obsłuż każdą awarię oddzielnie.

Praca z niestandardowym interfejsem API

Niestandardowy interfejs API umożliwia definiowanie niestandardowych punktów końcowych, które uwidaczniają funkcje serwera, które nie są mapujące na operację wstawiania, aktualizowania, usuwania ani odczytu. Korzystając z niestandardowego interfejsu API, możesz mieć większą kontrolę nad obsługą komunikatów, w tym odczytywanie i ustawianie nagłówków komunikatów HTTP oraz definiowanie formatu treści komunikatu innego niż JSON.

Wywoływanie niestandardowego interfejsu API przez wywołanie jednej z metod InvokeApiAsync na kliencie. Na przykład następujący wiersz kodu wysyła żądanie POST do completeAll api na zapleczu:

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

Ten formularz jest typem wywołania metody i wymaga, aby zdefiniowano typ zwracany MarkAllResult. Obsługiwane są zarówno metody typizowane, jak i nietypowe.

Metoda InvokeApiAsync() poprzedza element "/api/" do interfejsu API, który chcesz wywołać, chyba że interfejs API rozpoczyna się od znaku "/". Na przykład:

  • InvokeApiAsync("completeAll",...) wywołuje /api/completeAll na zapleczu
  • InvokeApiAsync("/.auth/me",...) wywołuje /.auth/me w zapleczu

Możesz użyć metody InvokeApiAsync do wywołania dowolnego interfejsu WebAPI, w tym tych interfejsów WebAPI, które nie są zdefiniowane w usłudze Azure Mobile Apps. Gdy używasz metody InvokeApiAsync(), odpowiednie nagłówki, w tym nagłówki uwierzytelniania, są wysyłane z żądaniem.

Uwierzytelnianie użytkowników

Usługa Mobile Apps obsługuje uwierzytelnianie i autoryzowanie użytkowników aplikacji przy użyciu różnych zewnętrznych dostawców tożsamości: Facebook, Google, Microsoft Account, Twitter i Microsoft Entra ID. Możesz ustawić uprawnienia do tabel, aby ograniczyć dostęp do określonych operacji tylko uwierzytelnieni użytkownicy. Możesz również użyć tożsamości uwierzytelnionych użytkowników do zaimplementowania reguł autoryzacji w skryptach serwera.

Obsługiwane są dwa przepływy uwierzytelniania: zarządzane przez klienta i przepływ zarządzany przez serwer . Przepływ zarządzany przez serwer zapewnia najprostsze środowisko uwierzytelniania, ponieważ opiera się na interfejsie uwierzytelniania internetowego dostawcy. Przepływ zarządzany przez klienta umożliwia głębszą integrację z funkcjami specyficznymi dla urządzenia, ponieważ opiera się na zestawach SDK specyficznych dla dostawcy.

Nuta

Zalecamy używanie przepływu zarządzanego przez klienta w aplikacjach produkcyjnych.

Aby skonfigurować uwierzytelnianie, musisz zarejestrować aplikację u co najmniej jednego dostawcy tożsamości. Dostawca tożsamości generuje identyfikator klienta i klucz tajny klienta dla aplikacji. Te wartości są następnie ustawiane w zapleczu, aby włączyć uwierzytelnianie/autoryzację usługi Azure App Service.

W tej sekcji opisano następujące tematy:

Uwierzytelnianie zarządzane przez klienta

Aplikacja może niezależnie skontaktować się z dostawcą tożsamości, a następnie podać zwrócony token podczas logowania do zaplecza. Ten przepływ klienta umożliwia zapewnienie użytkownikom środowiska logowania jednokrotnego lub pobranie dodatkowych danych użytkownika od dostawcy tożsamości. Uwierzytelnianie przepływu klienta jest preferowane w przypadku korzystania z przepływu serwera, ponieważ zestaw SDK dostawcy tożsamości zapewnia bardziej natywny sposób działania środowiska użytkownika i umożliwia większe dostosowanie.

Przykłady są dostępne dla następujących wzorców uwierzytelniania przepływu klienta:

Uwierzytelnianie użytkowników za pomocą biblioteki uwierzytelniania usługi Active Directory

Możesz użyć biblioteki uwierzytelniania usługi Active Directory (ADAL), aby zainicjować uwierzytelnianie użytkowników od klienta przy użyciu uwierzytelniania firmy Microsoft Entra.

Ostrzeżenie

Obsługa biblioteki uwierzytelniania usługi Active Directory (ADAL) zakończy się w grudniu 2022 r. Aplikacje korzystające z biblioteki ADAL w istniejących wersjach systemu operacyjnego będą nadal działać, ale wsparcie techniczne i aktualizacje zabezpieczeń zostaną zakończone. Aby uzyskać więcej informacji, zobacz Migrate apps to MSAL.

  1. Skonfiguruj zaplecze aplikacji mobilnej dla logowania firmy Microsoft Entra, postępując zgodnie z samouczkiem How to configure App Service for Active Directory login tutorial (Jak skonfigurować usługę App Service na potrzeby logowania do usługi Active Directory). Pamiętaj, aby wykonać opcjonalny krok rejestrowania natywnej aplikacji klienckiej.

  2. W programie Visual Studio otwórz projekt i dodaj odwołanie do pakietu NuGet Microsoft.IdentityModel.Clients.ActiveDirectory. Podczas wyszukiwania uwzględnij wersje wstępne.

  3. Dodaj następujący kod do aplikacji zgodnie z używaną platformą. W każdym z nich należy wykonać następujące zamiany:

    • Zastąp INSERT-AUTHORITY-HERE nazwą dzierżawy, w której aprowizowana aplikacja. Format powinien mieć wartość https://login.microsoftonline.com/contoso.onmicrosoft.com. Tę wartość można skopiować z karty Domena w identyfikatorze Firmy Microsoft w witrynie [Azure Portal].

    • Zastąp INSERT-RESOURCE-ID-HERE identyfikatorem klienta zaplecza aplikacji mobilnej. Identyfikator klienta można uzyskać na karcie Advanced w obszarze Microsoft Entra Settings w portalu.

    • Zastąp INSERT-CLIENT-ID-HERE identyfikatorem klienta skopiowanym z natywnej aplikacji klienckiej.

    • Zastąp INSERT-REDIRECT-URI-HERE punktem końcowym /.auth/login/done witryny przy użyciu schematu HTTPS. Ta wartość powinna być podobna do https://contoso.azurewebsites.net/.auth/login/done.

      Kod wymagany dla każdej platformy jest następujący:

      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);
      }
      

Logowanie jednokrotne przy użyciu tokenu z serwisu Facebook lub Google

Możesz użyć przepływu klienta, jak pokazano w tym fragmencie kodu dla serwisu Facebook lub 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();
    }
}

Uwierzytelnianie zarządzane przez serwer

Po zarejestrowaniu dostawcy tożsamości wywołaj metodę LoginAsync w obiekcie MobileServiceClient przy użyciu wartości MobileServiceAuthenticationProvider dostawcy. Na przykład poniższy kod inicjuje logowanie przepływu serwera przy użyciu serwisu 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();
    }
}

Jeśli używasz dostawcy tożsamości innego niż Facebook, zmień wartość elementu MobileServiceAuthenticationProvider na wartość dostawcy.

W przepływie serwera usługa Azure App Service zarządza przepływem uwierzytelniania OAuth, wyświetlając stronę logowania wybranego dostawcy. Po powrocie dostawcy tożsamości usługa Azure App Service generuje token uwierzytelniania usługi App Service. Metoda LoginAsync zwraca MobileServiceUser, która udostępnia zarówno identyfikator UserId uwierzytelnionego użytkownika, jak i token internetowy JSON (JWT). Ten token można buforować i ponownie używać do momentu jego wygaśnięcia. Aby uzyskać więcej informacji, zobacz buforowanie tokenu uwierzytelniania.

Nuta

W ramach tej sekcji usługa Azure Mobile Apps używa Xamarin.Essentials WebAuthenticator w celu wykonania tej pracy. Musisz obsłużyć odpowiedź z usługi, wywołując z powrotem do platformy Xamarin.Essentials. Aby uzyskać szczegółowe informacje, zobacz WebAuthenticator.

Buforowanie tokenu uwierzytelniania

W niektórych przypadkach wywołanie metody logowania można uniknąć po pierwszym pomyślnym uwierzytelnieniu, przechowując token uwierzytelniania od dostawcy. Aplikacje ze Sklepu Microsoft i platformy UWP mogą używać PasswordVault do buforowania bieżącego tokenu uwierzytelniania po pomyślnym zalogowaniu w następujący sposób:

await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook);

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

Wartość UserId jest przechowywana jako nazwa użytkownika poświadczeń, a token jest przechowywany jako hasło. W kolejnych startach możesz sprawdzić PasswordVault pod kątem buforowanych poświadczeń. W poniższym przykładzie są używane buforowane poświadczenia po znalezieniu i w przeciwnym razie próbuje ponownie uwierzytelnić się za pomocą zaplecza:

// 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.
}

Po wylogowaniu użytkownika należy również usunąć przechowywane poświadczenia w następujący sposób:

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

W przypadku korzystania z uwierzytelniania zarządzanego przez klienta można również buforować token dostępu uzyskany od dostawcy, takiego jak Facebook lub Twitter. Ten token można dostarczyć, aby zażądać nowego tokenu uwierzytelniania z zaplecza w następujący sposób:

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);

Różne tematy

Obsługa błędów

W przypadku wystąpienia błędu w zapleczu zestaw SDK klienta zgłasza MobileServiceInvalidOperationException. W poniższym przykładzie pokazano, jak obsłużyć wyjątek zwracany przez zaplecze:

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
    }
}

Dostosowywanie nagłówków żądań

Aby obsługiwać konkretny scenariusz aplikacji, może być konieczne dostosowanie komunikacji z zapleczem aplikacji mobilnej. Na przykład możesz dodać nagłówek niestandardowy do każdego żądania wychodzącego, a nawet zmienić kody stanu odpowiedzi. Możesz użyć niestandardowej DelegatingHandler, jak w poniższym przykładzie:

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;
    }
}

Włączanie rejestrowania żądań

Możesz również użyć programu DelegatingHandler, aby dodać rejestrowanie żądań:

public class LoggingHandler : DelegatingHandler
{
    public LoggingHandler() : base() { }
    public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token)
    {
        Debug.WriteLine($"[HTTP] >>> {request.Method} {request.RequestUri}");
        if (request.Content != null)
        {
            Debug.WriteLine($"[HTTP] >>> {await request.Content.ReadAsStringAsync().ConfigureAwait(false)}");
        }

        HttpResponseMessage response = await base.SendAsync(request, token).ConfigureAwait(false);

        Debug.WriteLine($"[HTTP] <<< {response.StatusCode} {response.ReasonPhrase}");
        if (response.Content != null)
        {
            Debug.WriteLine($"[HTTP] <<< {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}");
        }

        return response;
    }
}