Aracılığıyla paylaş


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

Not

Bu ürün kullanımdan kaldırıldı. .NET 8 veya üzerini kullanan projelerin yerini alması için bkz. Community Toolkit Datasync kitaplığı.

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:

Not

Bu makale, Microsoft Datasync Framework'ün en son (v6.0) sürümünü kapsar. Eski istemciler içinv4.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 Platformiç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:

  • Microsoft.Datasync.Client
  • Çevrimdışı tablolar kullanılıyorsa Microsoft.Datasync.Client.SQLiteStore .

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

Önceki kodda MOBILE_APP_URL değerini ASP.NET Core arka uçURL'si ile değiştirin. İ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 Authorization üst bilgisini ekleyen) 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ük ve istek üst bilgileri ekleme için temsilci işleyicileri örnekleri.

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

InstallationId

bir 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. InstallationIdkullanıyorsanız, 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.

SerializerSettings

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

TableEndpointResolver

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

Örneğin, aşağıdaki varsayımı değiştirerek tüm tabloların /apialtında bulunmasına neden 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. UserAgent özelliğini geçerli herhangi bir üst bilgi değerine ayarlayabilirsiniz.

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 GetRemoteTable<T>kullanın:

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

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

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

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

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) - trueöğe silinir.

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

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

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

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

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

İstemci, URI olarak /tables/{tablename} yolunu 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:

  • .Where() yan tümcesi ile filtreleme.
  • Çeşitli .OrderBy() yan tümceleriyle sıralama.
  • .Select()ile özellikleri seçme.
  • .Skip() ve .Take()ile sayfalama.

Sorgudaki öğeleri sayma

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

// 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 IAsyncEnumerablearacılığıyla döndürülür:

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

IAsyncEnumerable<T> farklı bir koleksiyona dönüştürmek 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 olan ConcurrentObservableCollection<T> sağlar. Bu sınıf, normalde bir listeyi yönetmek için ObservableCollection<T> kullanan kullanıcı arabirimi uygulamaları bağlamında kullanılabilir (örneğin, Xamarin Forms veya MAUI listeleri). Bir ConcurrentObservableCollection<T> doğrudan bir tablodan veya sorgudan temizleyip yükleyebilirsiniz:

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

.ToObservableCollection(collection) kullanılması, tek tek öğeler yerine koleksiyonun tamamı için CollectionChanged olayını bir kez tetikleyerek yeniden çizme süresinin daha hızlı olmasını sağlar.

ConcurrentObservableCollection<T> ayrıca koşul temelli değişiklikler de vardır:

// 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ümcesi kullanabilirsiniz. Mesela:

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

Filtreleme, IAsyncEnumerable öncesinde hizmette ve IAsyncEnumerable sonrasında istemcide yapılır. Mesela:

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

İlk .Where() yan tümcesi (yalnızca tamamlanmamış öğeleri döndür) hizmette yürütülürken, ikinci .Where() yan tümcesi ("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 .OrderBy(), .OrderByDescending(), .ThenBy()ve .ThenByDescending() kullanın.

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 .Skip() ve .Take() kullanarak 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

GetItemAsync işlevi, 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 includeDeleted parametresini 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 olanKimliği adlı bir üye içermelidir. CruD işlemlerini gerçekleştirmek ve çevrimdışı eşitleme için bu Kimliği gereklidir. Aşağıdaki kod, tabloya yeni satırlar eklemek için InsertItemAsync yönteminin nasıl kullanılacağını 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 benzersiz bir özel kimlik değeri item dahil değilse, 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, aynı kimlikle var olan bir kaydı yeni bilgilerle güncelleştirmek için ReplaceItemAsync yönteminin nasıl kullanılacağını 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, var olan bir örneği silmek için DeleteItemAsync yönteminin 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 version sistem özelliği sütununu kullanarak 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, istekle birlikte gelen kaydın version ö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 durumu 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 arka uçtan doğru version değeriyle yeniden yürüterek değişiklikleri işlemeye karar verebilir.

DatasyncClientData temel nesnesi kullanılırken iyimser eşzamanlılık otomatik olarak etkinleştirilir.

İyimser eşzamanlılığı etkinleştirmeye ek olarak, kodunuzda DatasyncConflictException<T> özel durumunu da yakalamanız gerekir. Güncelleştirilmiş kayda doğru version uygulayarak çakışmayı çözün ve ardından çağrıyı çözümlenen kayıtla yineleyin. 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. Microsoft.Datasync.Client.SQLiteStore her platform projesine ve paylaşılan projelere eklediğinizden 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. OfflineConnectionString, 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. SQLiteURI Dosya Adları.

  • Bellek içi önbellek kullanmak için file:inmemory.db?mode=memory&cache=privatekullanın.
  • 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ı kullanabilirsiniz: Örneğin:

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

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

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

Çevrimdışı tablo oluşturma

Tablo başvurusu GetOfflineTable<T> yöntemi kullanılarak 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. Mesela:

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

Not

PullItemsAsync için ilk bağımsız değişken, 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 nesnesi tarafından tanımlanan) genellikle ayarlanması gerekmez. Seçenekler şunlardır:

  • PushOtherTables - true olarak ayarlanırsa tüm tablolar gönderilir.
  • 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ılır?

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

Çakışma işleme bir PullAsync() yönteminde gerçekleşir. Çakışmaları çevrimiçi tablolarla aynı şekilde işleyebilir. Çakışma, ekleme, güncelleştirme veya silme işlemi yerine PullAsync() çağrıldığında oluşturulur. Birden çok çakışma oluşursa, bunlar tek bir PushFailedExceptionhalinde 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ümesinde değişiklikleri göndermek için PushTablesAsync() yöntemine bir IEnumerable<string> sağlayın:

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

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

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

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

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), boole değerleri için false ve diğer her şey için string.Empty için 0L kullanın.

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 platformunu 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 platformunu 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 App Service 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
    };
}

azure app service kimlik doğrulaması kullanılırken UserId ve DisplayName doğrudan kullanılamaz. Bunun yerine, bilgileri /.auth/me uç noktasından 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 table.PurgeItemsAsync()kullanın:

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 tabloda depolanan son varlık için updatedAt 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.

Verileri eşitlemek için table.PullItemsAsync() çağırırken kullandığınız QueryId değerini 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 DelegatingHandlerkullanı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 client.SynchronizationProgress olay temsilcisine 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
};

SynchronizationEventArgs türü 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; }
}

args içindeki özellikler, özellik eşitleme olayıyla ilgili olmadığında null veya -1.