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

相关推荐
2301_786964366 小时前
Django文档简化版——Django快速入门——创建一个基本的投票应用程序(3)
数据库·python·django·sqlite·html
真果粒wrdms6 小时前
【SQLite3】常用API
linux·服务器·c语言·jvm·数据库·oracle·sqlite
吹吹晚风-20 小时前
深入Django(六)
数据库·django·sqlite
吾名招财2 天前
五、保存数据到Excel、sqlite(爬虫及数据可视化)
爬虫·sqlite·excel
小哥山水之间2 天前
在Linux环境下使用sqlite3时,如果尝试对一个空表进行操作(例如插入数据),可能会遇到表被锁定的问题。
数据库·oracle·sqlite
吹吹晚风-2 天前
深入Django(二)
数据库·django·sqlite
吹吹晚风-2 天前
深入Django系列
python·django·sqlite
真果粒wrdms3 天前
【sqlite3】联系人管理系统
linux·c语言·数据库·经验分享·笔记·sqlite
不称职的程序员❦3 天前
Sqlite Browser 下载与安装教程(可视化创建,管理,以及查看 sqlite 数据库文件)
数据库·sqlite·db4s·db browser
q567315234 天前
Pylons 和 Flex 3
开发语言·数据库·python·django·sqlite