.NET için Azure Mobile Apps istemci kitaplığını kullanma

Bu kılavuz, Azure Mobile Apps için .NET istemci kitaplığını kullanarak yaygın senaryoları nasıl gerçekleştirebileceğinizi gösterir. MAUI, Xamarin ve Windows (WPF, UWP ve WinUI) dahil olmak üzere herhangi bir .NET 6 veya .NET Standard 2.0 uygulamasında .NET istemci kitaplığını kullanın.

Azure Mobile Apps'i yeni kullanmaya başlamanız durumunda ilk olarak hızlı başlangıç öğreticilerinden birini tamamlamalısınız:

Dekont

Bu makale, Microsoft Datasync Framework'ün en son (v6.0) sürümünü kapsar. Eski istemciler için v4.2.0 belgelerine bakın.

Desteklenen platformlar

.NET istemci kitaplığı, aşağıdakiler dahil olmak üzere herhangi bir .NET Standard 2.0 veya .NET 6 platformunu destekler:

  • Android, iOS ve Windows platformları için .NET MAUI.
  • Android API düzeyi 21 ve üzeri (Xamarin ve .NET için Android).
  • iOS sürüm 12.0 ve üzeri (.NET için Xamarin ve iOS).
  • Evrensel Windows Platformu 19041 ve üzeri derlemeler.
  • Windows Presentation Framework (WPF).
  • Windows Uygulama SDK'sı (WinUI 3).
  • Xamarin.Forms

Ayrıca, Avalonia ve Uno Platformu için örnekler oluşturulmuştur. TodoApp örneği, test edilen her platformun bir örneğini içerir.

Kurulum ve Önkoşullar

NuGet'ten aşağıdaki kitaplıkları ekleyin:

Bir platform projesi (örneğin, .NET MAUI) kullanıyorsanız kitaplıkları platform projesine ve paylaşılan herhangi bir projeye eklediğinizden emin olun.

Hizmet istemcisini oluşturma

Aşağıdaki kod, arka uç ve çevrimdışı tablolarla tüm iletişimi koordine etmek için kullanılan hizmet istemcisini oluşturur.

var options = new DatasyncClientOptions 
{
    // Options set here
};
var client = new DatasyncClient("MOBILE_APP_URL", options);

Yukarıdaki kodda değerini ASP.NET Core arka ucu URL'si ile değiştirinMOBILE_APP_URL. İstemci tekil olarak oluşturulmalıdır. Kimlik doğrulama sağlayıcısı kullanılıyorsa, şu şekilde yapılandırılabilir:

var options = new DatasyncClientOptions 
{
    // Options set here
};
var client = new DatasyncClient("MOBILE_APP_URL", authProvider, options);

Kimlik doğrulama sağlayıcısıyla ilgili diğer ayrıntılar bu belgenin ilerleyen bölümlerinde verilmiştir.

Seçenekler

Aşağıdaki gibi eksiksiz (varsayılan) bir seçenek kümesi oluşturulabilir:

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

Normalde bir HTTP isteği, isteği göndermeden önce kimlik doğrulama sağlayıcısından geçirilerek (şu anda kimliği doğrulanmış kullanıcının üst bilgisini ekleyen Authorization ) yapılır. İsteğe bağlı olarak, daha fazla temsilci işleyicisi ekleyebilirsiniz. Her istek, hizmete gönderilmeden önce temsilci işleyicilerinden geçer. temsilci atama işleyicileri ek üst bilgiler eklemenize, yeniden deneme yapmanıza veya günlüğe kaydetme özellikleri sağlamanıza olanak sağlar.

Bu makalenin devamında günlüğe kaydetme ve istek üst bilgileri ekleme için temsilci işleyicileri örnekleri verilmiştir.

IdGenerator

Bir varlık çevrimdışı tabloya eklendiğinde, bir kimliği olmalıdır. Kimlik sağlanmayan bir kimlik oluşturulur. IdGenerator seçeneği, oluşturulan kimliği uyarlamanıza olanak tanır. Varsayılan olarak, genel olarak benzersiz bir kimlik oluşturulur. Örneğin, aşağıdaki ayar tablo adını ve GUID'yi içeren bir dize oluşturur:

var options = new DatasyncClientOptions 
{
    IdGenerator = (table) => $"{table}-{Guid.NewGuid().ToString("D").ToUpperInvariant()}"
}

Installationıd

InstallationId ayarlanırsa, belirli bir cihazdaki uygulamanın birleşimini tanımlamak için her istekle birlikte özel bir üst bilgi X-ZUMO-INSTALLATION-ID gönderilir. Bu üst bilgi günlüklere kaydedilebilir ve uygulamanız için ayrı yükleme sayısını belirlemenize olanak tanır. kullanıyorsanız InstallationId, benzersiz yüklemelerin izlenebilmesi için kimlik cihazda kalıcı depolamada depolanmalıdır.

OfflineStore

OfflineStore çevrimdışı veri erişimi yapılandırılırken kullanılır. Daha fazla bilgi için bkz . Çevrimdışı tablolarla çalışma.

ParallelOperations

Çevrimdışı eşitleme işleminin bir bölümü, kuyruğa alınmış işlemleri uzak sunucuya göndermeyi içerir. Gönderme işlemi tetiklendiğinde, işlemler alındıkları sırayla gönderilir. İsteğe bağlı olarak, bu işlemleri göndermek için en fazla sekiz iş parçacığı kullanabilirsiniz. Paralel işlemler, işlemi daha hızlı tamamlamak için hem istemcide hem de sunucuda daha fazla kaynak kullanır. Birden çok iş parçacığı kullanılırken işlemlerin sunucuya ulaşma sırası garanti edilemez.

Seri hale getirici Ayarlar

Veri eşitleme sunucusunda seri hale getirici ayarlarını değiştirdiyseniz istemcide de aynı değişiklikleri SerializerSettings yapmanız gerekir. Bu seçenek, kendi seri hale getirici ayarlarınızı belirtmenize olanak tanır.

TableEndpointResolver

Kural gereği, tablolar yoldaki uzak hizmette /tables/{tableName} bulunur (sunucu kodunda Route özniteliği tarafından belirtildiği gibi). Ancak, tablolar herhangi bir uç nokta yolunda bulunabilir. TableEndpointResolver, bir tablo adını uzak hizmetle iletişim kurmak için bir yola dönüştüren bir işlevdir.

Örneğin, aşağıdakiler varsayımı değiştirerek tüm tabloların altında bulunmasına /apineden olur:

var options = new DatasyncClientOptions
{
    TableEndpointResolver = (table) => $"/api/{table}"
};

UserAgent

Veri eşitleme istemcisi, kitaplığın sürümüne göre uygun bir User-Agent üst bilgi değeri oluşturur. Bazı geliştiriciler, kullanıcı aracısı üst bilgisinin istemci hakkında bilgi sızdırdığı görüşündedir. Özelliğini geçerli herhangi bir üst bilgi değerine ayarlayabilirsiniz UserAgent .

Uzak tablolarla çalışma

Aşağıdaki bölümde, uzak bir tablodaki kayıtların nasıl aranıp alınıp değiştirileceği ve verilerin nasıl değiştirileceği ayrıntılı olarak anlatlanmıştır. Aşağıdaki konular ele alınmıştır:

Uzak tablo başvurusu oluşturma

Uzak tablo başvurusu oluşturmak için kullanın GetRemoteTable<T>:

IRemoteTable<TodoItem> remoteTable = client.GetRemoteTable();

Salt okunur bir tablo döndürmek istiyorsanız şu sürümü kullanın IReadOnlyRemoteTable<T> :

IReadOnlyRemoteTable<TodoItem> remoteTable = client.GetRemoteTable();

Model türünün hizmetten sözleşmeyi ITableData uygulaması gerekir. Gerekli alanları sağlamak için kullanın DatasyncClientData :

public class TodoItem : DatasyncClientData
{
    public string Title { get; set; }
    public bool IsComplete { get; set; }
}

DatasyncClientData Nesnesi şunları içerir:

  • Id (dize) - öğe için genel olarak benzersiz bir kimlik.
  • UpdatedAt (System.DataTimeOffset) - Öğenin son güncelleştirilmiş olduğu tarih/saat.
  • Version (dize) - sürüm oluşturma için kullanılan opak bir dize.
  • Deleted (boole) - ise true, öğe silinir.

Hizmet bu alanları korur. Bu alanları istemci uygulamanızın bir parçası olarak ayarlamayın.

Modellere Newtonsoft.JSON öznitelikleri kullanılarak açıklama eklenebilir. Tablonun adı özniteliği kullanılarak DataTable belirtilebilir:

[DataTable("todoitem")]
public class MyTodoItemClass : DatasyncClientData
{
    public string Title { get; set; }
    public bool IsComplete { get; set; }
}

Alternatif olarak, çağrıda GetRemoteTable() tablonun adını belirtin:

IRemoteTable<TodoItem> remoteTable = client.GetRemoteTable("todoitem");

İstemci, yolu /tables/{tablename} URI olarak kullanır. Tablo adı aynı zamanda SQLite veritabanındaki çevrimdışı tablonun adıdır.

Desteklenen türler

İlkel türlerin (int, float, dize vb.) yanı sıra, modeller için aşağıdaki türler desteklenir:

  • System.DateTime - ms doğruluğuna sahip bir ISO-8601 UTC tarih/saat dizesi olarak.
  • System.DateTimeOffset - ms doğruluğuna sahip bir ISO-8601 UTC tarih/saat dizesi olarak.
  • System.Guid - kısa çizgi olarak ayrılmış 32 basamak olarak biçimlendirilir.

Uzak sunucudan veri sorgulama

Uzak tablo LINQ benzeri deyimlerle kullanılabilir, örneğin:

  • Yan .Where() tümcesi ile filtreleme.
  • Çeşitli .OrderBy() yan tümcelerle sıralama.
  • ile .Select()özellikleri seçme.
  • ve .Take()ile .Skip() disk belleği oluşturma.

Sorgudaki öğeleri sayma

Sorgunun döndüreceği öğelerin sayısına ihtiyacınız varsa, bir tabloda veya .LongCountAsync() sorguda kullanabilirsiniz.CountItemsAsync():

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

Bu yöntem sunucuya gidiş dönüşe neden olur. Ayrıca, fazladan gidiş dönüşten kaçınarak bir liste doldurarak (örneğin) bir sayım alabilirsiniz:

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

Tablo içeriğini almak için yapılan ilk istek sonrasında sayı doldurulur.

Tüm verileri döndürme

Veriler bir IAsyncEnumerable aracılığıyla döndürülür:

var enumerable = remoteTable.ToAsyncEnumerable();
await foreach (var item in enumerable) 
{
    // Process each item
}

öğesini farklı bir koleksiyona dönüştürmek IAsyncEnumerable<T> için aşağıdaki sonlandırma yan tümcelerinden herhangi birini kullanın:

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

Arka planda, uzak tablo sonucun disk belleğini sizin için işler. Sorguyu gerçekleştirmek için kaç sunucu tarafı isteği gerektiğinden bağımsız olarak tüm öğeler döndürülür. Bu öğeler sorgu sonuçlarında da kullanılabilir (örneğin, remoteTable.Where(m => m.Rating == "R")).

Veri eşitleme çerçevesi ayrıca iş parçacığı açısından güvenli bir gözlemlenebilir koleksiyon sağlar ConcurrentObservableCollection<T> . Bu sınıf, normalde bir listeyi (örneğin, Xamarin Forms veya MAUI listeleri) yönetmek için kullanabilecek ObservableCollection<T> kullanıcı arabirimi uygulamaları bağlamında kullanılabilir. Bir tabloyu veya sorguyu doğrudan temizleyip yükleyebilirsiniz ConcurrentObservableCollection<T> :

var collection = new ConcurrentObservableCollection<T>();
await remoteTable.ToObservableCollection(collection);

Kullanmak .ToObservableCollection(collection) , olayı tek tek öğeler yerine koleksiyonun tamamı için bir kez tetikler CollectionChanged ve daha hızlı bir yeniden çizim süresi elde eder.

ayrıca ConcurrentObservableCollection<T> koşul temelli değişikliklere sahiptir:

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

Öğenin dizini önceden bilinmediğinde, koşul temelli değişiklikler olay işleyicilerinde kullanılabilir.

Verileri filtreleme

Verileri filtrelemek için bir .Where() yan tümce kullanabilirsiniz. Örneğin:

var items = await remoteTable.Where(x => !x.IsComplete).ToListAsync();

Filtreleme, IAsyncEnumerable öncesinde hizmette ve IAsyncEnumerable sonrasında istemcide yapılır. Örneğin:

var items = (await remoteTable.Where(x => !x.IsComplete).ToListAsync()).Where(x => x.Title.StartsWith("The"));

İlk .Where() yan tümce (yalnızca tamamlanmamış öğeleri döndür) hizmette yürütülürken, ikinci .Where() yan tümce ("The" ile başlayarak) istemcide yürütülür.

Where yan tümcesi, OData alt kümesine çevrilen işlemleri destekler. İşlemler şunlardır:

  • İlişkisel işleçler (==, !=, <, <=, >, >=),
  • Aritmetik işleçler (+, -, /, *, %),
  • Sayı duyarlığı (Math.Floor, Math.Ceiling),
  • Dize işlevleri (Length, Substring, Replace, IndexOf, , Equals, StartsWith, EndsWith) (yalnızca sıralı ve sabit kültürler),
  • Tarih özellikleri (Year, Month, Day, Hour, Minute, ), Second
  • Bir nesnenin özelliklerine erişme ve
  • Bu işlemlerden herhangi birini birleştiren ifadeler.

Verileri sıralama

Verileri sıralamak için bir özellik erişimcisi ile , .OrderByDescending(), .ThenBy()ve .ThenByDescending() kullanın.OrderBy().

var items = await remoteTable.OrderBy(x => x.IsComplete).ThenBy(x => x.Title).ToListAsync();

Sıralama, hizmet tarafından gerçekleştirilir. Herhangi bir sıralama yan tümcesinde ifade belirtemezsiniz. bir ifadeye göre sıralamak istiyorsanız istemci tarafı sıralamayı kullanın:

var items = await remoteTable.ToListAsync().OrderBy(x => x.Title.ToLowerCase());

Özellikleri seçme

Hizmetten bir veri alt kümesi döndürebilirsiniz:

var items = await remoteTable.Select(x => new { x.Id, x.Title, x.IsComplete }).ToListAsync();

Veri sayfası döndürme

disk belleği uygulamak için ve .Take() kullanarak .Skip() veri kümesinin bir alt kümesini döndürebilirsiniz:

var pageOfItems = await remoteTable.Skip(100).Take(10).ToListAsync();

Gerçek bir uygulamada, sayfalar arasında gezinmek için bir çağrıyıcı denetimi veya karşılaştırılabilir kullanıcı arabirimi ile önceki örneğe benzer sorguları kullanabilirsiniz.

Şimdiye kadar açıklanan tüm işlevler eklenmiştir, bu nedenle bunları zincirlemeye devam edebiliriz. Zincirlenmiş her çağrı sorgunun daha fazlasını etkiler. Bir örnek daha:

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

Kimliğine göre uzak verileri arama

işlevi, GetItemAsync belirli bir kimlikle veritabanındaki nesneleri aramak için kullanılabilir.

TodoItem item = await remoteTable.GetItemAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D");

Almaya çalıştığınız öğe geçici olarak silinmişse parametresini includeDeleted kullanmanız gerekir:

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

Uzak sunucuya veri ekleme

Tüm istemci türleri, varsayılan olarak bir dize olan Id adlı bir üye içermelidir. CruD işlemlerini gerçekleştirmek ve çevrimdışı eşitleme için bu kimlik gereklidir. Aşağıdaki kod, bir tabloya yeni satırlar eklemek için yönteminin nasıl kullanılacağını InsertItemAsync gösterir. parametresi, .NET nesnesi olarak eklenecek verileri içerir.

var item = new TodoItem { Title = "Text", IsComplete = false };
await remoteTable.InsertItemAsync(item);
// Note that item.Id will now be set

Ekleme sırasında içinde benzersiz bir özel kimlik değeri yoksa item , sunucu bir kimlik oluşturur. Çağrı döndürüldikten sonra nesnesini inceleyerek oluşturulan kimliği alabilirsiniz.

Uzak sunucudaki verileri güncelleştirme

Aşağıdaki kod, yeni bilgilerle aynı kimlikle var olan bir kaydı güncelleştirmek için yönteminin nasıl kullanılacağını ReplaceItemAsync gösterir.

// In this example, we assume the item has been created from the InsertItemAsync sample

item.IsComplete = true;
await remoteTable.ReplaceItemAsync(todoItem);

Uzak sunucudaki verileri silme

Aşağıdaki kod, mevcut bir örneği silmek için yönteminin DeleteItemAsync nasıl kullanılacağını gösterir.

// In this example, we assume the item has been created from the InsertItemAsync sample

await todoTable.DeleteItemAsync(item);

Çakışma çözümü ve iyimser eşzamanlılık

İki veya daha fazla istemci aynı öğeye aynı anda değişiklik yazabilir. Çakışma algılaması olmadan, son yazma işlemi önceki güncelleştirmelerin üzerine yazar. İyimser eşzamanlılık denetimi , her işlemin işlenebileceğini ve bu nedenle herhangi bir kaynak kilitleme kullanmadığını varsayar. İyimser eşzamanlılık denetimi, verileri işlemeden önce başka hiçbir işlemin verileri değiştirmediğini doğrular. Veriler değiştirildiyse işlem geri alınır.

Azure Mobile Apps, Mobil Uygulama arka ucunuzdaki her tablo için tanımlanan sistem özellik sütununu kullanarak version her öğede yapılan değişiklikleri izleyerek iyimser eşzamanlılık denetimini destekler. Bir kayıt her güncelleştirildiğinde, Mobile Apps bu kaydın version özelliğini yeni bir değere ayarlar. Her güncelleştirme isteği sırasında, version istekle birlikte gelen kaydın özelliği, sunucudaki kaydın aynı özelliğiyle karşılaştırılır. İstekle geçirilen sürüm arka uçla eşleşmiyorsa istemci kitaplığı bir DatasyncConflictException<T> özel durum oluşturur. Özel durumla birlikte gelen tür, kaydın sunucu sürümünü içeren arka uçtan alınan kayıttır. Uygulama daha sonra bu bilgileri kullanarak güncelleştirme isteğini yeniden arka uçtan doğru version değerle yürüterek değişiklikleri işleyip işlemeyeceğine karar verebilir.

İyimser eşzamanlılık, temel nesne kullanılırken DatasyncClientData otomatik olarak etkinleştirilir.

İyimser eşzamanlılığı etkinleştirmeye ek olarak, kodunuzda özel durumu da yakalamanız DatasyncConflictException<T> gerekir. Güncelleştirilmiş kayda doğru version uygulamayı ve ardından çağrıyı çözümlenen kayıtla tekrarlayarak çakışmayı çözün. Aşağıdaki kod, algılandıktan sonra yazma çakışmasının nasıl çözüleceğini gösterir:

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

Çevrimdışı tablolarla çalışma

Çevrimdışı tablolar, çevrimdışıyken kullanılacak verileri depolamak için yerel bir SQLite deposu kullanır. Tüm tablo işlemleri uzak sunucu deposu yerine yerel SQLite deposuna karşı gerçekleştirilir. öğesini her platform projesine ve paylaşılan projelere eklediğinizden Microsoft.Datasync.Client.SQLiteStore emin olun.

Tablo başvurusunun oluşturulabilmesi için önce yerel deponun hazırlanması gerekir:

var store = new OfflineSQLiteStore(Constants.OfflineConnectionString);
store.DefineTable<TodoItem>();

Mağaza tanımlandıktan sonra istemciyi oluşturabilirsiniz:

var options = new DatasyncClientOptions 
{
    OfflineStore = store
};
var client = new DatasyncClient("MOBILE_URL", options);

Son olarak, çevrimdışı özelliklerin başlatıldığından emin olmanız gerekir:

await client.InitializeOfflineStoreAsync();

Mağaza başlatma normalde istemci oluşturulduktan hemen sonra yapılır. Offline Bağlan ionString, hem SQLite veritabanının konumunu hem de veritabanını açmak için kullanılan seçenekleri belirtmek için kullanılan bir URI'dir. Daha fazla bilgi için bkz . SQLite'te URI Dosya Adları.

  • Bellek içi önbellek kullanmak için kullanın file:inmemory.db?mode=memory&cache=private.
  • Dosya kullanmak için file:/path/to/file.db

Dosya için mutlak dosya adını belirtmeniz gerekir. Xamarin kullanıyorsanız, bir yol oluşturmak için Xamarin Essentials Dosya Sistemi Yardımcılarını kullanabilirsiniz: Örneğin:

var dbPath = $"{Filesystem.AppDataDirectory}/todoitems.db";
var store = new OfflineSQLiteStore($"file:/{dbPath}?mode=rwc");

MAUI kullanıyorsanız bir yol oluşturmak için MAUI Dosya Sistemi Yardımcılarını kullanabilirsiniz: Örneğin:

var dbPath = $"{Filesystem.AppDataDirectory}/todoitems.db";
var store = new OfflineSQLiteStore($"file:/{dbPath}?mode=rwc");

Çevrimdışı tablo oluşturma

Aşağıdaki yöntem kullanılarak GetOfflineTable<T> bir tablo başvurusu elde edilebilir:

IOfflineTable<TodoItem> table = client.GetOfflineTable<TodoItem>();

Uzak tabloda olduğu gibi, salt okunur bir çevrimdışı tabloyu da kullanıma açabilirsiniz:

IReadOnlyOfflineTable<TodoItem> table = client.GetOfflineTable<TodoItem>();

Çevrimdışı tablo kullanmak için kimlik doğrulaması yapmanız gerekmez. Yalnızca arka uç hizmetiyle iletişim kurarken kimlik doğrulaması yapmanız gerekir.

Çevrimdışı Tabloyu Eşitleme

Çevrimdışı tablolar varsayılan olarak arka uçla eşitlenmez. Eşitleme iki parçaya ayrılır. Değişiklikleri yeni öğeleri indirmekten ayrı olarak gönderebilirsiniz. Örneğin:

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

Varsayılan olarak, tüm tablolar artımlı eşitleme kullanır; yalnızca yeni kayıtlar alınır. Her benzersiz sorgu için bir kayıt eklenir (OData sorgusunun MD5 karması oluşturularak oluşturulur).

Dekont

için ilk bağımsız değişken PullItemsAsync , cihaza hangi kayıtların çekildiğini gösteren OData sorgusudur. İstemci tarafında karmaşık sorgular oluşturmak yerine hizmeti yalnızca kullanıcıya özgü kayıtları döndürecek şekilde değiştirmek daha iyidir.

Seçeneklerin PullOptions (nesne tarafından tanımlanan) genellikle ayarlanması gerekmez. Seçenekler arasında bulunanlar:

  • PushOtherTables - true olarak ayarlanırsa, tüm tablolar gönderiliyor.
  • QueryId - oluşturulan sorgu yerine kullanılacak belirli bir sorgu kimliği.
  • WriteDeltaTokenInterval - artımlı eşitlemeyi izlemek için kullanılan delta belirtecinin ne sıklıkta yazılabileceğini.

SDK, kayıtları çekmeden önce örtük PushAsync() bir işlem gerçekleştirir.

Çakışma işleme bir PullAsync() yöntemde gerçekleşir. Çakışmaları çevrimiçi tablolarla aynı şekilde işleyebilir. Çakışma ekleme, güncelleştirme veya silme işlemi yerine çağrıldığında PullAsync() oluşturulur. Birden çok çakışma oluşursa, bunlar tek PushFailedExceptionbir içinde paketlenmiş olur. Her hatayı ayrı ayrı işleyebilir.

Tüm tablolar için değişiklikleri gönderme

Tüm değişiklikleri uzak sunucuya göndermek için şunu kullanın:

await client.PushTablesAsync();

Tabloların bir alt kümesine yönelik değişiklikleri göndermek için yöntemine PushTablesAsync() bir IEnumerable<string> sağlayın:

var tablesToPush = new string[] { "TodoItem", "Notes" };
await client.PushTables(tablesToPush);

client.PendingOperations Uzak hizmete gönderilmeyi bekleyen işlem sayısını okumak için özelliğini kullanın. Bu özellik, null hiçbir çevrimdışı depo yapılandırılmadığında kullanılır.

Karmaşık SQLite sorguları çalıştırma

Çevrimdışı veritabanında karmaşık SQL sorguları yapmanız gerekiyorsa yöntemini kullanarak ExecuteQueryAsync() bunu yapabilirsiniz. Örneğin, bir SQL JOIN deyim yapmak için dönüş değerinin yapısını gösteren bir JObject tanımlayın ve kullanın 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.

Tanım bir anahtar/değer kümesidir. Anahtarlar SQL sorgusunun döndürdüğü alan adlarıyla eşleşmeli ve değerler beklenen türdeki varsayılan değer olmalıdır. Sayılar (uzun), false boole değerleri ve string.Empty diğer her şey için kullanın0L.

SQLite'in desteklenen bir dizi kısıtlayıcı türü vardır. Tarih/saat, karşılaştırmalara izin vermek için dönem bu yana milisaniye olarak depolanır.

Kullanıcıların kimliğini doğrulama

Azure Mobile Apps, kimlik doğrulama çağrılarını işlemek için bir kimlik doğrulama sağlayıcısı oluşturmanıza olanak tanır. Hizmet istemcisini oluştururken kimlik doğrulama sağlayıcısını belirtin:

AuthenticationProvider authProvider = GetAuthenticationProvider();
var client = new DatasyncClient("APP_URL", authProvider);

Kimlik doğrulaması gerektiğinde, belirteci almak için kimlik doğrulama sağlayıcısı çağrılır. Genel kimlik doğrulama sağlayıcısı hem Yetkilendirme üst bilgisi tabanlı kimlik doğrulaması hem de App Service Kimlik Doğrulaması ve Yetkilendirme tabanlı kimlik doğrulaması için kullanılabilir. Aşağıdaki modeli kullanın:

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 */"
    };
}

Kimlik doğrulama belirteçleri bellekte önbelleğe alınır (hiçbir zaman cihaza yazılmasın) ve gerektiğinde yenilenir.

Microsoft kimlik platformu kullanma

Microsoft kimlik platformu, Microsoft Entra Id ile kolayca tümleştirmenizi sağlar. Microsoft Entra kimlik doğrulamasını uygulama hakkında eksiksiz bir öğretici için hızlı başlangıç öğreticilerine bakın. Aşağıdaki kod, erişim belirtecini alma örneğini gösterir:

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

Microsoft kimlik platformu ASP.NET 6 ile tümleştirme hakkında daha fazla bilgi için Microsoft kimlik platformu belgelerine bakın.

Xamarin Essentials veya MAUI WebAuthenticator kullanma

Azure Uygulaması Hizmet Kimlik Doğrulaması için Xamarin Essentials WebAuthenticator veya MAUI WebAuthenticator kullanarak belirteç alabilirsiniz:

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 veDisplayName, Azure Uygulaması Hizmet Kimlik Doğrulaması kullanılırken doğrudan kullanılamaz. Bunun yerine, uç noktadan bilgileri /.auth/me almak için gecikmeli bir istek sahibi kullanın:

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

Gelişmiş konular

Yerel veritabanındaki varlıkları temizleme

Normal işlem altında, varlıkları temizleme gerekli değildir. Eşitleme işlemi silinen varlıkları kaldırır ve yerel veritabanı tabloları için gerekli meta verileri korur. Ancak, veritabanı içindeki varlıkları temizlemenin yararlı olduğu zamanlar vardır. Bu tür senaryolardan biri, çok sayıda varlığı silmeniz gerektiği ve verileri tablodan yerel olarak temizlemenin daha verimli olduğu durumlardır.

Bir tablodan kayıtları temizlemek için kullanın table.PurgeItemsAsync():

var query = table.CreateQuery();
var purgeOptions = new PurgeOptions();
await table.PurgeItermsAsync(query, purgeOptions, cancellationToken);

Sorgu, tablodan kaldırılacak varlıkları tanımlar. LINQ kullanarak temizlenecek varlıkları tanımlayın:

var query = table.CreateQuery().Where(m => m.Archived == true);

PurgeOptions sınıfı temizleme işlemini değiştirmek için ayarlar sağlar:

  • DiscardPendingOperations sunucuya gönderilmeyi bekleyen işlem kuyruğundaki tablo için bekleyen işlemleri atar.
  • QueryId işlem için kullanılacak delta belirtecini tanımlamak için kullanılan sorgu kimliğini belirtir.
  • TimestampUpdatePolicy temizleme işleminin sonunda delta belirtecinin nasıl ayarlayabileceğinizi belirtir:
    • TimestampUpdatePolicy.NoUpdate delta belirtecinin güncelleştirilmemesi gerektiğini gösterir.
    • TimestampUpdatePolicy.UpdateToLastEntity , delta belirtecinin updatedAt tabloda depolanan son varlığın alanına güncelleştirilmesi gerektiğini gösterir.
    • TimestampUpdatePolicy.UpdateToNow delta belirtecinin geçerli tarih/saate güncelleştirilmesi gerektiğini gösterir.
    • TimestampUpdatePolicy.UpdateToEpoch delta belirtecinin tüm verileri eşitlemek için sıfırlanması gerektiğini gösterir.

QueryId Verileri eşitlemek için çağırırken table.PullItemsAsync() kullandığınız değeri kullanın. , QueryId temizleme tamamlandığında güncelleştirilecek delta belirtecini belirtir.

İstek üst bilgilerini özelleştirme

Belirli uygulama senaryonuzu desteklemek için Mobil Uygulama arka ucuyla iletişimi özelleştirmeniz gerekebilir. Örneğin, kullanıcıya dönmeden önce her giden isteğe özel bir üst bilgi ekleyebilir veya yanıt durum kodlarını değiştirebilirsiniz. Aşağıdaki örnekte olduğu gibi özel bir DelegatingHandler kullanın:

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

İstek günlüğünü etkinleştirme

İstek günlüğü eklemek için DelegatingHandler da kullanabilirsiniz:

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

Eşitleme olaylarını izleme

Eşitleme olayı gerçekleştiğinde, olay olay temsilcisine client.SynchronizationProgress yayımlanır. Olaylar, eşitleme işleminin ilerleme durumunu izlemek için kullanılabilir. Eşitleme olay işleyicisini aşağıdaki gibi tanımlayın:

client.SynchronizationProgress += (sender, args) => {
    // args is of type SynchronizationEventArgs
};

Türü SynchronizationEventArgs aşağıdaki gibi tanımlanır:

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

içindeki args özellikler ya da null-1 özelliğin eşitleme olayıyla ilgili olmadığı durumlardır.