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

一、异步初始化方法

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 方法。

 二、工厂方法模式

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)

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

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> 类来实现异步懒加载。

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)

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

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)

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

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

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

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数据库操作(二)异步初始化方法 ​​​​​​​

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值