Udostępnij za pośrednictwem


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

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, najpierw zapoznaj się z samouczkiem Szybki start dotyczącym platformy Xamarin.Forms .

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 kompiluje 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.

Konfigurowanie 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 ma nazwę TodoItem i zawiera ciąg Id, oraz pola i Text kolumnę logiczną Complete . Ta tabela jest tą samą tabelą utworzoną po ukończeniu przewodnika 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; }
}

Element JsonPropertyAttribute służy do definiowania mapowania WłaściwościName między polem klienta a polem tabeli.

Aby dowiedzieć się, jak tworzyć tabele w zapleczu usługi Mobile Apps, zobacz temat Zestaw SDK programu .NET Server w temacieNode.js Server SDK.

Instalowanie pakietu zestawu SDK klienta zarządzanego

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

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 ciąg 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ówione są następujące tematy:

Tworzenie odwołania do tabeli

Cały kod, który uzyskuje dostęp do danych lub modyfikuje je w tabeli zaplecza, wywołuje funkcje w MobileServiceTable obiekcie . 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:

Uwaga

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 Skip metody i Take zgodnie z opisem w temacie Zwracanie danych na stronach.

Filtr zwracanych danych

Poniższy kod ilustruje sposób filtrowania danych przez dołączenie Where klauzuli w zapytaniu. Zwraca wszystkie elementy, z todoTable których Complete właściwość 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 program 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 Where metody 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 podzestaw OData. Operacje obejmują:

  • Operatory relacyjne (==, !=, <, <=, >, >=),
  • Operatory arytmetyczne (+, -, /, *, ), %
  • Precyzja liczb (Math.Floor, Math.Ceiling),
  • Funkcje ciągów (Length, , SubstringReplace, 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.

Rozważając obsługę zestawu SDK serwera, możesz wziąć pod uwagę dokumentację OData w wersji 3.

Sortowanie zwróconych danych

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

// 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 metody Skip 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 całkowitej liczby wszystkich rekordów, które zostałyby zwrócone, ignorując każdą 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.

Uwaga

Aby zastąpić limit 50 wierszy w zapleczu aplikacji mobilnej, należy również zastosować atrybut 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 do uwzględnienia w wynikach, dodając klauzulę Select do zapytania. 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 metodę 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 JToken pliku 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, jak za pomocą metody InsertAsync wstawić nowe wiersze 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 wstawieniu 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 id 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 do 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 formatu 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 id wprowadzania aktualizacji należy określić pole. Zaplecze używa id pola do identyfikowania wiersza do zaktualizowania. Pole id można uzyskać z wyniku wywołania InsertAsync . Element ArgumentException jest zgłaszany, jeśli próbujesz zaktualizować element bez podawania id wartości.

Usuwanie danych

Poniższy kod ilustruje sposób użycia metody DeleteAsync do usunięcia istniejącego wystąpienia. Wystąpienie jest identyfikowane przez id pole 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 . Element MobileServiceInvalidOperationException jest zgłaszany podczas próby usunięcia elementu bez określania id pola.

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 zdefiniowanej version dla każdej tabeli w zapleczu aplikacji mobilnej. Za każdym razem, gdy rekord jest aktualizowany, usługa Mobile Apps ustawia właściwość dla tego rekordu version na nową wartość. Podczas każdego żądania version aktualizacji właściwość rekordu dołączonego do żądania jest porównywana z tą samą właściwością dla rekordu na serwerze. Jeśli wersja przekazana z żądaniem jest niezgodna z zapleczem, biblioteka klienta zgłasza MobileServicePreconditionFailedException<T> wyjątek. 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ą version wartością z zaplecza w celu zatwierdzenia zmian.

Zdefiniuj kolumnę w klasie tabeli dla właściwości systemowej 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 w 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ć MobileServicePreconditionFailedException<T> wyjątek w kodzie podczas wywoływania narzędzia UpdateAsync. Rozwiąż konflikt, stosując poprawność version zaktualizowanego rekordu i wywołaj metodę UpdateAsync z rozpoznanymi rekordami. 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 in Azure Mobile Apps (Synchronizacja danych offline 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. Aplikacja 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 zarządzanym środowisku uruchomieniowym 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 Telefon 8 i "Silverlight", użyj ToCollection metod rozszerzeń w systemach 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 metody 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 , MobileServiceIncrementalLoadingCollection aby obsługiwać wyjątki wynikające z wywołań do LoadMoreItemsAsyncmetody .

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 , kiedy używasz polecenia PullAsync():

PullOptions pullOptions = new PullOptions
    {
        MaxPageSize = 100
    };

Zakładając, że w ramach serwera utworzono wartość równą PageSize lub większą niż 100, żą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ądzaj pakietami NuGet dla rozwiązania >..., a następnie wyszukaj i zainstaluj pakiet NuGet Microsoft.Azure.Mobile.Client.SQLiteStore dla wszystkich projektów w rozwiązaniu.
  • W przypadku urządzeń z systemem Windows naciśnij klawisze Odwołania>dodaj odwołanie..., rozwiń węzeł Rozszerzenia folderu> systemu Windows, a następnie włącz odpowiednią bibliotekę SQLite dla zestawu Windows SDK wraz z środowiskiem uruchomieniowym Visual C++ 2013 dla zestawu 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. Ścieżka 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 GetSyncTable<> metody :

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 ma wartość PullAsync null, synchronizacja przyrostowa nie jest używana. Każda operacja synchronizacji pobiera wszystkie rekordy.

Zestaw SDK wykonuje niejawną operację 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.

Niestandardowy interfejs API jest wywoływany przez wywołanie jednej z metod InvokeApiAsync na kliencie. Na przykład następujący wiersz kodu wysyła żądanie POST do kompletnego interfejsu APIAll w zapleczu:

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

Ten formularz jest wywołaniem metody typizowanej 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 element /.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: przepływ zarządzany 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.

Uwaga

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 w celu włączenia uwierzytelniania/autoryzacji usługi aplikacja systemu Azure.

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 Migrowanie aplikacji do biblioteki 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 (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 Microsoft.IdentityModel.Clients.ActiveDirectory pakietu NuGet. 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 ciąg INSERT-AUTHORITY-HERE nazwą dzierżawy, w której aprowizowana aplikacja została aprowizowana. 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 ciąg INSERT-RESOURCE-ID-HERE identyfikatorem klienta zaplecza aplikacji mobilnej. Identyfikator klienta można uzyskać z karty Zaawansowane w obszarze Microsoft Entra Ustawienia w portalu.

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

    • Zastąp ciąg INSERT-REDIRECT-URI-HERE punktem końcowym witryny /.auth/login/done , używając 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 z 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 aplikacja systemu Azure zarządza przepływem uwierzytelniania OAuth, wyświetlając stronę logowania wybranego dostawcy. Po powrocie dostawcy tożsamości usługa aplikacja systemu Azure generuje token uwierzytelniania usługi App Service. Metoda LoginAsync zwraca element MobileServiceUser, który udostępnia zarówno identyfikator UserId uwierzytelnionego użytkownika, jak i token sieci Web MobileServiceAuthenticationToken (JWT). Ten token można zapisać w pamięci podręcznej i ponownie go używać, dopóki nie wygaśnie. Aby uzyskać więcej informacji, zobacz Buforowanie token uwierzytelniania.

Uwaga

W ramach tej sekcji usługa Azure Mobile Apps używa narzędzia WebAuthenticator Xamarin.Essentials do 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 token 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ć funkcji PasswordVault do buforowania bieżącego tokenu uwierzytelniania po pomyślnym zalogowaniu się 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ć hasłoVault 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 błąd 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 procedury delegowania, 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;
    }
}