一、异步初始化方法
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;
}
关键点
-
检查现有实例:
- 首先检查
_database
是否已被初始化。如果已经初始化,则直接返回,避免重复初始化。
- 首先检查
-
创建并初始化数据库:
- 如果
_database
为空,则创建一个新的SQLiteAsyncConnection
实例,并初始化所需的数据库表。
- 如果
-
异步操作:
- 这是一个异步方法,使用
await
关键字来等待数据库表创建完成。
- 这是一个异步方法,使用
-
实例变量:
_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>();}
}
关键点
-
私有构造函数:
- 构造函数被设为私有,确保只能通过
CreateAsync
方法来创建实例。
- 构造函数被设为私有,确保只能通过
-
静态工厂方法:
- 使用一个静态异步方法
CreateAsync
来创建并初始化TodoItemDatabase
实例。 CreateAsync
方法内部处理所有的初始化逻辑,并返回已初始化的实例。
- 使用一个静态异步方法
-
实例化与初始化分离:
- 将实例化和初始化逻辑封装在一个静态方法中,确保创建的实例已经被正确初始化。
优缺点
-
优点:
- 保证了实例在返回给调用者之前已经完全初始化,避免了未初始化或部分初始化的情况。
- 更加面向对象,将创建逻辑封装在类内部,符合单一职责原则。
- 易于测试,因为初始化逻辑是封装的,可以通过模拟
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数据库操作(二)异步初始化方法