Jak používat klientskou knihovnu Azure Mobile Apps pro .NET
Poznámka
Tento produkt je vyřazený. Náhradu za projekty používající .NET 8 nebo novější najdete v knihovně Community Toolkit Datasync.
V této příručce se dozvíte, jak provádět běžné scénáře pomocí klientské knihovny .NET pro Azure Mobile Apps. Klientskou knihovnu .NET použijte v libovolné aplikaci .NET 6 nebo .NET Standard 2.0, včetně MAUI, Xamarinu a Windows (WPF, UPW a WinUI).
Pokud s Azure Mobile Apps teprve začínáte, zvažte první dokončení některého z kurzů pro rychlý start:
- AvaloniaUI
-
MAUI (Android a iOS) - Uno Platform
- Windows (UPW)
-
Windows (WinUI3) -
Windows (WPF) - Xamarin (nativní android)
-
Xamarin (nativní pro iOS) -
Xamarin Forms (Android a iOS)
Poznámka
Tento článek popisuje nejnovější edici rozhraní Microsoft Datasync Framework (v6.0). Starší klienti najdete vdokumentaci k
Podporované platformy
Klientská knihovna .NET podporuje libovolnou platformu .NET Standard 2.0 nebo .NET 6, včetně:
- Platformy .NET MAUI pro Android, iOS a Windows
- Android API úrovně 21 a novější (Xamarin a Android pro .NET).
- iOS verze 12.0 a novější (Xamarin a iOS pro .NET).
- Univerzální platforma Windows buildy 19041 a novější.
- Windows Presentation Framework (WPF).
- Windows App SDK (WinUI 3).
- Xamarin.Forms
Kromě toho byly vytvořeny vzorky pro Avalonia a Uno Platform. Ukázka TodoAppu obsahuje příklad každé otestované platformy.
Nastavení a požadavky
Přidejte následující knihovny z NuGetu:
- Microsoft.Datasync.Client
- Microsoft.Datasync.Client.SQLiteStore, pokud používáte offline tabulky.
Pokud používáte projekt platformy (například .NET MAUI), ujistěte se, že do projektu platformy a všech sdílených projektů přidáte knihovny.
Vytvoření klienta služby
Následující kód vytvoří klienta služby, který slouží ke koordinaci veškeré komunikace s back-endovými a offline tabulkami.
var options = new DatasyncClientOptions
{
// Options set here
};
var client = new DatasyncClient("MOBILE_APP_URL", options);
V předchozím kódu nahraďte MOBILE_APP_URL
adresou URL back-endu ASP.NET Core back-end. Klient by se měl vytvořit jako jednoúčelový. Pokud používáte zprostředkovatele ověřování, můžete ho nakonfigurovat takto:
var options = new DatasyncClientOptions
{
// Options set here
};
var client = new DatasyncClient("MOBILE_APP_URL", authProvider, options);
Další podrobnosti o zprostředkovateli ověřování najdete dále v tomto dokumentu.
Volby
Úplnou (výchozí) sadu možností je možné vytvořit takto:
var options = new DatasyncClientOptions
{
HttpPipeline = new HttpMessageHandler[](),
IdGenerator = (table) => Guid.NewGuid().ToString("N"),
InstallationId = null,
OfflineStore = null,
ParallelOperations = 1,
SerializerSettings = null,
TableEndpointResolver = (table) => $"/tables/{tableName.ToLowerInvariant()}",
UserAgent = $"Datasync/5.0 (/* Device information */)"
};
HttpPipeline
Za normálních okolností se požadavek HTTP provádí předáním požadavku prostřednictvím zprostředkovatele ověřování (který před odesláním požadavku přidá hlavičku Authorization
pro aktuálně ověřeného uživatele). Volitelně můžete přidat další obslužné rutiny delegování. Každý požadavek před odesláním do služby prochází delegujícími obslužné rutiny. Delegování obslužných rutin umožňuje přidávat další hlavičky, provádět opakování nebo poskytovat možnosti protokolování.
Příklady delegování obslužných rutin jsou k dispozici pro protokolování a přidání hlaviček požadavků dále v tomto článku.
IdGenerator
Když je entita přidána do offline tabulky, musí mít ID. ID se vygeneruje, pokud ho nezadáte. Možnost IdGenerator
umožňuje přizpůsobit ID, které se vygeneruje. Ve výchozím nastavení se vygeneruje globálně jedinečné ID. Například následující nastavení vygeneruje řetězec, který obsahuje název tabulky a identifikátor GUID:
var options = new DatasyncClientOptions
{
IdGenerator = (table) => $"{table}-{Guid.NewGuid().ToString("D").ToUpperInvariant()}"
}
ID instalace
Pokud je nastavená InstallationId
, odešle se s každou žádostí vlastní hlavička X-ZUMO-INSTALLATION-ID
k identifikaci kombinace aplikace na konkrétním zařízení. Tato hlavička se dá zaznamenat v protokolech a umožňuje určit počet jedinečných instalací vaší aplikace. Pokud používáte InstallationId
, mělo by být ID uloženo v trvalém úložišti na zařízení, aby bylo možné sledovat jedinečné instalace.
Offlinestore
OfflineStore
se používá při konfiguraci přístupu k offline datům. Další informace naleznete v tématu Práce s offline tabulkami.
ParallelOperations
Součástí procesu offline synchronizace je nabízení operací zařazených do fronty na vzdálený server. Při aktivaci operace nabízení se operace odesílají v pořadí, v jakém byly přijaty. Volitelně můžete k nasdílení těchto operací použít až osm vláken. Paralelní operace používají více prostředků na klientovi i serveru k rychlejšímu dokončení operace. Pořadí, ve kterém operace přicházejí na server, nelze zaručit při použití více vláken.
SerializerSettings
Pokud jste na serveru synchronizace dat změnili nastavení serializátoru, musíte v klientovi provést stejné změny SerializerSettings
. Tato možnost umožňuje zadat vlastní nastavení serializátoru.
TableEndpointResolver
Podle konvence se tabulky nacházejí ve vzdálené službě v cestě /tables/{tableName}
(jak je specifikováno atributem Route
v kódu serveru). Tabulky však mohou existovat v libovolné cestě koncového bodu.
TableEndpointResolver
je funkce, která změní název tabulky na cestu pro komunikaci se vzdálenou službou.
Například následující změna předpokladu tak, aby všechny tabulky byly umístěny pod /api
:
var options = new DatasyncClientOptions
{
TableEndpointResolver = (table) => $"/api/{table}"
};
UserAgent
Klient synchronizace dat vygeneruje vhodnou hodnotu hlavičky User-Agent na základě verze knihovny. Někteří vývojáři cítí, že hlavička uživatelského agenta nevrací informace o klientovi. Vlastnost UserAgent
můžete nastavit na libovolnou platnou hodnotu záhlaví.
Práce se vzdálenými tabulkami
Následující část podrobně popisuje, jak vyhledávat a načítat záznamy a upravovat data ve vzdálené tabulce. Probíráme následující témata:
- Vytvoření odkazu na tabulku
- dotazování na datové
- Count items from a query
- Vyhledání vzdálených dat podle ID
- Vložení dat na vzdáleném serveru
- Aktualizovat data na vzdáleném serveru
- Odstranění dat na vzdáleném serveru
- řešení konfliktů a optimistická souběžnost
Vytvoření odkazu na vzdálenou tabulku
Pokud chcete vytvořit odkaz na vzdálenou tabulku, použijte GetRemoteTable<T>
:
IRemoteTable<TodoItem> remoteTable = client.GetRemoteTable();
Pokud chcete vrátit tabulku jen pro čtení, použijte IReadOnlyRemoteTable<T>
verzi:
IReadOnlyRemoteTable<TodoItem> remoteTable = client.GetRemoteTable();
Typ modelu musí implementovat kontrakt ITableData
ze služby. Pomocí DatasyncClientData
zadejte požadovaná pole:
public class TodoItem : DatasyncClientData
{
public string Title { get; set; }
public bool IsComplete { get; set; }
}
Objekt DatasyncClientData
zahrnuje:
-
Id
(řetězec) – globálně jedinečné ID položky. -
UpdatedAt
(System.DataTimeOffset) – datum a čas poslední aktualizace položky. -
Version
(řetězec) – neprůzný řetězec použitý pro správu verzí. -
Deleted
(logická hodnota) – pokudtrue
, položka se odstraní.
Služba tato pole udržuje. Tato pole neupravujte jako součást klientské aplikace.
Modely lze anotovat pomocí atributů Newtonsoft.JSON. Název tabulky lze zadat pomocí atributu DataTable
:
[DataTable("todoitem")]
public class MyTodoItemClass : DatasyncClientData
{
public string Title { get; set; }
public bool IsComplete { get; set; }
}
Případně zadejte název tabulky ve volání GetRemoteTable()
:
IRemoteTable<TodoItem> remoteTable = client.GetRemoteTable("todoitem");
Klient používá cestu /tables/{tablename}
jako identifikátor URI. Název tabulky je také název offline tabulky v databázi SQLite.
Podporované typy
Kromě primitivních typů (int, float, string atd.) jsou pro modely podporovány následující typy:
-
System.DateTime
– jako řetězec data a času UTC ve formátu ISO-8601 s přesností ms. -
System.DateTimeOffset
– jako řetězec data a času UTC ve formátu ISO-8601 s přesností ms. -
System.Guid
– formátováno jako 32 číslic oddělených jako pomlčky.
Dotazování dat ze vzdáleného serveru
Vzdálená tabulka se dá použít s příkazy typu LINQ, včetně:
- Filtrování pomocí klauzule
.Where()
- Řazení pomocí různých klauzulí
.OrderBy()
- Výběr vlastností pomocí
.Select()
. - Stránkování s
.Skip()
a.Take()
.
Počítání položek z dotazu
Pokud potřebujete počet položek, které dotaz vrátí, můžete použít .CountItemsAsync()
v tabulce nebo .LongCountAsync()
dotazu:
// Count items in a table.
long count = await remoteTable.CountItemsAsync();
// Count items in a query.
long count = await remoteTable.Where(m => m.Rating == "R").LongCountAsync();
Tato metoda způsobí odezvu na server. Můžete také získat počet při vyplňování seznamu (například), abyste se vyhnuli dodatečnému odezvě:
var enumerable = remoteTable.ToAsyncEnumerable() as AsyncPageable<T>;
var list = new List<T>();
long count = 0;
await foreach (var item in enumerable)
{
count = enumerable.Count;
list.Add(item);
}
Počet se vyplní po prvním požadavku na načtení obsahu tabulky.
Vrácení všech dat
Data se vrací prostřednictvím IAsyncEnumerable:
var enumerable = remoteTable.ToAsyncEnumerable();
await foreach (var item in enumerable)
{
// Process each item
}
K převodu IAsyncEnumerable<T>
na jinou kolekci použijte některou z následujících ukončovací klauzulí:
T[] items = await remoteTable.ToArrayAsync();
Dictionary<string, T> items = await remoteTable.ToDictionaryAsync(t => t.Id);
HashSet<T> items = await remoteTable.ToHashSetAsync();
List<T> items = await remoteTable.ToListAsync();
Vzdálená tabulka na pozadí zpracovává stránkování výsledku za vás. Všechny položky se vrátí bez ohledu na to, kolik požadavků na straně serveru je potřeba k splnění dotazu. Tyto prvky jsou k dispozici také u výsledků dotazu (například remoteTable.Where(m => m.Rating == "R")
).
Architektura synchronizace dat také poskytuje ConcurrentObservableCollection<T>
– pozorovatelnou kolekci bezpečnou pro přístup z více vláken. Tuto třídu lze použít v kontextu aplikací uživatelského rozhraní, které by normálně používaly ObservableCollection<T>
ke správě seznamu (například seznamy Xamarin Forms nebo MAUI).
ConcurrentObservableCollection<T>
můžete vymazat a načíst přímo z tabulky nebo dotazu:
var collection = new ConcurrentObservableCollection<T>();
await remoteTable.ToObservableCollection(collection);
Použití .ToObservableCollection(collection)
aktivuje událost CollectionChanged
jednou pro celou kolekci místo pro jednotlivé položky, což vede k rychlejšímu překreslení času.
ConcurrentObservableCollection<T>
má také predikátové úpravy:
// Add an item only if the identified item is missing.
bool modified = collection.AddIfMissing(t => t.Id == item.Id, item);
// Delete one or more item(s) based on a predicate
bool modified = collection.DeleteIf(t => t.Id == item.Id);
// Replace one or more item(s) based on a predicate
bool modified = collection.ReplaceIf(t => t.Id == item.Id, item);
Predikátové úpravy lze použít v obslužných rutinách událostí, pokud index položky není předem známý.
Filtrování dat
K filtrování dat můžete použít klauzuli .Where()
. Například:
var items = await remoteTable.Where(x => !x.IsComplete).ToListAsync();
Filtrování se provádí ve službě před IAsyncEnumerable a v klientovi po IAsyncEnumerable. Například:
var items = (await remoteTable.Where(x => !x.IsComplete).ToListAsync()).Where(x => x.Title.StartsWith("The"));
První klauzule .Where()
(vrácení pouze neúplných položek) se provádí ve službě, zatímco druhá klauzule .Where()
(počínaje "The") se provádí v klientovi.
Klauzule Where
podporuje operace, které se přeloží do podmnožina OData. Mezi operace patří:
- Relační operátory (
==
,!=
,<
,<=
,>
,>=
), - Aritmetické operátory (
+
,-
,/
,*
,%
), - Přesnost čísel (
Math.Floor
,Math.Ceiling
), - Řetězcové funkce (
Length
,Substring
,Replace
,IndexOf
,Equals
,StartsWith
,EndsWith
) (pouze řadové a invariantní jazykové verze), - Vlastnosti data (
Year
,Month
,Day
,Hour
,Minute
,Second
), - Přístup k vlastnostem objektu a
- Výrazy kombinující kteroukoli z těchto operací.
Řazení dat
K řazení dat použijte .OrderBy()
, .OrderByDescending()
, .ThenBy()
a .ThenByDescending()
s příslušenstvím vlastnosti.
var items = await remoteTable.OrderBy(x => x.IsComplete).ThenBy(x => x.Title).ToListAsync();
Řazení provádí služba. Výraz nelze zadat v žádné klauzuli řazení. Pokud chcete řadit podle výrazu, použijte řazení na straně klienta:
var items = await remoteTable.ToListAsync().OrderBy(x => x.Title.ToLowerCase());
Výběr vlastností
Ze služby můžete vrátit podmnožinu dat:
var items = await remoteTable.Select(x => new { x.Id, x.Title, x.IsComplete }).ToListAsync();
Vrácení stránky dat
K implementaci stránkování můžete vrátit podmnožinu datové sady pomocí .Skip()
a .Take()
:
var pageOfItems = await remoteTable.Skip(100).Take(10).ToListAsync();
V reálné aplikaci můžete k navigaci mezi stránkami použít dotazy podobné předchozímu příkladu pomocí ovládacího prvku pager nebo srovnatelného uživatelského rozhraní.
Všechny dosud popsané funkce jsou přídatné, takže je můžeme dál zřetězovat. Každé zřetězený volání ovlivňuje více dotazu. Jeden další příklad:
var query = todoTable
.Where(todoItem => todoItem.Complete == false)
.Select(todoItem => todoItem.Text)
.Skip(3).
.Take(3);
List<string> items = await query.ToListAsync();
Vyhledání vzdálených dat podle ID
Funkci GetItemAsync
lze použít k vyhledání objektů z databáze s konkrétním ID.
TodoItem item = await remoteTable.GetItemAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D");
Pokud byla položka, kterou se pokoušíte načíst, obnovitelně odstraněna, musíte použít parametr includeDeleted
:
// The following code will throw a DatasyncClientException if the item is soft-deleted.
TodoItem item = await remoteTable.GetItemAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D");
// This code will retrieve the item even if soft-deleted.
TodoItem item = await remoteTable.GetItemAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D", includeDeleted: true);
Vložení dat na vzdálený server
Všechny typy klientů musí obsahovat člen s názvem ID, což je ve výchozím nastavení řetězec. Toto id se vyžaduje k provádění operací CRUD a k offline synchronizaci. Následující kód ukazuje, jak pomocí metody InsertItemAsync
vložit nové řádky do tabulky. Parametr obsahuje data, která se mají vložit jako objekt .NET.
var item = new TodoItem { Title = "Text", IsComplete = false };
await remoteTable.InsertItemAsync(item);
// Note that item.Id will now be set
Pokud v item
během vložení není zahrnuta jedinečná hodnota vlastního ID, server vygeneruje ID. Vygenerované ID můžete načíst kontrolou objektu po vrácení volání.
Aktualizace dat na vzdáleném serveru
Následující kód ukazuje, jak použít metodu ReplaceItemAsync
k aktualizaci existujícího záznamu se stejným ID s novými informacemi.
// In this example, we assume the item has been created from the InsertItemAsync sample
item.IsComplete = true;
await remoteTable.ReplaceItemAsync(todoItem);
Odstranění dat na vzdáleném serveru
Následující kód ukazuje, jak pomocí metody DeleteItemAsync
odstranit existující instanci.
// In this example, we assume the item has been created from the InsertItemAsync sample
await todoTable.DeleteItemAsync(item);
Řešení konfliktů a optimistická souběžnost
Dva nebo více klientů můžou zapisovat změny do stejné položky současně. Bez detekce konfliktů by poslední zápis přepsal všechny předchozí aktualizace. optimistické řízení souběžnosti předpokládá, že každá transakce může potvrdit, a proto nepoužívá žádné uzamčení prostředků. Optimistická kontrola souběžnosti ověřuje, že žádná jiná transakce neupravila data před potvrzením dat. Pokud se data změnila, transakce se vrátí zpět.
Azure Mobile Apps podporuje optimistické řízení souběžnosti sledováním změn jednotlivých položek pomocí sloupce vlastností systému version
definovaného pro každou tabulku v back-endu mobilní aplikace. Při každé aktualizaci záznamu nastaví Mobile Apps vlastnost version
pro tento záznam na novou hodnotu. Během každé žádosti o aktualizaci se version
vlastnost záznamu zahrnutého do požadavku porovná se stejnou vlastností záznamu na serveru. Pokud se verze předaná požadavkem neshoduje s back-endem, klientská knihovna vyvolá DatasyncConflictException<T>
výjimku. Typ zahrnutý s výjimkou je záznam z back-endu obsahujícího verzi serveru záznamu. Aplikace pak může pomocí těchto informací rozhodnout, jestli se má žádost o aktualizaci spustit znovu se správnou version
hodnotou z back-endu a potvrdit změny.
Optimistická souběžnost se automaticky povolí při použití základního objektu DatasyncClientData
.
Kromě povolení optimistické souběžnosti musíte také zachytit výjimku DatasyncConflictException<T>
v kódu. Vyřešte konflikt použitím správného version
na aktualizovaný záznam a potom volání opakujte s vyřešeným záznamem. Následující kód ukazuje, jak po zjištění vyřešit konflikt zápisu:
private async void UpdateToDoItem(TodoItem item)
{
DatasyncConflictException<TodoItem> exception = null;
try
{
//update at the remote table
await remoteTable.UpdateAsync(item);
}
catch (DatasyncConflictException<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();
}
Práce s offline tabulkami
Offline tabulky používají místní úložiště SQLite k ukládání dat pro použití v offline režimu. Všechny operace tabulek se provádějí s místním úložištěm SQLite místo vzdáleného úložiště serveru. Ujistěte se, že do každého projektu platformy a do všech sdílených projektů přidáte Microsoft.Datasync.Client.SQLiteStore
.
Před vytvořením odkazu na tabulku musí být místní úložiště připravené:
var store = new OfflineSQLiteStore(Constants.OfflineConnectionString);
store.DefineTable<TodoItem>();
Po definování úložiště můžete vytvořit klienta:
var options = new DatasyncClientOptions
{
OfflineStore = store
};
var client = new DatasyncClient("MOBILE_URL", options);
Nakonec musíte zajistit, aby se inicializovaly možnosti offline:
await client.InitializeOfflineStoreAsync();
Inicializace úložiště se obvykle provádí okamžitě po vytvoření klienta. OfflineConnectionString je identifikátor URI, který slouží k určení umístění databáze SQLite i možností použitých k otevření databáze. Další informace najdete v tématu názvy souborů identifikátorů URI vSQLite .
- Pokud chcete použít mezipaměť v paměti, použijte
file:inmemory.db?mode=memory&cache=private
. - Pokud chcete použít soubor, použijte
file:/path/to/file.db
Musíte zadat absolutní název souboru. Pokud používáte Xamarin, můžete použít pomocné rutiny systému souborů Xamarin Essentials vytvořit cestu: Například:
var dbPath = $"{Filesystem.AppDataDirectory}/todoitems.db";
var store = new OfflineSQLiteStore($"file:/{dbPath}?mode=rwc");
Pokud používáte MAUI, můžete k vytvoření cesty použít pomocných rutin systému souborů MAUI: Například:
var dbPath = $"{Filesystem.AppDataDirectory}/todoitems.db";
var store = new OfflineSQLiteStore($"file:/{dbPath}?mode=rwc");
Vytvoření offline tabulky
Odkaz na tabulku lze získat pomocí metody GetOfflineTable<T>
:
IOfflineTable<TodoItem> table = client.GetOfflineTable<TodoItem>();
Stejně jako u vzdálené tabulky můžete také vystavit offline tabulku jen pro čtení:
IReadOnlyOfflineTable<TodoItem> table = client.GetOfflineTable<TodoItem>();
Abyste mohli používat offline tabulku, nemusíte se ověřovat. Při komunikaci s back-endovou službou je potřeba provést ověření.
Synchronizace offline tabulky
Offline tabulky se ve výchozím nastavení nesynchronují s back-endem. Synchronizace je rozdělená na dvě části. Změny můžete odesílat odděleně od stahování nových položek. Například:
public async Task SyncAsync()
{
ReadOnlyCollection<TableOperationError> syncErrors = null;
try
{
foreach (var offlineTable in offlineTables.Values)
{
await offlineTable.PushItemsAsync();
await offlineTable.PullItemsAsync("", options);
}
}
catch (PushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == TableOperationKind.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"]);
}
}
}
Ve výchozím nastavení používají všechny tabulky přírůstkovou synchronizaci – načtou se jenom nové záznamy. Záznam je součástí každého jedinečného dotazu (vygenerovaného vytvořením hodnoty hash MD5 dotazu OData).
Poznámka
Prvním argumentem PullItemsAsync
je dotaz OData, který indikuje, které záznamy se mají na zařízení vyžádat. Je lepší službu upravit tak, aby vracela jenom záznamy specifické pro uživatele, a ne vytvářet složité dotazy na straně klienta.
Možnosti (definované objektem PullOptions
) se obvykle nemusí nastavovat. Mezi možnosti patří:
-
PushOtherTables
– pokud je nastavená hodnota true, všechny tabulky se nasdílí. -
QueryId
– konkrétní ID dotazu, které se má místo vygenerovaného použít. -
WriteDeltaTokenInterval
– jak často se má zapisovat rozdílový token použitý ke sledování přírůstkové synchronizace.
Sada SDK provádí implicitní PushAsync()
před vyžádáním záznamů.
Zpracování konfliktů probíhá u metody PullAsync()
. Zpracovává konflikty stejným způsobem jako online tabulky. Konflikt se vytvoří, když se místo během vkládání, aktualizace nebo odstranění volá PullAsync()
. Pokud dojde k několika konfliktům, jsou spojeny do jednoho PushFailedException
. Zpracujte jednotlivé chyby samostatně.
Nasdílení změn pro všechny tabulky
Pokud chcete odeslat všechny změny na vzdálený server, použijte:
await client.PushTablesAsync();
Pokud chcete odeslat změny pro podmnožinu tabulek, zadejte IEnumerable<string>
metodě PushTablesAsync()
:
var tablesToPush = new string[] { "TodoItem", "Notes" };
await client.PushTables(tablesToPush);
Pomocí vlastnosti client.PendingOperations
můžete přečíst počet operací čekajících na odeslání do vzdálené služby. Tato vlastnost je null
, pokud není nakonfigurované žádné offline úložiště.
Spouštění složitých dotazů SQLite
Pokud potřebujete provádět složité dotazy SQL na offline databázi, můžete to udělat pomocí metody ExecuteQueryAsync()
. Pokud chcete například provést příkaz SQL JOIN
, definujte JObject
, který zobrazuje strukturu návratové hodnoty, a pak použijte ExecuteQueryAsync()
:
var definition = new JObject()
{
{ "id", string.Empty },
{ "title", string.Empty },
{ "first_name", string.Empty },
{ "last_name", string.Empty }
};
var sqlStatement = "SELECT b.id as id, b.title as title, a.first_name as first_name, a.last_name as last_name FROM books b INNER JOIN authors a ON b.author_id = a.id ORDER BY b.id";
var items = await store.ExecuteQueryAsync(definition, sqlStatement, parameters);
// Items is an IList<JObject> where each JObject conforms to the definition.
Definice je sada klíčů/hodnot. Klíče musí odpovídat názvům polí, které dotaz SQL vrátí, a hodnoty musí být výchozí hodnotou očekávaného typu. Použijte 0L
pro čísla (dlouhá), false
pro logické hodnoty a string.Empty
pro všechno ostatní.
SQLite má omezující sadu podporovaných typů. Datum a časy se ukládají jako počet milisekund od epochy, aby bylo možné porovnávat.
Ověřování uživatelů
Azure Mobile Apps umožňuje vygenerovat zprostředkovatele ověřování pro zpracování volání ověřování. Při vytváření klienta služby zadejte zprostředkovatele ověřování:
AuthenticationProvider authProvider = GetAuthenticationProvider();
var client = new DatasyncClient("APP_URL", authProvider);
Pokaždé, když se vyžaduje ověřování, zavolá se zprostředkovatel ověřování, který token získá. Pro ověřování založené na autorizační hlavičce i ověřování na základě autorizace a ověřování na základě autorizace je možné použít obecného zprostředkovatele ověřování. Použijte následující model:
public AuthenticationProvider GetAuthenticationProvider()
=> new GenericAuthenticationProvider(GetTokenAsync);
// Or, if using Azure App Service Authentication and Authorization
// public AuthenticationProvider GetAuthenticationProvider()
// => new GenericAuthenticationProvider(GetTokenAsync, "X-ZUMO-AUTH");
public async Task<AuthenticationToken> GetTokenAsync()
{
// TODO: Any code necessary to get the right access token.
return new AuthenticationToken
{
DisplayName = "/* the display name of the user */",
ExpiresOn = DateTimeOffset.Now.AddHours(1), /* when does the token expire? */
Token = "/* the access token */",
UserId = "/* the user id of the connected user */"
};
}
Ověřovací tokeny se ukládají do mezipaměti (nikdy se nezapisují do zařízení) a v případě potřeby se aktualizují.
Použití platformy Microsoft Identity Platform
Platforma Microsoft Identity Platform umožňuje snadnou integraci s ID Microsoft Entra. Kompletní kurz implementace ověřování Microsoft Entra najdete v úvodních kurzech. Následující kód ukazuje příklad načtení přístupového tokenu:
private readonly string[] _scopes = { /* provide your AAD scopes */ };
private readonly object _parentWindow; /* Fill in with the required object before using */
private readonly PublicClientApplication _pca; /* Create one */
public MyAuthenticationHelper(object parentWindow)
{
_parentWindow = parentWindow;
_pca = PublicClientApplicationBuilder.Create(clientId)
.WithRedirectUri(redirectUri)
.WithAuthority(authority)
/* Add options methods here */
.Build();
}
public async Task<AuthenticationToken> GetTokenAsync()
{
// Silent authentication
try
{
var account = await _pca.GetAccountsAsync().FirstOrDefault();
var result = await _pca.AcquireTokenSilent(_scopes, account).ExecuteAsync();
return new AuthenticationToken
{
ExpiresOn = result.ExpiresOn,
Token = result.AccessToken,
UserId = result.Account?.Username ?? string.Empty
};
}
catch (Exception ex) when (exception is not MsalUiRequiredException)
{
// Handle authentication failure
return null;
}
// UI-based authentication
try
{
var account = await _pca.AcquireTokenInteractive(_scopes)
.WithParentActivityOrWindow(_parentWindow)
.ExecuteAsync();
return new AuthenticationToken
{
ExpiresOn = result.ExpiresOn,
Token = result.AccessToken,
UserId = result.Account?.Username ?? string.Empty
};
}
catch (Exception ex)
{
// Handle authentication failure
return null;
}
}
Další informace o integraci platformy Microsoft Identity Platform s ASP.NET 6 najdete v dokumentaci k platformě Microsoft Identity Platform.
Použití Xamarin Essentials nebo MAUI WebAuthenticator
Pro ověřování azure App Service můžete k získání tokenu použít
Uri authEndpoint = new Uri(client.Endpoint, "/.auth/login/aad");
Uri callback = new Uri("myapp://easyauth.callback");
public async Task<AuthenticationToken> GetTokenAsync()
{
var authResult = await WebAuthenticator.AuthenticateAsync(authEndpoint, callback);
return new AuthenticationToken
{
ExpiresOn = authResult.ExpiresIn,
Token = authResult.AccessToken
};
}
UserId
a DisplayName
nejsou při použití ověřování služby Azure App Service přímo dostupné. Místo toho použijte opožděný žadatel k načtení informací z koncového bodu /.auth/me
:
var userInfo = new AsyncLazy<UserInformation>(() => GetUserInformationAsync());
public async Task<UserInformation> GetUserInformationAsync()
{
// Get the token for the current user
var authInfo = await GetTokenAsync();
// Construct the request
var request = new HttpRequestMessage(HttpMethod.Get, new Uri(client.Endpoint, "/.auth/me"));
request.Headers.Add("X-ZUMO-AUTH", authInfo.Token);
// Create a new HttpClient, then send the request
var httpClient = new HttpClient();
var response = await httpClient.SendAsync(request);
// If the request is successful, deserialize the content into the UserInformation object.
// You will have to create the UserInformation class.
if (response.IsSuccessStatusCode)
{
var content = await response.ReadAsStringAsync();
return JsonSerializer.Deserialize<UserInformation>(content);
}
}
Pokročilá témata
Vymazání entit v místní databázi
V normálním provozu není potřeba vyprázdnit entity. Proces synchronizace odebere odstraněné entity a udržuje požadovaná metadata pro tabulky místní databáze. Existují ale chvíle, kdy je užitečné vyprázdnit entity v databázi. Jedním z takových scénářů je, když potřebujete odstranit velký počet entit a je efektivnější vymazat data z tabulky místně.
K vymazání záznamů z tabulky použijte table.PurgeItemsAsync()
:
var query = table.CreateQuery();
var purgeOptions = new PurgeOptions();
await table.PurgeItermsAsync(query, purgeOptions, cancellationToken);
Dotaz identifikuje entity, které se mají z tabulky odebrat. Identifikujte entity, které se mají vyprázdnit pomocí LINQ:
var query = table.CreateQuery().Where(m => m.Archived == true);
Třída PurgeOptions
poskytuje nastavení pro úpravu operace vyprázdnění:
-
DiscardPendingOperations
zahodí všechny čekající operace pro tabulku, která je ve frontě operací, která čeká na odeslání na server. -
QueryId
určuje ID dotazu, které slouží k identifikaci rozdílového tokenu, který se má pro operaci použít. -
TimestampUpdatePolicy
určuje, jak upravit rozdílový token na konci operace vyprázdnění:-
TimestampUpdatePolicy.NoUpdate
indikuje, že rozdílový token nesmí být aktualizován. -
TimestampUpdatePolicy.UpdateToLastEntity
označuje, že by se rozdílový token měl aktualizovat na poleupdatedAt
poslední entity uložené v tabulce. -
TimestampUpdatePolicy.UpdateToNow
označuje, že by se rozdílový token měl aktualizovat na aktuální datum a čas. -
TimestampUpdatePolicy.UpdateToEpoch
indikuje, že rozdílový token by se měl resetovat, aby se synchronizovala všechna data.
-
Použijte stejnou QueryId
hodnotu, kterou jste použili při volání table.PullItemsAsync()
k synchronizaci dat.
QueryId
určuje rozdílový token, který se má aktualizovat po dokončení vyprázdnění.
Přizpůsobení hlaviček požadavků
Pokud chcete podporovat konkrétní scénář aplikace, možná budete muset přizpůsobit komunikaci s back-endem mobilní aplikace. Můžete například přidat vlastní hlavičku do každého odchozího požadavku nebo změnit stavové kódy odpovědi před návratem k uživateli. Použijte vlastní delegování obslužné rutiny, jako v následujícím příkladu:
public async Task CallClientWithHandler()
{
var options = new DatasyncClientOptions
{
HttpPipeline = new DelegatingHandler[] { new MyHandler() }
};
var client = new Datasync("AppUrl", options);
var todoTable = client.GetRemoteTable<TodoItem>();
var newItem = new TodoItem { Text = "Hello world", Complete = false };
await todoTable.InsertItemAsync(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;
}
}
Povolení protokolování požadavků
Protokolování požadavku můžete přidat také pomocí delegační obslužné rutiny:
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;
}
}
Monitorování událostí synchronizace
Když dojde k události synchronizace, událost se publikuje do delegáta client.SynchronizationProgress
události. Události lze použít ke sledování průběhu procesu synchronizace. Obslužnou rutinu události synchronizace definujte následujícím způsobem:
client.SynchronizationProgress += (sender, args) => {
// args is of type SynchronizationEventArgs
};
Typ SynchronizationEventArgs
je definován takto:
public enum SynchronizationEventType
{
PushStarted,
ItemWillBePushed,
ItemWasPushed,
PushFinished,
PullStarted,
ItemWillBeStored,
ItemWasStored,
PullFinished
}
public class SynchronizationEventArgs
{
public SynchronizationEventType EventType { get; }
public string ItemId { get; }
public long ItemsProcessed { get; }
public long QueueLength { get; }
public string TableName { get; }
public bool IsSuccessful { get; }
}
Vlastnosti v rámci args
jsou buď null
, nebo -1
, pokud tato vlastnost není relevantní pro událost synchronizace.