.NET MAUI Sqlite数据库操作(二)异步初始化方法

一、异步初始化方法

cs 复制代码
private SQLiteAsyncConnection _database;

public async Task Init()
{
    if (_database != null)
        return;

    var database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    await database.CreateTableAsync<TodoItem>();
    await database.CreateTableAsync<AnotherTableItem>();

    _database = database;
}
关键点
  1. 检查现有实例

    • 首先检查 _database 是否已被初始化。如果已经初始化,则直接返回,避免重复初始化。
  2. 创建并初始化数据库

    • 如果 _database 为空,则创建一个新的 SQLiteAsyncConnection 实例,并初始化所需的数据库表。
  3. 异步操作

    • 这是一个异步方法,使用 await 关键字来等待数据库表创建完成。
  4. 实例变量:

    • _database 是一个实例变量,存储数据库连接。
优缺点
  • 优点

    • 简单明了,直接在需要的地方调用 Init 方法即可。
    • 易于理解和实现。
  • 缺点

    • 在某些情况下,仍然有可能因为竞争条件而导致多次初始化(虽然通过检查 _database 可以减少这种情况,但并不能完全避免)。
    • 依赖于外部调用者必须记住在使用数据库之前调用 Init 方法。

二、工厂方法模式

cs 复制代码
public class TodoItemDatabase
{
    private readonly SQLiteAsyncConnection _database;

    private TodoItemDatabase(SQLiteAsyncConnection database)
    {
        _database = database;
    }

    public static async Task<TodoItemDatabase> CreateAsync()
    {
        var database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
        var instance = new TodoItemDatabase(database);
        await instance.InitializeTables();
        return instance;
    }

    private async Task InitializeTables()
    {
        await _database.CreateTableAsync<TodoItem>();
        await _database.CreateTableAsync<AnotherTableItem>();
    }
}
关键点
  1. 私有构造函数

    • 构造函数被设为私有,确保只能通过 CreateAsync 方法来创建实例。
  2. 静态工厂方法

    • 使用一个静态异步方法 CreateAsync 来创建并初始化 TodoItemDatabase 实例。
    • CreateAsync 方法内部处理所有的初始化逻辑,并返回已初始化的实例。
  3. 实例化与初始化分离

    • 将实例化和初始化逻辑封装在一个静态方法中,确保创建的实例已经被正确初始化。
优缺点
  • 优点

    • 保证了实例在返回给调用者之前已经完全初始化,避免了未初始化或部分初始化的情况。
    • 更加面向对象,将创建逻辑封装在类内部,符合单一职责原则。
    • 易于测试,因为初始化逻辑是封装的,可以通过模拟 CreateAsync 方法来控制初始化过程。
  • 缺点

    • 相对来说实现稍微复杂一些。
    • 如果在应用程序生命周期中只需要一次初始化(例如单例模式),可能显得有些过度设计。

总结

  • 异步初始化方法 :适合于简单且明确的初始化需求,如果能确保调用者始终在使用前调用 Init 方法,是一种较为直接的方式。
  • 工厂方法模式:适用于需要确保实例在任何时候都被正确初始化的场景,更加健壮和可扩展,特别是在复杂应用中,有利于维护和测试。

选择哪种方法取决于你具体的应用需求和复杂度。如果你的应用程序较为简单,异步初始化方法已经足够;如果你需要更高的鲁棒性和可维护性,工厂方法模式则是更好的选择。

三、双重检查锁定(Double-Checked Locking)

这种方法适用于多线程环境,确保资源只被初始化一次。

cs 复制代码
private static readonly object _lock = new object();
private SQLiteAsyncConnection _database;

public async Task<SQLiteAsyncConnection> GetDatabaseAsync()
{
    if (_database == null)
    {
        lock (_lock)
        {
            if (_database == null)
            {
                var database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
                await database.CreateTableAsync<TodoItem>();
                await database.CreateTableAsync<AnotherTableItem>();
                _database = database;
            }
        }
    }
    return _database;
}
优缺点
  • 优点

    • 确保在多线程环境下资源只被初始化一次。
    • 性能较高,因为只在第一次初始化时加锁。
  • 缺点

    • 实现起来相对复杂一些。

四、懒加载(Lazy Initialization)

利用 Lazy<T> 类来实现异步懒加载。

cs 复制代码
private Lazy<Task<SQLiteAsyncConnection>> _databaseLazy = new Lazy<Task<SQLiteAsyncConnection>>(async () =>
{
    var database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    await database.CreateTableAsync<TodoItem>();
    await database.CreateTableAsync<AnotherTableItem>();
    return database;
});

public Task<SQLiteAsyncConnection> GetDatabaseAsync()
{
    return _databaseLazy.Value;
}
优缺点
  • 优点

    • 简化了初始化逻辑。
    • 懒加载仅在首次访问时初始化。
  • 缺点

    • 异常处理较为复杂,需要特别注意捕获和处理异步初始化过程中的异常。

五、静态构造函数(Static Constructor)

可以使用静态构造函数进行异步初始化(虽然静态构造函数本身不能是异步的,但可以在静态构造函数中启动异步初始化)。

cs 复制代码
private static SQLiteAsyncConnection _database;
private static Task _initializationTask;

static MyClass()
{
    _initializationTask = InitializeDatabaseAsync();
}

private static async Task InitializeDatabaseAsync()
{
    _database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    await _database.CreateTableAsync<TodoItem>();
    await _database.CreateTableAsync<AnotherTableItem>();
}

public static async Task<SQLiteAsyncConnection> GetDatabaseAsync()
{
    await _initializationTask;
    return _database;
}
优缺点
  • 优点

    • 在类加载时就开始初始化,确保数据库连接在第一次使用时已经准备好。
  • 缺点

    • 如果初始化过程很长,可能会影响类的首次使用性能。

六、线程安全的异步单例(Thread-Safe Async Singleton)

如果需要确保只有一个实例并且异步初始化,可以使用异步单例模式

cs 复制代码
private static readonly Lazy<Task<TodoItemDatabase>> _instance = new Lazy<Task<TodoItemDatabase>>(async () =>
{
    var database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    var instance = new TodoItemDatabase(database);
    await instance.InitializeTables();
    return instance;
});

public static Task<TodoItemDatabase> Instance => _instance.Value;

private async Task InitializeTables()
{
    await _database.CreateTableAsync<TodoItem>();
    await _database.CreateTableAsync<AnotherTableItem>();
}
优缺点
  • 优点

    • 确保全局只存在一个实例,并且该实例是异步初始化的。
  • 缺点

    • 实现起来稍微复杂,需要理解异步单例模式。

七、异步方法总结

每种方法都有其适用场景和优缺点。你可以根据以下因素选择合适的方法:

  • 简单性:如果你的应用程序较为简单,直接使用异步初始化方法或工厂方法模式即可。
  • 多线程环境:如果你的应用程序是多线程的,双重检查锁定或异步单例模式是更好的选择。
  • 延迟初始化:如果希望延迟到第一次使用时才初始化,可以考虑使用懒加载。

八、.NET MAUI Sqlite数据库操作(一)采用工厂模式补充完善

Constants.cs

cs 复制代码
using SQLite;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace TodoSQLite
{
    public static class Constants
    {
        public const string DatabaseFilename = "TodoSQLite.db3"; // 数据库文件名

        public const SQLite.SQLiteOpenFlags Flags =
            // 以读写模式打开数据库。
            SQLite.SQLiteOpenFlags.ReadWrite |
            // 如果数据库文件不存在,则创建它。
            SQLite.SQLiteOpenFlags.Create |
            // 启用多线程数据库访问,以便多个线程可以共享数据库连接。
            SQLite.SQLiteOpenFlags.SharedCache;

        public static string DatabasePath => Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename);
    }

TodoItemDatabase.cs

cs 复制代码
public class TodoItemDatabase
    {
        private readonly SQLiteAsyncConnection _database;

        private TodoItemDatabase(SQLiteAsyncConnection database)
        {
            _database = database;
        }

        public static async Task<TodoItemDatabase> CreateAsync()
        {
            var database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
            var instance = new TodoItemDatabase(database);
            await instance.InitializeTables();
            return instance;
        }

        private async Task InitializeTables()
        {
            await _database.CreateTableAsync<TodoItem>();
            await _database.CreateTableAsync<AnotherTableItem>();
        }

        // 获取所有TodoItem项
        public Task<List<TodoItem>> GetItemsAsync()
        {
            return _database.Table<TodoItem>().ToListAsync();
        }

        // 获取未完成的TodoItem项
        public Task<List<TodoItem>> GetItemsNotDoneAsync()
        {
            return _database.Table<TodoItem>().Where(t => !t.Done).ToListAsync();
        }

        // 根据ID获取单个TodoItem项
        public Task<TodoItem> GetItemAsync(int id)
        {
            return _database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
        }

        // 保存或更新TodoItem项
        public Task<int> SaveItemAsync(TodoItem item)
        {
            if (item.ID != 0)
            {
                return _database.UpdateAsync(item);
            }
            else
            {
                return _database.InsertAsync(item);
            }
        }

        // 删除TodoItem项
        public Task<int> DeleteItemAsync(TodoItem item)
        {
            return _database.DeleteAsync(item);
        }

        // 获取所有AnotherTableItem项
        public Task<List<AnotherTableItem>> GetAnotherTableItemsAsync()
        {
            return _database.Table<AnotherTableItem>().ToListAsync();
        }

        // 保存或更新AnotherTableItem项
        public Task<int> SaveAnotherTableItemAsync(AnotherTableItem item)
        {
            if (item.ID != 0)
            {
                return _database.UpdateAsync(item);
            }
            else
            {
                return _database.InsertAsync(item);
            }
        }

        // 删除AnotherTableItem项
        public Task<int> DeleteAnotherTableItemAsync(AnotherTableItem item)
        {
            return _database.DeleteAsync(item);
        }
    }
}

完整思路都有了,仔细阅读,必能成功!

关联阅读
​​​​​​​.NET MAUI Sqlite数据库操作(一)

.NET MAUI Sqlite数据库操作(二)异步初始化方法​​​​​​​

相关推荐
Azhao110611 小时前
商城产品详情页的客服咨询在哪里设置详解:从入门到实战全攻略
sqlite
ggabb21 小时前
战斗机器人的发展与战争伦理影响
sqlite
鹏子训1 天前
AI记忆新思路:用SQLite替代向量数据库,去EMBEDDINGS化,谷歌开源Google Always On Memory Agent
数据库·人工智能·sqlite·embedding
Muyuan19982 天前
25.Paper RAG Agent 优化记录:上传反馈、计算器安全与 Chunk 参数调整
python·安全·django·sqlite·fastapi
code_pgf7 天前
sqlite数据库cmakelist.txt编译
数据库·sqlite
_F_y7 天前
SQLite3的基础使用
jvm·数据库·sqlite
IntMainJhy8 天前
【flutter for open harmony】第三方库 Flutter 二维码生成的鸿蒙化适配与实战指南
数据库·flutter·华为·sqlite·harmonyos
IntMainJhy8 天前
【flutter for open harmony】第三方库Flutter 国际化多语言的鸿蒙化适配与实战指南
数据库·flutter·华为·sqlite·harmonyos
IntMainJhy8 天前
【flutter for open harmony】Flutter SQLite 本地数据库的鸿蒙化适配与实战指南
数据库·flutter·sqlite
北冥有羽Victoria9 天前
Django Auth组件完整版教程:从原理到项目落地
大数据·服务器·数据库·后端·python·django·sqlite