قواعد البيانات المحلية في .NET MAUI: دليل عملي باستخدام SQLite و EF Core

دليل عملي لبناء قواعد بيانات محلية في تطبيقات .NET MAUI باستخدام sqlite-net-pcl و Entity Framework Core 10. يغطي عمليات CRUD، نمط Repository، المزامنة بدون اتصال، ونصائح أداء متقدمة مع أمثلة كود كاملة.

لو سألت أي مطوّر موبايل عن أول تحدٍّ حقيقي واجهه بعد ما خلّص بناء الواجهة، أغلب الظن رح يقولك: "طيب، وين أحفظ البيانات؟". وصراحة، هذا سؤال منطقي جداً. سواء كنت تبني تطبيق قائمة مهام بسيط أو نظام إدارة مخزون كامل، التخزين المحلي هو العمود الفقري لأي تطبيق يحترم تجربة المستخدم — خاصة لما يكون الإنترنت مو مضمون.

في هذا الدليل، رح نغطي كل اللي تحتاجه لبناء طبقة بيانات محلية صلبة في تطبيقات .NET MAUI. رح نشتغل بنهجين مختلفين: مكتبة sqlite-net-pcl الخفيفة والمباشرة، وEntity Framework Core مع مزوّد SQLite للتطبيقات الأكثر تعقيداً. وطبعاً، رح نستخدم أحدث الإصدارات المتوافقة مع .NET 10 ونتبع أفضل الممارسات المعتمدة في 2026.

لماذا SQLite تحديداً لتطبيقات الموبايل؟

SQLite مو مجرد قاعدة بيانات عادية — هي فعلياً محرك قواعد البيانات الأكثر انتشاراً في العالم. ولسبب وجيه.

إليك ليش تعتبر الخيار المثالي لتطبيقات .NET MAUI:

  • لا تحتاج خادم: تشتغل كملف واحد داخل التطبيق بدون أي عمليات خلفية
  • متعددة المنصات: تشتغل على Android و iOS و Windows و macOS بنفس الكود
  • خفيفة جداً: حجم المكتبة أقل من 1 ميجابايت (نعم، أقل من صورة واحدة!)
  • موثوقة: تستخدمها مليارات الأجهزة يومياً — من متصفح Chrome إلى نظام iOS نفسه
  • أداء عالي: عمليات القراءة والكتابة المحلية أسرع بأضعاف من الاتصال بخادم بعيد

وفي سياق .NET MAUI، عندك خيارين رئيسيين للتعامل مع SQLite:

  • sqlite-net-pcl: مكتبة خفيفة وبسيطة تعطيك ORM مباشر مع واجهة سهلة — مثالية للمشاريع الصغيرة والمتوسطة
  • Entity Framework Core + SQLite: الحل الكامل مع دعم LINQ المتقدم والترحيل (Migrations) وإدارة العلاقات — الأنسب للتطبيقات المعقدة

النهج الأول: sqlite-net-pcl — البساطة والسرعة

تثبيت الحزم المطلوبة

لبدء استخدام sqlite-net-pcl في مشروع .NET MAUI، ثبّت الحزم التالية:

dotnet add package sqlite-net-pcl --version 1.9.172
dotnet add package SQLitePCLRaw.bundle_green

ملاحظة مهمة: رغم اسم الحزمة اللي فيه "pcl"، هي فعلياً مكتبة .NET Standard 2.0 ومتوافقة تماماً مع .NET MAUI و.NET 10. لا تخلي الاسم يخدعك.

إعداد ثوابت قاعدة البيانات

أول شي، حدد مسار قاعدة البيانات وإعدادات الاتصال في ملف ثوابت مركزي:

public static class DatabaseConstants
{
    public const string DatabaseFilename = "app_data.db3";

    public const SQLite.SQLiteOpenFlags Flags =
        SQLite.SQLiteOpenFlags.ReadWrite |
        SQLite.SQLiteOpenFlags.Create |
        SQLite.SQLiteOpenFlags.SharedCache;

    public static string DatabasePath =>
        Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.LocalApplicationData),
            DatabaseFilename);
}

لاحظ استخدام SharedCache مع ReadWrite وCreate. هذا المزيج يضمن إن قاعدة البيانات تُنشأ تلقائياً عند أول تشغيل، وتدعم الوصول المتزامن من عدة أجزاء في التطبيق.

إنشاء نموذج البيانات (Model)

هنا نصمم نماذج البيانات باستخدام السمات (Attributes) اللي توفرها المكتبة. الموضوع بسيط ومباشر:

using SQLite;

public class TodoItem
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }

    [MaxLength(200), NotNull]
    public string Title { get; set; } = string.Empty;

    public string? Description { get; set; }

    public bool IsCompleted { get; set; }

    [Indexed]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

    public DateTime? CompletedAt { get; set; }
}

بناء طبقة الوصول للبيانات (Repository)

الآن نجي للجزء المهم — كلاس يتعامل مع قاعدة البيانات بشكل غير متزامن. وهذا مو اختياري، هذا ضروري لتجنب تجميد واجهة المستخدم أثناء العمليات:

using SQLite;

public class TodoRepository
{
    private SQLiteAsyncConnection? _database;

    private async Task GetConnectionAsync()
    {
        if (_database is not null)
            return _database;

        _database = new SQLiteAsyncConnection(
            DatabaseConstants.DatabasePath,
            DatabaseConstants.Flags);

        await _database.CreateTableAsync();

        // تفعيل Write-Ahead Logging لأداء أفضل
        await _database.EnableWriteAheadLoggingAsync();

        return _database;
    }

    public async Task> GetAllAsync()
    {
        var db = await GetConnectionAsync();
        return await db.Table()
            .OrderByDescending(t => t.CreatedAt)
            .ToListAsync();
    }

    public async Task> GetPendingAsync()
    {
        var db = await GetConnectionAsync();
        return await db.Table()
            .Where(t => !t.IsCompleted)
            .OrderBy(t => t.CreatedAt)
            .ToListAsync();
    }

    public async Task GetByIdAsync(int id)
    {
        var db = await GetConnectionAsync();
        return await db.Table()
            .FirstOrDefaultAsync(t => t.Id == id);
    }

    public async Task SaveAsync(TodoItem item)
    {
        var db = await GetConnectionAsync();
        if (item.Id != 0)
            return await db.UpdateAsync(item);
        else
            return await db.InsertAsync(item);
    }

    public async Task DeleteAsync(TodoItem item)
    {
        var db = await GetConnectionAsync();
        return await db.DeleteAsync(item);
    }
}

شخصياً، أحب هذا النمط لأنه يوفّر Lazy Initialization — يعني قاعدة البيانات ما تنشأ إلا لما فعلاً تحتاجها أول مرة.

التسجيل في حقن التبعيات (Dependency Injection)

سجّل المستودع كخدمة مفردة (Singleton) في MauiProgram.cs. والسبب إنك تبي اتصال واحد فقط بقاعدة البيانات طول عمر التطبيق:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // تسجيل المستودع كـ Singleton
        builder.Services.AddSingleton();

        // تسجيل ViewModels والصفحات
        builder.Services.AddTransient();
        builder.Services.AddTransient();

        return builder.Build();
    }
}

ربط البيانات مع ViewModel

استخدم نمط MVVM مع CommunityToolkit.Mvvm لربط البيانات بالواجهة. لو ما تعرف هذه المكتبة، هي توفّر عليك كتابة كود INotifyPropertyChanged يدوياً (واللي هو — بصراحة — كود ممل ومتكرر):

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;

public partial class TodoListViewModel : ObservableObject
{
    private readonly TodoRepository _repository;

    public TodoListViewModel(TodoRepository repository)
    {
        _repository = repository;
    }

    [ObservableProperty]
    private ObservableCollection _items = [];

    [ObservableProperty]
    private string _newTitle = string.Empty;

    [ObservableProperty]
    private bool _isLoading;

    [RelayCommand]
    private async Task LoadItemsAsync()
    {
        IsLoading = true;
        var items = await _repository.GetAllAsync();
        Items = new ObservableCollection(items);
        IsLoading = false;
    }

    [RelayCommand]
    private async Task AddItemAsync()
    {
        if (string.IsNullOrWhiteSpace(NewTitle))
            return;

        var item = new TodoItem { Title = NewTitle.Trim() };
        await _repository.SaveAsync(item);
        NewTitle = string.Empty;
        await LoadItemsAsync();
    }

    [RelayCommand]
    private async Task ToggleCompletedAsync(TodoItem item)
    {
        item.IsCompleted = !item.IsCompleted;
        item.CompletedAt = item.IsCompleted ? DateTime.UtcNow : null;
        await _repository.SaveAsync(item);
    }

    [RelayCommand]
    private async Task DeleteItemAsync(TodoItem item)
    {
        await _repository.DeleteAsync(item);
        Items.Remove(item);
    }
}

النهج الثاني: Entity Framework Core — القوة والمرونة

طيب، sqlite-net-pcl ممتازة للمشاريع البسيطة. لكن وش لو تطبيقك فيه علاقات بين الجداول، واستعلامات LINQ معقدة، وتحتاج ترحيل مخططات؟ هنا يجي دور Entity Framework Core.

مع الإصدار 10.0.3 المتوفر حالياً في 2026، أصبح EF Core أنضج وأسرع من أي وقت مضى.

تثبيت الحزم

dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 10.0.3
dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.3

تصميم النماذج مع العلاقات

خلنا نبني مثال أكثر واقعية من مجرد Todo — تطبيق لإدارة المشاريع والمهام مع علاقة واحد إلى كثير:

public class Project
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string? Description { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public bool IsArchived { get; set; }

    // علاقة واحد إلى كثير
    public ICollection Tasks { get; set; } = [];
}

public class TaskItem
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string? Notes { get; set; }
    public TaskPriority Priority { get; set; } = TaskPriority.Medium;
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime? DueDate { get; set; }

    // المفتاح الخارجي
    public int ProjectId { get; set; }
    public Project Project { get; set; } = null!;
}

public enum TaskPriority
{
    Low,
    Medium,
    High,
    Critical
}

لاحظ إننا سمّينا الكلاس TaskItem بدل Task عشان ما يتعارض مع System.Threading.Tasks.Task. نقطة بسيطة لكن توفّر عليك صداع كبير لاحقاً.

إنشاء DbContext

كلاس DbContext هو البوابة الرئيسية للتعامل مع قاعدة البيانات في EF Core:

using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public DbSet Projects => Set();
    public DbSet Tasks => Set();

    public AppDbContext(DbContextOptions options)
        : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // إعداد جدول المشاريع
        modelBuilder.Entity(entity =>
        {
            entity.HasKey(p => p.Id);
            entity.Property(p => p.Name)
                .IsRequired()
                .HasMaxLength(100);
            entity.HasIndex(p => p.IsArchived);
        });

        // إعداد جدول المهام مع العلاقة
        modelBuilder.Entity(entity =>
        {
            entity.HasKey(t => t.Id);
            entity.Property(t => t.Title)
                .IsRequired()
                .HasMaxLength(200);
            entity.HasIndex(t => t.Priority);
            entity.HasIndex(t => t.IsCompleted);

            entity.HasOne(t => t.Project)
                .WithMany(p => p.Tasks)
                .HasForeignKey(t => t.ProjectId)
                .OnDelete(DeleteBehavior.Cascade);
        });
    }
}

التسجيل في MauiProgram.cs

استخدم AddDbContext لتسجيل السياق مع مسار قاعدة البيانات:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        var dbPath = Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.LocalApplicationData),
            "projects.db3");

        builder.Services.AddDbContext(options =>
            options.UseSqlite($"Data Source={dbPath}"));

        return builder.Build();
    }
}

التأكد من إنشاء قاعدة البيانات

في App.xaml.cs، تأكد إن قاعدة البيانات تنشأ عند بدء التطبيق:

public partial class App : Application
{
    public App(AppDbContext dbContext)
    {
        InitializeComponent();
        // إنشاء قاعدة البيانات والجداول إذا لم تكن موجودة
        dbContext.Database.EnsureCreated();
    }
}

ملاحظة: EnsureCreated() حل سريع وعملي للتطوير والتطبيقات البسيطة. لكن لو تطبيقك في بيئة إنتاجية وتحتاج تعدّل المخطط بين الإصدارات، الأفضل تستخدم Migrations.

استعلامات LINQ المتقدمة

هنا وين تبان قوة EF Core الحقيقية. الاستعلامات اللي ممكن تاخذ منك سطور طويلة من SQL، تكتبها بـ LINQ بشكل أنظف وأقرأ:

public class ProjectService
{
    private readonly AppDbContext _context;

    public ProjectService(AppDbContext context)
    {
        _context = context;
    }

    // جلب المشاريع النشطة مع عدد المهام
    public async Task> GetActiveProjectsAsync()
    {
        return await _context.Projects
            .Where(p => !p.IsArchived)
            .Select(p => new ProjectSummary
            {
                Id = p.Id,
                Name = p.Name,
                TotalTasks = p.Tasks.Count,
                CompletedTasks = p.Tasks.Count(t => t.IsCompleted),
                HasOverdueTasks = p.Tasks.Any(t =>
                    !t.IsCompleted &&
                    t.DueDate.HasValue &&
                    t.DueDate < DateTime.UtcNow)
            })
            .OrderByDescending(p => p.TotalTasks - p.CompletedTasks)
            .ToListAsync();
    }

    // جلب المهام العاجلة عبر جميع المشاريع
    public async Task> GetCriticalTasksAsync()
    {
        return await _context.Tasks
            .Include(t => t.Project)
            .Where(t => !t.IsCompleted &&
                         t.Priority == TaskPriority.Critical)
            .OrderBy(t => t.DueDate)
            .ToListAsync();
    }
}

public record ProjectSummary
{
    public int Id { get; init; }
    public string Name { get; init; } = string.Empty;
    public int TotalTasks { get; init; }
    public int CompletedTasks { get; init; }
    public bool HasOverdueTasks { get; init; }
}

مقارنة بين النهجين: متى تستخدم كل واحد؟

صراحة، هذا السؤال اللي يسأله كل مطوّر. والجواب يعتمد على طبيعة مشروعك:

المعيارsqlite-net-pclEF Core + SQLite
سهولة الإعدادسهل جداً — حزمة واحدة وكلاس واحديحتاج إعداد DbContext وتسجيل الخدمات
العلاقات بين الجداولمحدود — يدعم العلاقات الأساسية فقطكامل — واحد لكثير، كثير لكثير
استعلامات LINQأساسية — Where و OrderByمتقدمة — Join و GroupBy و Projection
ترحيل المخططاتغير مدعوم — CreateTable فقطمدعوم بالكامل عبر Migrations
الحجم والتبعياتخفيف جداًأثقل — يضيف تبعيات متعددة
توافق AOTمتوافقيحتاج اهتمام خاص بالإعدادات
الأنسب لـتطبيقات بسيطة ومتوسطةتطبيقات معقدة ومؤسسية

نصيحتي الشخصية: ابدأ دايماً بـ sqlite-net-pcl إلا إذا كنت متأكد إنك تحتاج العلاقات المعقدة والـ Migrations من البداية. الانتقال من sqlite-net-pcl إلى EF Core لاحقاً أسهل مما تتوقع.

أفضل الممارسات للتخزين المحلي في .NET MAUI

1. فعّل Write-Ahead Logging دائماً

وضع WAL يحسّن الأداء بشكل ملحوظ لأنه يسمح بعمليات قراءة وكتابة متزامنة. بدل ما يقفل قاعدة البيانات بالكامل عند الكتابة، يكتب التغييرات في ملف منفصل ويدمجها لاحقاً:

// مع sqlite-net-pcl
await connection.EnableWriteAheadLoggingAsync();

// مع EF Core — يُفعّل تلقائياً في معظم الحالات
// أو يدوياً:
optionsBuilder.UseSqlite($"Data Source={dbPath};Cache=Shared");

2. استخدم العمليات غير المتزامنة دائماً

هذي القاعدة غير قابلة للنقاش. أي عملية قاعدة بيانات على الخيط الرئيسي رح تتسبب في تجمّد الواجهة، والمستخدم رح يحس إن التطبيق معلّق:

// ❌ خطأ — يجمّد الواجهة
var items = db.Table().ToList();

// ✅ صحيح — غير متزامن
var items = await db.Table().ToListAsync();

3. أغلق الاتصال عند عدم الحاجة

مع EF Core، استخدم نطاق (Scope) محدد لكل عملية. أما مع sqlite-net-pcl، استخدم نمط Singleton للاتصال — فتح وإغلاق الاتصال بشكل متكرر يأثر على الأداء أكثر مما تتخيل.

4. أضف فهارس (Indexes) للأعمدة المستعلمة كثيراً

الفهارس فرق كبير في الأداء، خاصة لما تكبر قاعدة البيانات:

// مع sqlite-net-pcl
[Indexed]
public DateTime CreatedAt { get; set; }

// مع EF Core
entity.HasIndex(t => t.CreatedAt);
entity.HasIndex(t => new { t.ProjectId, t.IsCompleted }); // فهرس مركب

5. تعامل مع إصدارات قاعدة البيانات

لما تحدّث التطبيق، ممكن تحتاج تعدّل مخطط قاعدة البيانات. هذا نمط بسيط وفعّال للتعامل مع الموضوع:

public async Task MigrateDatabaseAsync(SQLiteAsyncConnection db)
{
    var currentVersion = await db.ExecuteScalarAsync(
        "PRAGMA user_version");

    if (currentVersion < 1)
    {
        await db.CreateTableAsync();
        await db.ExecuteAsync("PRAGMA user_version = 1");
    }

    if (currentVersion < 2)
    {
        // إضافة عمود جديد في الإصدار 2
        await db.ExecuteAsync(
            "ALTER TABLE TodoItem ADD COLUMN Priority INTEGER DEFAULT 0");
        await db.ExecuteAsync("PRAGMA user_version = 2");
    }
}

هذا النمط استخدمته في أكثر من مشروع وما خذلني. الفكرة بسيطة: كل إصدار من التطبيق يعرف وش يحتاج يعدّل، والـ PRAGMA user_version يتتبع وين وصلت.

دعم العمل بدون اتصال (Offline-First)

من أهم مزايا التخزين المحلي هي إن تطبيقك يشتغل حتى بدون إنترنت. وهذا الشي اللي المستخدمين يقدّرونه فعلاً.

إليك نمط عملي لمزامنة البيانات المحلية مع خادم بعيد:

public class SyncService
{
    private readonly TodoRepository _localRepo;
    private readonly IApiService _apiService;
    private readonly IConnectivity _connectivity;

    public SyncService(
        TodoRepository localRepo,
        IApiService apiService,
        IConnectivity connectivity)
    {
        _localRepo = localRepo;
        _apiService = apiService;
        _connectivity = connectivity;
    }

    public async Task SyncAsync()
    {
        if (_connectivity.NetworkAccess != NetworkAccess.Internet)
            return;

        // رفع التغييرات المحلية
        var pendingChanges = await _localRepo.GetPendingSyncAsync();
        foreach (var item in pendingChanges)
        {
            await _apiService.UpsertAsync(item);
            item.IsSynced = true;
            await _localRepo.SaveAsync(item);
        }

        // جلب التحديثات من الخادم
        var lastSync = Preferences.Get("last_sync",
            DateTime.MinValue);
        var remoteChanges = await _apiService
            .GetChangesSinceAsync(lastSync);

        foreach (var item in remoteChanges)
        {
            await _localRepo.SaveAsync(item);
        }

        Preferences.Set("last_sync", DateTime.UtcNow);
    }
}

الفكرة واضحة: التطبيق يشتغل محلياً بالكامل، ويزامن البيانات بس لما يكون فيه إنترنت. المستخدم ما يحس بأي فرق.

التعامل مع البيانات الحساسة

نقطة مهمة كثير ناس يتجاهلونها: لا تخزّن كلمات المرور أو التوكنات في SQLite مباشرة. استخدم بدالها التخزين الآمن المدمج في .NET MAUI:

// تخزين رمز مميز بشكل آمن
await SecureStorage.Default.SetAsync("auth_token", token);

// استرجاع الرمز
var token = await SecureStorage.Default.GetAsync("auth_token");

// للبيانات البسيطة مثل إعدادات المستخدم
Preferences.Set("theme", "dark");
var theme = Preferences.Get("theme", "light");

القاعدة العامة بسيطة: SQLite للبيانات المهيكلة، SecureStorage للبيانات الحساسة، وPreferences للإعدادات البسيطة. لا تعقّد الأمور أكثر من كذا.

نصائح الأداء المتقدمة

معاملات مجمّعة (Batch Transactions)

لو تحتاج تدرج أو تحدّث عدد كبير من السجلات، المعاملات المجمّعة تفرق بشكل كبير. نتكلم عن تحسين 10 أضعاف أو أكثر:

// مع sqlite-net-pcl — أسرع بـ 10x أو أكثر
await db.RunInTransactionAsync(tran =>
{
    foreach (var item in items)
    {
        tran.Insert(item);
    }
});

// مع EF Core
await using var transaction = await context.Database
    .BeginTransactionAsync();
context.Tasks.AddRange(taskItems);
await context.SaveChangesAsync();
await transaction.CommitAsync();

التحميل الكسول مقابل الفوري

مع EF Core، عندك تحكم كامل في كيفية تحميل البيانات المرتبطة. اختر بحكمة — التحميل الفوري لما تعرف إنك تحتاج البيانات، والانتقائي لما تبي توفّر الموارد:

// التحميل الفوري — جلب المشروع مع مهامه دفعة واحدة
var project = await context.Projects
    .Include(p => p.Tasks)
    .FirstOrDefaultAsync(p => p.Id == projectId);

// التحميل الانتقائي — جلب ما تحتاجه فقط
var projectName = await context.Projects
    .Where(p => p.Id == projectId)
    .Select(p => p.Name)
    .FirstOrDefaultAsync();

الأسئلة الشائعة

هل يمكن استخدام SQLite و Entity Framework Core معاً في نفس التطبيق؟

تقنياً نعم، لكن عملياً ما أنصح بهذا. يزيد التعقيد بدون فائدة حقيقية. اختر نهج واحد والتزم فيه. لو بدأت بـ sqlite-net-pcl واكتشفت إنك تحتاج ميزات EF Core، الترحيل التدريجي ممكن وأسهل مما تتوقع.

كيف أتعامل مع ترقية مخطط قاعدة البيانات عند تحديث التطبيق؟

مع sqlite-net-pcl، استخدم نمط PRAGMA user_version اللي شرحناه فوق لتتبع إصدار المخطط وتنفيذ التحديثات التدريجية. مع EF Core، ممكن تستخدم EnsureCreated() للتطبيقات البسيطة، أو نظام Migrations الكامل للتطبيقات المعقدة عن طريق إنشاء سكريبتات SQL وتطبيقها عند بدء التشغيل.

ما الحد الأقصى لحجم قاعدة بيانات SQLite في تطبيقات الموبايل؟

تقنياً، SQLite تدعم قواعد بيانات لحد 281 تيرابايت. لكن واقعياً في تطبيقات الموبايل؟ حاول تخلي الحجم أقل من 50 ميجابايت. هذا يحافظ على أداء جيد ويتجنب مشاكل التخزين على أجهزة المستخدمين.

هل SQLite آمنة لتخزين بيانات المستخدمين؟

SQLite بحد ذاتها ما تشفّر البيانات. لو تحتاج تشفير كامل لقاعدة البيانات المحلية، استخدم مكتبة SQLCipher اللي توفر تشفير AES-256. أما للبيانات الحساسة الفردية (كلمات مرور، توكنات)، فـ SecureStorage المدمج في .NET MAUI هو الخيار الصحيح.

هل يعمل EF Core مع التجميع المسبق (NativeAOT) في .NET MAUI؟

مع .NET 10 و EF Core 10، تحسّن دعم AOT بشكل كبير لكنه لا يزال يحتاج بعض الانتباه. نصيحتي: اختبر تطبيقك في وضع AOT من بدري، وتجنب استخدام الانعكاس (Reflection) في إعدادات DbContext. وارجع لوثائق مايكروسوفت الرسمية دايماً لأحدث المعلومات عن قيود التوافق.

عن الكاتب Editorial Team

Our team of expert writers and editors.