.NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)

WebAppDbTest 项目准备

  • 项目准备
    • 1、.net cli 创建项目
    • 2、nuget 包引用和项目结构
      • 2.1、项目添加相关 nuget 包
      • 2.2、WebAppDbTest 项目结构
    • 3、项目代码说明
      • 3.1、CSharp/C# 类文件说明
      • 3.2、json 配置文件说明
    • 4、项目运行预览
  • 数据库 .db 文件准备
    • 1、创建 SQLite 数据库
      • 1.1、在 Windows 上安装 SQLite
      • 1.2、创建 SQLite 数据库
    • 2、创建 LiteDB 数据库
      • 2.1、LiteDB.Shell
      • 2.2、创建 LiteDB 数据库

项目准备

此处还是以默认的 WeatherForecast(天气预报) 的数据为例,分别对两种类型的数据库做相应的 crud 操作,并对比测试性能。

1、.net cli 创建项目

这里我们使用的 .net8 版本,.net cli 创建 WebAppDbTest 项目,执行命令如下:

dotnet new webapi -o WebAppDbTest --no-https -f net8.0

2、nuget 包引用和项目结构

2.1、项目添加相关 nuget 包

  <ItemGroup><PackageReference Include="FreeSql" Version="3.2.805" /><PackageReference Include="FreeSql.Provider.Sqlite" Version="3.2.805" /><PackageReference Include="FreeSql.Repository" Version="3.2.805" /><PackageReference Include="LiteDB.Async" Version="0.1.7" /><PackageReference Include="Mapster.Async" Version="2.0.1" /><PackageReference Include="Serilog.AspNetCore" Version="8.0.0" /><PackageReference Include="Serilog.Sinks.LiteDB" Version="1.0.29" /><PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /></ItemGroup>

2.2、WebAppDbTest 项目结构

左边部分为 Nuget 安装的依赖包,右边部分为项目整体目录结构。

WebAppDbTest

3、项目代码说明

3.1、CSharp/C# 类文件说明

1、控制器类(Controllers

  • LiteDbController.cs,针对 LiteDb 数据库的 CRUD 方法;
  • SqliteController.cs,针对 SQLite 数据库的 CRUD 方法;
  • WeatherForecastController.cs(项目默认的类);

2、模型类(Models

  • ActionExecTime.cs,记录方法执行时间;
  • AppLogs.cs,记录日志信息;
  • WeatherForecast.cs,天气预报数据模型;

3、服务类(Services

  • AppLogsServices.cs,提供日志写入相关方法;
using System.Text.Json;
using WebAppDbTest.Models;namespace WebAppDbTest.Services;/// <summary>
/// 接口规范定义
/// </summary>
public interface IAppLogsServices
{/// <summary>/// 写入日志信息/// </summary>/// <param name="logs"></param>/// <param name="logLevel"></param>/// <returns></returns>Task WriteLogAsync(AppLogs logs, LogLevel logLevel = LogLevel.Information);/// <summary>/// 模型数据序列化json字符串/// </summary>/// <typeparam name="TData"></typeparam>/// <param name="data"></param>/// <returns></returns>Task<string> JsonSerializeAsync<TData>(TData data);
}/// <summary>
/// 接口规范实现
/// </summary>
public class AppLogsServices : IAppLogsServices
{#region 构造函数 DIprivate readonly ILogger<AppLogsServices> _logger;public AppLogsServices(ILogger<AppLogsServices> logger){_logger = logger;}#endregion/// <summary>/// 写入日志信息/// </summary>/// <param name="logs"></param>/// <param name="logLevel"></param>/// <returns></returns>public async Task WriteLogAsync(AppLogs logs, LogLevel logLevel = LogLevel.Information){logs.LogLevel = logLevel;string jsonLogs = await JsonSerializeAsync(logs);switch (logLevel){case LogLevel.Trace:_logger.LogTrace(jsonLogs);break;case LogLevel.Debug:_logger.LogDebug(jsonLogs);break;case LogLevel.Information:_logger.LogInformation(jsonLogs);break;case LogLevel.Warning:_logger.LogWarning(jsonLogs);break;case LogLevel.Error:_logger.LogError(jsonLogs);break;case LogLevel.Critical:_logger.LogCritical(jsonLogs);break;case LogLevel.None:_logger.LogInformation(jsonLogs);break;default:_logger.LogInformation(jsonLogs);break;}}/// <summary>/// json 序列化/// </summary>/// <typeparam name="TData"></typeparam>/// <param name="data"></param>/// <returns></returns>public async Task<string> JsonSerializeAsync<TData>(TData data){var options = new JsonSerializerOptions{PropertyNameCaseInsensitive = true};await using var stream = new MemoryStream();await JsonSerializer.SerializeAsync(stream, data, options);stream.Position = 0;using var reader = new StreamReader(stream);return await reader.ReadToEndAsync();}
}
  • WeatherForecastServices.cs,模拟天气预报的数据;
using LiteDB;
using LiteDB.Async;
using Mapster;
using System.Diagnostics;
using System.Linq.Expressions;
using WebAppDbTest.Models;namespace WebAppDbTest.Services;/// <summary>
/// 天气预报接口规范定义
/// </summary>
public interface IWeatherForecastServices
{/// <summary>/// 获取天气预报概要/// </summary>/// <returns></returns>string GetSummarie();/// <summary>/// 获取天气预报列表/// </summary>/// <param name="count"></param>/// <returns></returns>IEnumerable<WeatherForecast> GetWeatherForecasts(int count);#region about litedb crudTask<Guid> LiteDbAddSingleAsync<T>(string collectioName, T t);Task<int> LiteDbAddBulkAsync<T>(string collectioName, IEnumerable<T> list);Task<T> LiteDbGetSingleAsync<T>(string collectioName, Guid id);Task<IEnumerable<T>> LiteDbGetAllAsync<T>(string collectioName);Task<bool> LiteDbUpdateSingleAsync<T>(string collectioName, T t);Task<int> LiteDbUpdateBulkAsync<T>(string collectioName, IEnumerable<T> list);Task<bool> LiteDbDeleteSingleAsync<T>(string collectioName, Guid id);Task<int> LiteDbDeleteBulkAsync<T>(string collectioName, Expression<Func<T, bool>> predicate);#endregion#region about sqlite crudTask<Guid> SqliteAddSingleAsync<T>(T t) where T : BaseEntity;Task<int> SqliteAddBulkAsync<T>(IEnumerable<T> list) where T : BaseEntity;Task<T> SqliteGetSingleAsync<T>(Guid id) where T : BaseEntity;Task<IEnumerable<T>> SqliteGetAllAsync<T>() where T : BaseEntity;Task<bool> SqliteUpdateSingleAsync<T>(T t) where T : BaseEntity, new();Task<int> SqliteUpdateBulkAsync<T>(IEnumerable<T> list) where T : BaseEntity, new();Task<bool> SqliteDeleteSingleAsync<T>(Guid id) where T : BaseEntity;Task<int> SqliteDeleteBulkAsync<T>(List<Guid> ids) where T : BaseEntity;#endregion
}/// <summary>
/// 天气预报接口规范实现,模拟天气预报的数据
/// </summary>
public class WeatherForecastServices : IWeatherForecastServices
{#region 构造函数 DIprivate readonly IAppLogsServices _logger;private readonly IConfiguration _configuration;private readonly IFreeSql _freeSql;private readonly IWebHostEnvironment _webHostEnvironment;public WeatherForecastServices(IAppLogsServices logger,IConfiguration configuration,IFreeSql freeSql,IWebHostEnvironment webHostEnvironment){_logger = logger;_configuration = configuration;_freeSql = freeSql;_webHostEnvironment = webHostEnvironment;}#endregion#region 模拟数据/// <summary>/// 模拟天气情况摘要数据列表/// </summary>private static readonly string[] Summaries = new[]{"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"};public string GetSummarie() => Summaries[Random.Shared.Next(Summaries.Length)];public IEnumerable<WeatherForecast> GetWeatherForecasts(int count){if (count <= 0 || count > 1000) count = 1000;/** 等效代码如下return Enumerable.Range(1, count).Select(index => {int temperatureC = Random.Shared.Next(-20, 55);var wf = new WeatherForecast{Id = Guid.NewGuid(),//Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),Date = DateTime.Now.AddDays(index),TemperatureC = temperatureC,TemperatureF = 32 + (int)(temperatureC / 0.5556),Summary = GetSummarie()};return wf;}).ToArray();*/return Enumerable.Range(1, count).Select(index => GetWeatherForecast(index)).ToArray();}private WeatherForecast GetWeatherForecast(int index) {int temperatureC = Random.Shared.Next(-20, 55);var wf = new WeatherForecast{Id = Guid.NewGuid(),Date = DateTime.Now.AddDays(index),TemperatureC = temperatureC,TemperatureF = 32 + (int)(temperatureC / 0.5556),Summary = GetSummarie()};return wf;}#endregionprivate enum DbFileType { LiteDB, SQLite };private string GetConnString(int index, DbFileType dbFileType = DbFileType.LiteDB) {string? dbFile = _configuration.GetSection($"DbConfig:{index}:DbFilePath").Value;string filePath = Path.Combine(_webHostEnvironment.ContentRootPath, dbFile);string dbConnString = string.Empty;switch (dbFileType){case DbFileType.LiteDB:dbConnString = $"Filename={ filePath };Connection=shared;Password=123456";break;case DbFileType.SQLite:dbConnString = $"Data Source={ filePath };Version=3;Pooling=False;Max Pool Size=100";break;default:dbConnString = $"Filename={ filePath };Connection=shared;Password=123456";break;}return dbConnString;}private static readonly Stopwatch _sw = new();/// <summary>/// 记录信息/// </summary>/// <param name="ts">方法执行耗时,单位:毫秒/ms</param>/// <param name="appLogs"></param>/// <returns></returns>private async Task LiteDbWraiteInfoAsync(ActionExecInfo actionExecInfo, AppLogs appLogs){// 记录操作方法执行的时间string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<ActionExecInfo>(nameof(ActionExecInfo));var item = await collection.InsertAsync(actionExecInfo);appLogs.ActionExecInfoId = item.AsGuid;// 记录日志await _logger.WriteLogAsync(appLogs);}#region About LiteDb CRUDpublic async Task<Guid> LiteDbAddSingleAsync<T>(string collectioName, T t){_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);var item = await collection.InsertAsync(t);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "AddSingle",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "AddSingle",ItemCount = 1,OperationInfo = $"[AddSingle] ==> 插入数据:1条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return item.AsGuid;}public async Task<int> LiteDbAddBulkAsync<T>(string collectioName, IEnumerable<T> list){_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个表和 MongoDB 一样的var collection = db.GetCollection<T>(collectioName);int rcount = await collection.InsertBulkAsync(list);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "AddBulk",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "AddBulk",ItemCount = 1,OperationInfo = $"[AddBulk] ==> 插入数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount;}public async Task<T> LiteDbGetSingleAsync<T>(string collectioName, Guid id) {_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);var result = await collection.FindByIdAsync(id); // 下面代码等效// var item = await collection.FindOneAsync(x => x.Id == id);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "GetSingle",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "GetSingle",ItemCount = 1,OperationInfo = $"[GetSingle] ==> 查询数据:1条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return result;}public async Task<IEnumerable<T>> LiteDbGetAllAsync<T>(string collectioName){_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);var result = await collection.FindAllAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "GetAll",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "GetAll",ItemCount = result.Count(),OperationInfo = $"[GetAll] ==> 查询数据:{result.Count()}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return result;}public async Task<bool> LiteDbUpdateSingleAsync<T>(string collectioName, T t) {_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);bool isOk = await collection.UpdateAsync(t);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "UpdateSingle",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "UpdateSingle",ItemCount = 1,OperationInfo = $"[UpdateSingle] ==> 更新数据:1条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return isOk;}public async Task<int> LiteDbUpdateBulkAsync<T>(string collectioName, IEnumerable<T> list){_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);int rcount = await collection.UpdateAsync(list);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "UpdateBulk",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "UpdateBulk",ItemCount = rcount,OperationInfo = $"[UpdateBulk] ==> 更新数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount;}public async Task<bool> LiteDbDeleteSingleAsync<T>(string collectioName, Guid id) {_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);bool isOk = await collection.DeleteAsync(id);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "DeleteSingle",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "DeleteSingle",ItemCount = 1,OperationInfo = $"[DeleteSingle] ==> 删除数据:1条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return isOk;}public static BsonValue Serialize(Guid id) => new BsonDocument(new Dictionary<string, BsonValue>{{"_id", id }});public static Guid Deserialize(BsonValue bsonValue){var id = bsonValue["_id"].AsGuid;return id;}public async Task<int> LiteDbDeleteBulkAsync<T>(string collectioName, Expression<Func<T, bool>> predicate) {_sw.Start();string connectionString = GetConnString(0);//打开数据库,如果不存在会自动创建。using var db = new LiteDatabaseAsync(connectionString);//打开一个集合和 MongoDB 一样的,类似关系数据库的表。var collection = db.GetCollection<T>(collectioName);//int rcount = await collection.DeleteAllAsync();int rcount = await collection.DeleteManyAsync(predicate);_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "DeleteBulk",ExecTime = ts,Database = "litedb"};// 记录日志var appLogs = new AppLogs{Label = "DeleteBulk",ItemCount = rcount,OperationInfo = $"[DeleteBulk] ==> 删除数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount;}#endregion#region About SQLite CRUDpublic async Task<Guid> SqliteAddSingleAsync<T>(T t) where T : BaseEntity{_sw.Start();var rcount = await _freeSql.Insert(t).ExecuteAffrowsAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "AddSingle",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "AddSingle",ItemCount = rcount,OperationInfo = $"[AddSingle] ==> 插入数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return t.Id;}public async Task<int> SqliteAddBulkAsync<T>(IEnumerable<T> list) where T : BaseEntity{_sw.Start();int rcount = await _freeSql.Insert(list).ExecuteAffrowsAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "AddBulk",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "AddBulk",ItemCount = 1,OperationInfo = $"[AddBulk] ==> 插入数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount;}public async Task<T> SqliteGetSingleAsync<T>(Guid id) where T : BaseEntity{_sw.Start();var result = await _freeSql.Select<T>().Where(x => x.Id == id).FirstAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "GetSingle",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "GetSingle",ItemCount = 1,OperationInfo = $"[GetSingle] ==> 查询数据:1条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return result;}public async Task<IEnumerable<T>> SqliteGetAllAsync<T>() where T : BaseEntity{_sw.Start();var result = await _freeSql.Select<T>().ToListAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "GetAll",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "GetAll",ItemCount = result.Count(),OperationInfo = $"[GetAll] ==> 查询数据:{result.Count()}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return result;}public async Task<bool> SqliteUpdateSingleAsync<T>(T t) where T : BaseEntity, new(){_sw.Start();// 推荐快照模式var repo = _freeSql.GetRepository<T>();var item = new T { Id = t.Id };repo.Attach(item); //此时快照 itemt.Adapt(item);//bool isOk = ReferenceEquals(item, t);int rcount = await repo.UpdateAsync(item); //对比快照时的变化// 传统模式// int rcount = await _freeSql.Update<T>().SetSource(t).IgnoreColumns(a => new { a.Id }).ExecuteAffrowsAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "UpdateSingle",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "UpdateSingle",ItemCount = rcount,OperationInfo = $"[UpdateSingle] ==> 更新数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount > 0;}public async Task<int> SqliteUpdateBulkAsync<T>(IEnumerable<T> list) where T : BaseEntity, new(){_sw.Start();// 推荐快照模式var repo = _freeSql.GetRepository<T>();var items = list.Select(x => new T{ Id = x.Id });repo.Attach(items); //此时快照 item//list.Adapt(items);items = list;bool isOk = ReferenceEquals(items, list);int rcount = await repo.UpdateAsync(items); //对比快照时的变化// 传统模式//int rcount = await _freeSql.Update<T>().SetSource(list).IgnoreColumns(a => new { a.Id }).ExecuteAffrowsAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "UpdateBulk",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "UpdateBulk",ItemCount = rcount,OperationInfo = $"[UpdateBulk] ==> 更新数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount;}public async Task<bool> SqliteDeleteSingleAsync<T>(Guid id) where T : BaseEntity{_sw.Start();int rcount = await _freeSql.Delete<T>().Where(x => x.Id == id).ExecuteAffrowsAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "DeleteSingle",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "DeleteSingle",ItemCount = rcount,OperationInfo = $"[DeleteSingle] ==> 删除数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount > 0;}public async Task<int> SqliteDeleteBulkAsync<T>(List<Guid> ids) where T : BaseEntity{_sw.Start();int rcount = await _freeSql.Delete<T>(ids.ToArray()).ExecuteAffrowsAsync();_sw.Stop();TimeSpan ts = _sw.Elapsed;// 记录操作方法执行的时间var actionExecInfo = new ActionExecInfo{ActionName = "DeleteBulk",ExecTime = ts,Database = "sqlite"};// 记录日志var appLogs = new AppLogs{Label = "DeleteBulk",ItemCount = rcount,OperationInfo = $"[DeleteBulk] ==> 删除数据:{rcount}条,耗时:{ts.TotalMilliseconds}ms."};await LiteDbWraiteInfoAsync(actionExecInfo, appLogs);return rcount;}#endregion
}

4、程序入口类

  • Program.cs
using Serilog;
using WebAppDbTest.Services;var builder = WebApplication.CreateBuilder(args);//const string OUTPUT_TEMPLATE = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}";
const string OUTPUT_TEMPLATE = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}";
char b = Path.DirectorySeparatorChar; // 符号 // creates custom collection `applog`
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().Enrich.FromLogContext().CreateLogger();#region Host
builder.Host.ConfigureAppConfiguration((context, config) => {string configPath = $"{context.HostingEnvironment.ContentRootPath}{b}AppData{b}Configuration";config.SetBasePath(configPath).AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true).AddEnvironmentVariables();
}).UseSerilog((context, logger) => {string liteDbPath = Path.Combine(context.HostingEnvironment.ContentRootPath, $"AppData{b}DataBase{b}LiteDbLogs.db");logger.WriteTo.LiteDB(liteDbPath, logCollectionName: "applog");logger.WriteTo.Console(outputTemplate: OUTPUT_TEMPLATE);
});
// .UseSerilog(Log.Logger, dispose: true);
#endregion#region Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();// 注册 AppLogsServices
builder.Services.AddScoped<IAppLogsServices, AppLogsServices>();
// 注册 WeatherForecastServices
builder.Services.AddScoped<IWeatherForecastServices, WeatherForecastServices>();// 注入 Sqlite 类型的 IFreeSql 
//string sqlitePath = $"AppData{b}DataBase{b}SQLiteTest.db";
string sqlitePath = builder.Configuration.GetSection("DbConfig:1:DbFilePath").Value;
string connStr = $"Data Source={Path.Combine(builder.Environment.ContentRootPath, sqlitePath)};Version=3;Pooling=False;Max Pool Size=100";
// Log.Logger.Information(connStr);IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(dataType: FreeSql.DataType.Sqlite, connectionString: connStr).UseAutoSyncStructure(false) //自动同步实体结构【开发环境必备】,FreeSql不会扫描程序集,只有CRUD时才会生成表。//.UseMonitorCommand(cmd => Console.Write(cmd.CommandText)) .UseMonitorCommand(cmd => Log.Logger.Information(cmd.CommandText)).Build(); //请务必定义成 Singleton 单例模式
builder.Services.AddSingleton(fsql); 
#endregionvar app = builder.Build();#region Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseAuthorization();
app.MapControllers(); 
#endregionapp.Run();

3.2、json 配置文件说明

  • appsettings.json
{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","DbConfig": [{"DbType": "LiteDB","DbFilePath": "AppData\\DataBase\\LiteDbTest.db"},{"DbType": "SQLite","DbFilePath": "AppData\\DataBase\\SqliteTest.db"}]
}

相关文件代码此处就不再详细说明,感兴趣的可自行查看项目地址:

  • WebAppDbTesthttps://gitee.com/dolayout/sample/tree/master/code/Sample.WebAppDbTest

4、项目运行预览

  • 启动 WebAppDbTestswagger 页面显示如下:

webapp

  • LiteDB & Sqlite 对应的 CRUD 方法:

webapp-dbtest-crud

数据库 .db 文件准备

1、创建 SQLite 数据库

请访问 SQLite 下载页面,从 Windows 区下载预编译的二进制文件。

1.1、在 Windows 上安装 SQLite

  • SQLite 下载,https://www.sqlite.org/download.html
    sqlite-download

此处我是 Windows 11 x64 环境,下载文件分别如下:

  • sqlite-dll-win-x64-3440200.zip
  • sqlite-tools-win-x64-3440200.zip

把下载文件拷贝到 D 盘并解压文件,如下所示:

sqlite

文件夹默认文件说明:

  • sqlite-dll-win-x64-3440200 文件夹默认包含:sqlite3.defsqlite3.dll 文件;
  • sqlite-tools-win-x64-3440200 文件夹默认包含:sqldiff.exesqlite3.exesqlite3_analyzer.exe 文件;

可以把 D:\sqlite\sqlite-tools-win-x64-3440200 添加到 PATH 环境变量,最后在命令提示符下,使用 sqlite3 命令,此处我就不添加环境变量了,直接双击 sqlite.exe 文件将显示如下结果:

sqlite-tools-win-x64

1.2、创建 SQLite 数据库

依据终端提示信息,输入命令创建数据库 SQLiteTest.db 文件,执行如下:

sqlite> .open SQLiteTest.db

查看 sqlite 更多命令帮助信息:

SQLite version 3.44.2 2023-11-24 11:41:44 (UTF-16 console I/O)
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open SQLiteTest.db
sqlite> PRAGMA key = '123456';
sqlite> .help
.archive ...             Manage SQL archives
.auth ON|OFF             Show authorizer callbacks
.backup ?DB? FILE        Backup DB (default "main") to FILE
.bail on|off             Stop after hitting an error.  Default OFF
.cd DIRECTORY            Change the working directory to DIRECTORY
.changes on|off          Show number of rows changed by SQL
.check GLOB              Fail if output since .testcase does not match
.clone NEWDB             Clone data into NEWDB from the existing database
.connection [close] [#]  Open or close an auxiliary database connection
.crnl on|off             Translate \n to \r\n.  Default ON
.databases               List names and files of attached databases
.dbconfig ?op? ?val?     List or change sqlite3_db_config() options
.dbinfo ?DB?             Show status information about the database
.dump ?OBJECTS?          Render database content as SQL
.echo on|off             Turn command echo on or off
.eqp on|off|full|...     Enable or disable automatic EXPLAIN QUERY PLAN
.excel                   Display the output of next command in spreadsheet
.exit ?CODE?             Exit this program with return-code CODE
.expert                  EXPERIMENTAL. Suggest indexes for queries
.explain ?on|off|auto?   Change the EXPLAIN formatting mode.  Default: auto
.filectrl CMD ...        Run various sqlite3_file_control() operations
.fullschema ?--indent?   Show schema and the content of sqlite_stat tables
.headers on|off          Turn display of headers on or off
.help ?-all? ?PATTERN?   Show help text for PATTERN
.import FILE TABLE       Import data from FILE into TABLE
.indexes ?TABLE?         Show names of indexes
.limit ?LIMIT? ?VAL?     Display or change the value of an SQLITE_LIMIT
.lint OPTIONS            Report potential schema issues.
.load FILE ?ENTRY?       Load an extension library
.log FILE|on|off         Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?OPTIONS?     Set output mode
.nonce STRING            Suspend safe mode for one command if nonce matches
.nullvalue STRING        Use STRING in place of NULL values
.once ?OPTIONS? ?FILE?   Output for the next SQL command only to FILE
.open ?OPTIONS? ?FILE?   Close existing database and reopen FILE
.output ?FILE?           Send output to FILE or stdout if FILE is omitted
.parameter CMD ...       Manage SQL parameter bindings
.print STRING...         Print literal STRING
.progress N              Invoke progress handler after every N opcodes
.prompt MAIN CONTINUE    Replace the standard prompts
.quit                    Stop interpreting input stream, exit if primary.
.read FILE               Read input from FILE or command output
.recover                 Recover as much data as possible from corrupt db.
.restore ?DB? FILE       Restore content of DB (default "main") from FILE
.save ?OPTIONS? FILE     Write database to FILE (an alias for .backup ...)
.scanstats on|off|est    Turn sqlite3_stmt_scanstatus() metrics on or off
.schema ?PATTERN?        Show the CREATE statements matching PATTERN
.separator COL ?ROW?     Change the column and row separators
.session ?NAME? CMD ...  Create or control sessions
.sha3sum ...             Compute a SHA3 hash of database content
.shell CMD ARGS...       Run CMD ARGS... in a system shell
.show                    Show the current values for various settings
.stats ?ARG?             Show stats or turn stats on or off
.system CMD ARGS...      Run CMD ARGS... in a system shell
.tables ?TABLE?          List names of tables matching LIKE pattern TABLE
.timeout MS              Try opening locked tables for MS milliseconds
.timer on|off            Turn SQL timer on or off
.trace ?OPTIONS?         Output each SQL statement as it is run
.version                 Show source, library and compiler versions
.vfsinfo ?AUX?           Information about the top-level VFS
.vfslist                 List all available VFSes
.vfsname ?AUX?           Print the name of the VFS stack
.width NUM1 NUM2 ...     Set minimum column widths for columnar output
sqlite>

此时在当前目录下,SQLite 的数据库文件 SQLiteTest.db 文件就创建好了。

接下来使用 dbeaver-ce 工具连接数据库文件测试:

连接测试

  • sqlite 数据表脚本:
-- WeatherForecast definitionCREATE TABLE "WeatherForecast" (  "Id" CHARACTER(36) NOT NULL, "Date" TEXT NOT NULL, "TemperatureC" INTEGER NOT NULL, "TemperatureF" INTEGER NOT NULL, "Summary" NVARCHAR(255), PRIMARY KEY ("Id")
);

2、创建 LiteDB 数据库

2.1、LiteDB.Shell

LiteDB 项目包含一个简单的控制台应用程序 (LiteDB.Shell.exe),可用于查看、更新以及测试你的数据,在处理你的数据库时非常有用。

  • LiteDB.Shell 项目地址,https://github.com/mustakimali/LiteDB.Shell.NetCore

2.2、创建 LiteDB 数据库

使用 LiteDB.Shell 创建数据库,执行如下命令:

> open <filename>|<connectionString>Open/Crete a new database

基本 Shell 命令,尝试使用 help full 执行所有命令:

Basic Shell Commands - try `help full` for all commands
=======================================================
> open <filename>|<connectionString>Open/Crete a new database> show collectionsList all collections inside database> db.<collection>.insert <jsonDoc>Insert a new document into collection> db.<collection>.update <jsonDoc>Update a document inside collection> db.<collection>.delete <filter>Delete documents using a filter clausule (see find)> db.<collection>.find <filter> [skip N][limit N]Show filtered documents based on index search> db.<collection>.count <filter>Show count rows according query filter> db.<collection>.ensureIndex <field> [true|{options}]Create a new index document field. For unique key, use true> db.<collection>.indexesList all indexes in this collection<filter> = <field> [=|>|>=|<|<=|!=|like|between] <jsonValue>Filter query syntax<filter> = (<filter> [and|or] <filter> [and|or] ...)Multi queries syntaxTry:> db.customers.insert { _id:1, name:"John Doe", age: 37 }> db.customers.ensureIndex name> db.customers.find name like "John"> db.customers.find name like "John" and _id between [0, 100] limit 10

说明:litedb 数据库和数据集无需创建,当不存在时执行 crud 代码会自动创建。

好了先到这里,我们就把测试项目准备好了,关于接口测试性能对比,下篇再续,敬请观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/219389.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# WPF上位机开发(函数运行时间分析)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 上位机除了基本功能和稳定性之外&#xff0c;还有一个要注意的就是运行效率的问题。如果我们想提高软件的运行效率&#xff0c;单位时间做更多的工…

用Go汇编实现一个快速排序算法

本代码全网首发&#xff0c;使用Go plan9 windows arm64汇编&#xff0c;实现基础版快速排序算法。 未引入随机因子的快速排序的普通Go代码长这样。 func QuickSort(arr []int) {if len(arr) < 1 {return}base, l, r : arr[0], 0, len(arr)-1for i : 1; i < r; {if arr…

【jenkins操作步骤】

一、安装ant 1、下载安装文件 1.1 进入https://ant.apache.org/ 然后点击 https://ant.apache.org/bindownload.cgi 超连接下载即可 1.2下载到本地&#xff0c;最好放到D盘下&#xff0c;然后把apache-jmeter-4.0\extras目录下的ant-jmeter-1.1.1.jar 文件放置到ant下的lib目…

Unity Web 浏览器-3D WebView中有关于CanvasWebViewPrefab

一、CanvasWebViewPrefab默认设置 这个是在2_CanvasWebViewDemo示例场景文件中可以可以查看得到&#xff0c;可以看出CanvasWebViewPrefab的默认配置如下。 二、Web 浏览器网页和Unity内置UI的渲染顺序 1、如果你勾选了以下这个Native 2D Mode选项的话&#xff0c;那么Unit…

MacOS多屏状态栏位置不固定,程序坞不小心跑到副屏

目录 方式一&#xff1a;通过系统设置方式二&#xff1a;鼠标切换 MacOS多屏状态栏位置不固定&#xff0c;程序坞不小心跑到副屏 方式一&#xff1a;通过系统设置 先切换到左边 再切换到底部 就能回到主屏了 方式二&#xff1a;鼠标切换 我的两个屏幕放置位置如下 鼠标在…

【IC前端虚拟项目】MVU模块方案与背景熟悉

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 mvu这个模块是干嘛用的呢&#xff1f;从这个名字就可以看出来move_unit&#xff0c;应该是做数据搬运的。很多指令级中都会有数据搬运的指令&#xff0c;这类指令的作用一般是在片内片外缓存以及通用专用…

阿里云RDS MySQL 数据如何快速同步到 ClickHouse

云数据库 RDS MySQL 和 云数据库 ClickHouse 是阿里云推出的两个备受欢迎的数据库解决方案&#xff0c;它们为用户提供了可靠的数据存储方案、分析数仓方案&#xff0c;本文介绍如何快速将 RDS MySQL 的数据同步到云数据库 ClickHouse。 如何快速将RDSMySQL的数据同步到云数据库…

【Linux】cp问题,生产者消费者问题代码实现

文章目录 前言一、 BlockQueue.hpp&#xff08;阻塞队列&#xff09;二、main.cpp 前言 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用…

Sketch for Mac:实现你的创意绘图梦想的矢量绘图软件

随着数字时代的到来&#xff0c;矢量绘图软件成为了广告设计、插画创作和UI设计等领域中必不可少的工具。在众多矢量绘图软件中&#xff0c;Sketch for Mac&#xff08;矢量绘图软件&#xff09;以其强大的功能和简洁的界面脱颖而出&#xff0c;成为了众多设计师的首选。 Sket…

RHEL7.5编译openssl1.1.1w源码包到rpm包

openssl1.1.1w下载地址 https://www.openssl.org/source/ 安装依赖包 yum -y install curl which make gcc perl perl-WWW-Curl rpm-build wget http://mirrors.aliyun.com/centos-vault/7.5.1804/os/x86_64/Packages/perl-WWW-Curl-4.15-13.el7.x86_64.rpm rpm -ivh pe…

hive常用SQL函数及案例

1 函数简介 Hive会将常用的逻辑封装成函数给用户进行使用&#xff0c;类似于Java中的函数。 好处&#xff1a;避免用户反复写逻辑&#xff0c;可以直接拿来使用。 重点&#xff1a;用户需要知道函数叫什么&#xff0c;能做什么。 Hive提供了大量的内置函数&#xff0c;按照其特…

【95 6K⭐】Scrcpy:一款免费、强大且实用的Android镜像投屏控制软件

【95.6K⭐】Scrcpy&#xff1a;一款免费、功能丰富且实用的Android镜像投屏控制软件 随着科技的不断发展&#xff0c;我们的生活中出现了越来越多的智能设备。尤其是智能手机&#xff0c;已经成为了我们日常生活中不可或缺的一部分。然而&#xff0c;有时候我们需要在电脑上操…

CesiumLab地理信息基础数据处理平台 各类数据类型介绍、发布数据介绍

目录 0 引言1 CesiumLab2 数据处理模块2.1 输出格式&#xff1a;切片文件格式2.2 输入格式2.2.1 传统GIS数据2.2.2 人工模型2.2.3 BIM模型2.2.4 倾斜实景数据2.2.5 点云数据 3 发布服务功能3.1 拓展&#xff1a;其他平台发布服务功能 &#x1f64b;‍♂️ 作者&#xff1a;海码…

件夹和文件比较软件VisualDiffer mac功能介绍

VisualDiffer mac是一款运行在MacOS上的文件夹和文件快速比较工具。VisualDiffer可以对不同文件夹中文件或文档做出比较或者比较两个文件的路径。还可以通过UNIS diff命令快速、标准和可靠的比较出各类不同的文件夹和文件结果&#xff0c;使用不同的颜色直观地显示。 VisualDif…

Element 介绍

Element 介绍 Vue 快速入门 Vue 常见组件 表格 分页组件 其他自己去看吧 链接: 其他组件

基于linux系统的Tomcat+Mysql+Jdk环境搭建(二)jdk1.8 linux 上传到MobaXterm 工具的已有session里

【JDK安装】 1.首先下载一个JDK版本 官网地址&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载1.8版本&#xff0c;用红框标注出来了&#xff1a; 也许有的同学看到没有1.8版本&#xff0c;你可以随便下载一个linux的…

保姆级:Windows Server 2012上安装.NET Framework 3.5

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Windows》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有…

MySQL笔记-第10章_创建和管理表

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第10章_创建和管理表1. 基础知识1.1 一条数据存储的过程1.2 标识符命名规则1.3 MySQL中的数据类型 2. 创建和管理数据库2.1 创建数据库2.2 使…

在Node.js中MongoDB排序的方法

本文主要介绍在Node.js中MongoDB排序的方法。 目录 Node.js中MongoDB排序使用原生的mongodb驱动程序进行排序使用Mongoose库中的排序 Node.js中MongoDB排序 在Node.js中使用MongoDB进行排序&#xff0c;可以使用原生的mongodb驱动程序或者Mongoose库。 使用原生的mongodb驱动…

spring6 基于xml自动装配

目录结构 代码 UserContronller.java package bean.auto.controller;import bean.auto.service.UserService; import bean.auto.service.UserServiceImpl;public class UserContronller {private UserService userService;public void setUserService(UserService userServ…