Dela via


Så här använder du Azure Mobile Apps v4.2.0-klientbiblioteket för .NET

Den här guiden visar hur du utför vanliga scenarier med hjälp av .NET-klientbiblioteket för Azure Mobile Apps. Använd .NET-klientbiblioteket i Windows-program (WPF, UWP) eller Xamarin (native eller Forms). Om du inte har använt Azure Mobile Apps tidigare kan du överväga att först slutföra självstudiekursen Snabbstart för Xamarin.Forms .

Varning

Den här artikeln beskriver information för biblioteksversionen v4.2.0, som ersätts av v5.0.0-biblioteket. Den senaste informationen finns i artikeln om den senaste versionen

Plattformar som stöds

.NET-klientbiblioteket stöder .NET Standard 2.0 och följande plattformar:

  • Xamarin.Android från API-nivå 19 upp till API-nivå 30.
  • Xamarin.iOS version 8.0 till 14.3.
  • Universell Windows-plattform bygger 16299 och senare.
  • Alla .NET Standard 2.0-program.

Autentiseringen "serverflöde" använder en WebView för det presenterade användargränssnittet och kanske inte är tillgänglig på alla plattformar. Om den inte är tillgänglig måste du ange en "klientflödesautentisering". Det här klientbiblioteket är inte lämpligt för klock- eller IoT-formulärfaktorer när du använder autentisering.

Konfiguration och förutsättningar

Vi antar att du redan har skapat och publicerat ditt Azure Mobile Apps-serverdelsprojekt, som innehåller minst en tabell. I den kod som används i det här avsnittet namnges TodoItem tabellen och har en sträng Id, och Text fält och en boolesk Complete kolumn. Den här tabellen är samma tabell som skapades när du slutför snabbstarten.

Motsvarande typ på klientsidan i C# är den här klassen:

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 används för att definiera PropertyName-mappningen mellan klientfältet och tabellfältet.

Mer information om hur du skapar tabeller i mobile apps-serverdelen finns i avsnittet .NET Server SDK iavsnittet Node.js Server SDK.

Installera SDK-paketet för den hanterade klienten

Högerklicka på projektet, tryck på Hantera NuGet-paket, sök Microsoft.Azure.Mobile.Client efter paketet och tryck sedan på Installera. Installera även paketet för Microsoft.Azure.Mobile.Client.SQLiteStore offlinefunktioner.

Skapa Azure Mobile Apps-klienten

Följande kod skapar MobileServiceClient-objektet som används för att komma åt mobilappens serverdel.

var client = new MobileServiceClient("MOBILE_APP_URL");

I föregående kod ersätter du MOBILE_APP_URL med URL:en för App Service-serverdelen. Objektet MobileServiceClient ska vara en singleton.

Arbeta med tabeller

I följande avsnitt beskrivs hur du söker efter och hämtar poster och ändrar data i tabellen. Följande avsnitt beskrivs:

Skapa en tabellreferens

All kod som kommer åt eller ändrar data i en serverdelstabell anropar funktioner i MobileServiceTable objektet. Hämta en referens till tabellen genom att anropa metoden GetTable enligt följande:

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

Det returnerade objektet använder den typerade serialiseringsmodellen. En otypad serialiseringsmodell stöds också. I följande exempel skapas en referens till en otypad tabell:

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

I otypade frågor måste du ange den underliggande OData-frågesträngen.

Fråga efter data från din mobilapp

I det här avsnittet beskrivs hur du utfärdar frågor till mobilappens serverdel, som innehåller följande funktioner:

Kommentar

En serverdriven sidstorlek tillämpas för att förhindra att alla rader returneras. Växling hindrar standardbegäranden för stora datamängder från att påverka tjänsten negativt. Om du vill returnera fler än 50 rader använder du metoden och Take enligt beskrivningen Skip i Returnera data på sidor.

Filtrera returnerade data

Följande kod visar hur du filtrerar data genom att inkludera en Where sats i en fråga. Den returnerar alla objekt från todoTable vars Complete egenskap är lika med false. Funktionen Where tillämpar ett radfiltreringspredikat på frågan mot tabellen.

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

Du kan visa URI:n för begäran som skickas till serverdelen med hjälp av programvara för meddelandegranskning, till exempel utvecklarverktyg för webbläsare eller Fiddler. Om du tittar på begärande-URI:n ser du att frågesträngen har ändrats:

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

Denna OData-begäran översätts till en SQL-fråga av Server SDK:

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

Funktionen som skickas till Where metoden kan ha ett godtyckligt antal villkor.

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

Det här exemplet skulle översättas till en SQL-fråga av Server SDK:

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

Den här frågan kan också delas upp i flera satser:

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

De två metoderna är likvärdiga och kan användas omväxlande. Det tidigare alternativet – att sammanfoga flera predikater i en fråga – är mer kompakt och rekommenderas.

Satsen Where stöder åtgärder som översätts till OData-delmängden. Åtgärderna omfattar:

  • Relationsoperatorer (==, !=, <, <=, >, ), >=
  • Aritmetiska operatorer (+, -, /, *, %),
  • Talprecision (Math.Floor, Math.Ceiling),
  • Strängfunktioner (Length, Substring, Replace, IndexOf, StartsWith, ), EndsWith
  • Datumegenskaper (Year, Month, Day, Hour, Minute, ), Second
  • Åtkomstegenskaper för ett objekt och
  • Uttryck som kombinerar någon av dessa åtgärder.

När du överväger vad Server SDK stöder kan du överväga OData v3-dokumentationen.

Sortera returnerade data

Följande kod visar hur du sorterar data genom att inkludera en OrderBy - eller OrderByDescending-funktion i frågan. Den returnerar objekt från todoTable sorterade stigande Text efter fältet.

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

Returnera data på sidor

Som standard returnerar serverdelen endast de första 50 raderna. Du kan öka antalet returnerade rader genom att anropa metoden Take . Använd Take tillsammans med metoden Hoppa över för att begära en specifik "sida" av den totala datamängden som returneras av frågan. Följande fråga returnerar de tre översta objekten i tabellen när den körs.

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

Följande reviderade fråga hoppar över de tre första resultaten och returnerar nästa tre resultat. Den här frågan genererar den andra "sidan" med data, där sidstorleken är tre objekt.

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

Metoden IncludeTotalCount begär det totala antalet poster som skulle ha returnerats, och ignorerar alla angivna växlings-/gränssatser:

query = query.IncludeTotalCount();

I en verklig app kan du använda frågor som liknar föregående exempel med en sidkontroll eller ett jämförbart användargränssnitt för att navigera mellan sidor.

Kommentar

Om du vill åsidosätta gränsen på 50 rader i en mobilappsserverdel måste du även tillämpa EnableQueryAttribute på den offentliga GET-metoden och ange växlingsbeteendet. När metoden används anger följande maximalt antal returnerade rader till 1 000:

[EnableQuery(MaxTop=1000)]

Välj specifika kolumner

Du kan ange vilken uppsättning egenskaper som ska inkluderas i resultatet genom att lägga till en Select-sats i frågan. Följande kod visar till exempel hur du bara väljer ett fält och hur du väljer och formaterar flera fält:

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

Alla funktioner som beskrivs hittills är additiva, så vi kan fortsätta att länka dem. Varje länkat anrop påverkar mer av frågan. Ett exempel till:

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

Slå upp data efter ID

Funktionen LookupAsync kan användas för att söka efter objekt från databasen med ett visst ID.

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

Köra otypade frågor

När du kör en fråga med ett otypiskt tabellobjekt måste du uttryckligen ange OData-frågesträngen genom att anropa ReadAsync, som i följande exempel:

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

Du får tillbaka JSON-värden som du kan använda som en egenskapsväska. Mer information om och Newtonsoft Json finns på JTokenNewtonsoft JSON-webbplatsen .

Infoga data

Alla klienttyper måste innehålla en medlem med namnet ID, som som standard är en sträng. Detta ID krävs för att utföra CRUD-åtgärder och för offlinesynkronisering. Följande kod visar hur du använder metoden InsertAsync för att infoga nya rader i en tabell. Parametern innehåller de data som ska infogas som ett .NET-objekt.

await todoTable.InsertAsync(todoItem);

Om ett unikt anpassat ID-värde inte ingår i todoItem under en infogning genereras ett GUID av servern. Du kan hämta det genererade ID:t genom att inspektera objektet när anropet har returnerats.

Om du vill infoga otypade data kan du dra nytta av Json.NET:

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

Här är ett exempel med en e-postadress som ett unikt sträng-ID:

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

Arbeta med ID-värden

Mobile Apps stöder unika anpassade strängvärden för tabellens ID-kolumn . Med ett strängvärde kan program använda anpassade värden, till exempel e-postadresser eller användarnamn för ID:t. Med sträng-ID:t får du följande fördelar:

  • ID:t genereras utan att göra en tur och retur-resa till databasen.
  • Poster är enklare att sammanfoga från olika tabeller eller databaser.
  • ID-värden kan integreras bättre med ett programs logik.

När ett sträng-ID-värde inte har angetts för en infogad post genererar mobilappens serverdel ett unikt värde för ID:t. Du kan använda metoden Guid.NewGuid för att generera egna ID-värden, antingen på klienten eller i serverdelen.

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

Uppdatera data

Följande kod visar hur du använder metoden UpdateAsync för att uppdatera en befintlig post med samma ID med ny information. Parametern innehåller de data som ska uppdateras som ett .NET-objekt.

await todoTable.UpdateAsync(todoItem);

Om du vill uppdatera otypade data kan du dra nytta av Newtonsoft JSON på följande sätt:

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

Ett id fält måste anges när du gör en uppdatering. Serverdelen använder fältet id för att identifiera vilken rad som ska uppdateras. Fältet id kan hämtas från resultatet av anropet InsertAsync . En ArgumentException utlöses om du försöker uppdatera ett objekt utan att ange id värdet.

Ta bort data

Följande kod visar hur du använder metoden DeleteAsync för att ta bort en befintlig instans. Instansen identifieras av fältet id som anges på todoItem.

await todoTable.DeleteAsync(todoItem);

Om du vill ta bort otypade data kan du dra nytta av Json.NET på följande sätt:

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

När du gör en borttagningsbegäran måste ett ID anges. Andra egenskaper skickas inte till tjänsten eller ignoreras i tjänsten. Resultatet av ett DeleteAsync anrop är vanligtvis null. Det ID som ska skickas in kan hämtas från resultatet av anropet InsertAsync . En MobileServiceInvalidOperationException genereras när du försöker ta bort ett objekt utan att ange fältet id .

Konfliktlösning och optimistisk samtidighet

Två eller flera klienter kan skriva ändringar i samma objekt samtidigt. Utan konfliktidentifiering skulle den senaste skrivningen skriva över alla tidigare uppdateringar. Optimistisk samtidighetskontroll förutsätter att varje transaktion kan checka in och därför inte använder någon resurslåsning. Innan du genomför en transaktion verifierar optimistisk samtidighetskontroll att ingen annan transaktion har ändrat data. Om data har ändrats återställs incheckningstransaktionen.

Mobile Apps stöder optimistisk samtidighetskontroll genom att spåra ändringar i varje objekt med hjälp av den version systemegenskapskolumn som definieras för varje tabell i mobilappens serverdel. Varje gång en post uppdateras anger Mobile Apps egenskapen för posten version till ett nytt värde. Under varje uppdateringsbegäran version jämförs egenskapen för posten som ingår i begäran med samma egenskap för posten på servern. Om den version som skickades med begäran inte matchar serverdelen skapar klientbiblioteket ett MobileServicePreconditionFailedException<T> undantag. Den typ som ingår i undantaget är posten från serverdelen som innehåller serverns version av posten. Programmet kan sedan använda den här informationen för att avgöra om uppdateringsbegäran ska köras igen med rätt version värde från serverdelen för att genomföra ändringar.

Definiera en kolumn i tabellklassen för systemegenskapen version för att aktivera optimistisk samtidighet. Till exempel:

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

Program som använder otypade tabeller möjliggör optimistisk samtidighet genom att ange Version flaggan i tabellen på SystemProperties följande sätt.

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

Förutom att aktivera optimistisk samtidighet måste du också fånga MobileServicePreconditionFailedException<T> undantaget i koden när du anropar UpdateAsync. Lös konflikten genom att tillämpa rätt version på den uppdaterade posten och anropa UpdateAsync med den lösta posten. Följande kod visar hur du löser en skrivkonflikt när den har identifierats:

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

Mer information finns i avsnittet offlinedatasynkronisering i Azure Mobile Apps .

Binda data till ett Windows-användargränssnitt

Det här avsnittet visar hur du visar returnerade dataobjekt med hjälp av användargränssnittselement i en Windows-app. Följande exempelkod binder till källan i listan med en fråga om ofullständiga objekt. MobileServiceCollection skapar en Mobile Apps-medveten bindningssamling.

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

Vissa kontroller i den hanterade körningen stöder ett gränssnitt med namnet ISupportIncrementalLoading. Med det här gränssnittet kan kontroller begära extra data när användaren rullar. Det finns inbyggt stöd för det här gränssnittet för universella Windows-appar via MobileServiceIncrementalLoadingCollection, som automatiskt hanterar anropen från kontrollerna. Använd MobileServiceIncrementalLoadingCollection i Windows-appar på följande sätt:

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

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

Om du vill använda den nya samlingen i Windows Telefon 8- och Silverlight-appar använder du tilläggsmetoderna ToCollectionIMobileServiceTableQuery<T> och IMobileServiceTable<T>. Om du vill läsa in data anropar du LoadMoreItemsAsync().

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

När du använder samlingen som skapats genom att anropa ToCollectionAsync eller ToCollectionfår du en samling som kan bindas till användargränssnittskontroller. Den här samlingen är växlingsmedveten. Eftersom samlingen läser in data från nätverket misslyckas ibland inläsningen. Om du vill hantera sådana fel åsidosätter du OnException metoden på MobileServiceIncrementalLoadingCollection för att hantera undantag som uppstår från anrop till LoadMoreItemsAsync.

Tänk på om tabellen har många fält men du bara vill visa några av dem i din kontroll. Du kan använda vägledningen i föregående avsnitt "Välj specifika kolumner" för att välja specifika kolumner som ska visas i användargränssnittet.

Ändra sidstorlek

Azure Mobile Apps returnerar som standard högst 50 objekt per begäran. Du kan ändra växlingsstorleken genom att öka den maximala sidstorleken på både klienten och servern. Om du vill öka den begärda sidstorleken anger du PullOptions när du använder PullAsync():

PullOptions pullOptions = new PullOptions
    {
        MaxPageSize = 100
    };

Förutsatt att du har gjort PageSize lika med eller större än 100 på servern returnerar en begäran upp till 100 objekt.

Arbeta med offlinetabeller

Offlinetabeller använder ett lokalt SQLite-arkiv för att lagra data för användning när de är offline. Alla tabellåtgärder utförs mot det lokala SQLite-arkivet i stället för fjärrserverarkivet. Förbered först projektet för att skapa en offlinetabell.

  • I Visual Studio högerklickar du på lösningen >Hantera NuGet-paket för lösning... och söker sedan efter och installerar NuGet-paketet Microsoft.Azure.Mobile.Client.SQLiteStore för alla projekt i lösningen.
  • För Windows-enheter trycker du på Referenser>Lägg till referens..., expanderar Windows-mapptilläggen> och aktiverar sedan lämplig SQLite för Windows SDK tillsammans med Visual C++ 2013 Runtime för Windows SDK. SQLite SDK-namnen varierar något för varje Windows-plattform.

Innan en tabellreferens kan skapas måste det lokala arkivet förberedas:

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

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

Butiksinitiering görs normalt omedelbart efter att klienten har skapats. OfflineDbPath bör vara ett filnamn som är lämpligt för användning på alla plattformar som du stöder. Om sökvägen är en fullständigt kvalificerad sökväg (dvs. börjar den med ett snedstreck) används den sökvägen. Om sökvägen inte är fullständigt kvalificerad placeras filen på en plattformsspecifik plats.

  • För iOS- och Android-enheter är standardsökvägen mappen "Personliga filer".
  • För Windows-enheter är standardsökvägen den programspecifika mappen "AppData".

En tabellreferens kan hämtas med hjälp av GetSyncTable<> metoden:

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

Du behöver inte autentisera för att använda en offlinetabell. Du behöver bara autentisera när du kommunicerar med serverdelstjänsten.

Synkronisera en offlinetabell

Offlinetabeller synkroniseras inte med serverdelen som standard. Synkroniseringen är uppdelad i två delar. Du kan skicka ändringar separat från att ladda ned nya objekt. Här är en typisk synkroniseringsmetod:

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

Om det första argumentet är PullAsync null används inte inkrementell synkronisering. Varje synkroniseringsåtgärd hämtar alla poster.

SDK utför en implicit PushAsync() innan du hämtar poster.

Konflikthantering sker på en PullAsync() metod. Du kan hantera konflikter på samma sätt som onlinetabeller. Konflikten skapas när PullAsync() anropas i stället för under infogningen, uppdateringen eller borttagningen. Om flera konflikter inträffar paketeras de till en enda MobileServicePushFailedException. Hantera varje fel separat.

Arbeta med ett anpassat API

Med ett anpassat API kan du definiera anpassade slutpunkter som exponerar serverfunktioner som inte mappas till en infognings-, uppdaterings-, borttagnings- eller läsåtgärd. Genom att använda ett anpassat API kan du ha mer kontroll över meddelanden, inklusive att läsa och ange HTTP-meddelandehuvuden och definiera ett annat meddelandetextformat än JSON.

Du anropar ett anpassat API genom att anropa någon av InvokeApiAsync-metoderna på klienten. Följande kodrad skickar till exempel en POST-begäran till completeAll API på serverdelen:

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

Det här formuläret är ett typbeskrivet metodanrop och kräver att returtypen MarkAllResult har definierats. Både inskrivna och otypade metoder stöds.

Metoden InvokeApiAsync() prepends '/api/' till det API som du vill anropa om inte API:et börjar med en '/'. Till exempel:

  • InvokeApiAsync("completeAll",...) anropar /api/completeAlla på serverdelen
  • InvokeApiAsync("/.auth/me",...) anropar /.auth/me på serverdelen

Du kan använda InvokeApiAsync för att anropa valfri WebAPI, inklusive de WebAPIs som inte har definierats med Azure Mobile Apps. När du använder InvokeApiAsync() skickas lämpliga rubriker, inklusive autentiseringshuvuden, med begäran.

Autentisera användare

Mobile Apps stöder autentisering och auktorisering av appanvändare med hjälp av olika externa identitetsprovidrar: Facebook, Google, Microsoft-konto, Twitter och Microsoft Entra-ID. Du kan ange behörigheter för tabeller för att begränsa åtkomsten för specifika åtgärder till endast autentiserade användare. Du kan också använda identiteten för autentiserade användare för att implementera auktoriseringsregler i serverskript.

Två autentiseringsflöden stöds: klienthanterat och serverhanterat flöde. Det serverhanterade flödet ger den enklaste autentiseringsupplevelsen eftersom det förlitar sig på leverantörens webbautentiseringsgränssnitt. Det klienthanterade flödet möjliggör djupare integrering med enhetsspecifika funktioner eftersom det förlitar sig på providerspecifika enhetsspecifika SDK:er.

Kommentar

Vi rekommenderar att du använder ett klienthanterat flöde i dina produktionsappar.

Om du vill konfigurera autentisering måste du registrera din app med en eller flera identitetsprovidrar. Identitetsprovidern genererar ett klient-ID och en klienthemlighet för din app. Dessa värden anges sedan i serverdelen för att aktivera Azure App Service-autentisering/auktorisering.

Följande avsnitt beskrivs i det här avsnittet:

Klienthanterad autentisering

Din app kan självständigt kontakta identitetsprovidern och sedan ange den returnerade token under inloggningen med serverdelen. Med det här klientflödet kan du tillhandahålla en enkel inloggningsupplevelse för användare eller hämta extra användardata från identitetsprovidern. Klientflödesautentisering är att föredra att använda ett serverflöde eftersom identitetsproviderns SDK ger en mer inbyggd UX-känsla och möjliggör mer anpassning.

Exempel finns för följande autentiseringsmönster för klientflöde:

Autentisera användare med Active Directory-autentiseringsbiblioteket

Du kan använda Active Directory Authentication Library (ADAL) för att initiera användarautentisering från klienten med Hjälp av Microsoft Entra-autentisering.

Varning

Stödet för Active Directory Authentication Library (ADAL) upphör i december 2022. Appar som använder ADAL på befintliga OS-versioner fortsätter att fungera, men teknisk support och säkerhetsuppdateringar upphör. Mer information finns i Migrera appar till MSAL.

  1. Konfigurera mobilappens serverdel för Microsoft Entra-inloggning genom att följa självstudien Konfigurera App Service för Active Directory-inloggning . Slutför det valfria steget för att registrera ett internt klientprogram.

  2. Öppna projektet i Visual Studio och lägg till en referens till Microsoft.IdentityModel.Clients.ActiveDirectory NuGet-paketet. När du söker ska du inkludera förhandsversioner.

  3. Lägg till följande kod i ditt program enligt den plattform du använder. I var och en gör du följande ersättningar:

    • Ersätt INSERT-AUTHORITY-HERE med namnet på den klientorganisation där du etablerade programmet. Formatet ska vara https://login.microsoftonline.com/contoso.onmicrosoft.com. Det här värdet kan kopieras från fliken Domän i ditt Microsoft Entra-ID i [Azure-portalen].

    • Ersätt INSERT-RESOURCE-ID-HERE med klient-ID:t för mobilappens serverdel. Du kan hämta klient-ID:t från fliken Avancerat under Microsoft Entra Inställningar i portalen.

    • Ersätt INSERT-CLIENT-ID-HERE med det klient-ID som du kopierade från det interna klientprogrammet.

    • Ersätt INSERT-REDIRECT-URI-HERE med webbplatsens /.auth/login/done slutpunkt med hjälp av HTTPS-schemat. Det här värdet bör likna https://contoso.azurewebsites.net/.auth/login/done.

      Koden som behövs för varje plattform följer:

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

Enkel inloggning med en token från Facebook eller Google

Du kan använda klientflödet enligt det här kodfragmentet för Facebook eller 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();
    }
}

Serverhanterad autentisering

När du har registrerat din identitetsprovider anropar du metoden LoginAsync på MobileServiceClient med värdet MobileServiceAuthenticationProvider för providern. Följande kod initierar till exempel inloggning med serverflöde med hjälp av 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();
    }
}

Om du använder en annan identitetsprovider än Facebook ändrar du värdet för MobileServiceAuthenticationProvider till värdet för din provider.

I ett serverflöde hanterar Azure App Service OAuth-autentiseringsflödet genom att visa inloggningssidan för den valda providern. När identitetsprovidern har returnerats genererar Azure App Service en App Service-autentiseringstoken. Metoden LoginAsync returnerar en MobileServiceUser, som tillhandahåller både UserId för den autentiserade användaren och MobileServiceAuthenticationToken som en JSON-webbtoken (JWT). Den här token kan cachelagras och återanvändas tills den upphör att gälla. Mer information finns i Cachelagring autentiseringstoken.

Kommentar

Under täcket använder Azure Mobile Apps en Xamarin.Essentials WebAuthenticator för att utföra arbetet. Du måste hantera svaret från tjänsten genom att anropa tillbaka till Xamarin.Essentials. Mer information finns i WebAuthenticator.

Cachelagring autentiseringstoken

I vissa fall kan anropet till inloggningsmetoden undvikas efter den första lyckade autentiseringen genom att lagra autentiseringstoken från providern. Microsoft Store- och UWP-appar kan använda PasswordVault för att cachelagrar den aktuella autentiseringstoken efter en lyckad inloggning på följande sätt:

await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook);

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

UserId-värdet lagras som Användarnamn för autentiseringsuppgifterna och token är den som lagras som lösenord. Vid efterföljande nystartade datorer kan du kontrollera PasswordVault efter cachelagrade autentiseringsuppgifter. I följande exempel används cachelagrade autentiseringsuppgifter när de hittas och försöker i övrigt autentisera igen med serverdelen:

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

När du loggar ut en användare måste du också ta bort den lagrade autentiseringsuppgiften på följande sätt:

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

När du använder klienthanterad autentisering kan du även cachelagra åtkomsttoken som hämtats från din provider, till exempel Facebook eller Twitter. Den här token kan anges för att begära en ny autentiseringstoken från serverdelen enligt följande:

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

Diverse ämnen

Hantera fel

När ett fel inträffar i serverdelen genererar klient-SDK:et en MobileServiceInvalidOperationException. I följande exempel visas hur du hanterar ett undantag som returneras av serverdelen:

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

Anpassa begärandehuvuden

För att stödja ditt specifika appscenario kan du behöva anpassa kommunikationen med mobilappens serverdel. Du kanske till exempel vill lägga till en anpassad rubrik i varje utgående begäran eller till och med ändra statuskoder för svar. Du kan använda en anpassad DelegeringHandler, som i följande exempel:

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

Aktivera loggning av begäranden

Du kan också använda en DelegeringHandler för att lägga till loggning av begäranden:

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