Skip to content

Commit

Permalink
[feat]CsvDb支持事务,开启事务后加速数据操作,提交事务时再写入磁盘
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Aug 28, 2024
1 parent 06a30af commit c8645c1
Show file tree
Hide file tree
Showing 2 changed files with 360 additions and 267 deletions.
99 changes: 84 additions & 15 deletions NewLife.Core/IO/CsvDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ namespace NewLife.IO;
/// <summary>Csv文件轻量级数据库</summary>
/// <remarks>
/// 文档 https://newlifex.com/core/csv_db
/// 适用于大量数据需要快速存储、快速查找,很少修改和删除的场景。
/// 适用于大量数据需要快速追加、快速查找,很少修改和删除的场景。
/// 在桌面客户端中,关系型数据库SQLite很容易因非法关机而损坏,本数据库能跳过损坏行,自动恢复。
///
/// 中间插入和删除时,需要移动尾部数据,性能较差。
///
/// 本设计不支持线程安全,务必确保单线程操作。
/// </remarks>
/// <typeparam name="T"></typeparam>
public class CsvDb<T> where T : new()
public class CsvDb<T> : DisposeBase where T : new()
{
#region 属性
/// <summary>文件名</summary>
Expand All @@ -34,14 +38,43 @@ namespace NewLife.IO;
/// <summary>实例化Csv文件数据库</summary>
/// <param name="comparer"></param>
public CsvDb(Func<T?, T?, Boolean> comparer) => Comparer = new MyComparer(comparer);

/// <summary>销毁</summary>
/// <param name="disposing"></param>
protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);

Commit();
}
#endregion

#region 基础方法
private List<T>? _cache;
/// <summary>开启事务,便于批量处理数据</summary>
public void BeginTransaction()
{
_cache ??= FindAll().ToList();
}

/// <summary>提交事务,把缓存数据写入磁盘</summary>
public void Commit()
{
if (_cache == null) return;

Write(_cache, false);
_cache = null;
}
#endregion

#region 方法
/// <summary>强行写入数据</summary>
#region 添删改查
/// <summary>批量写入数据(高性能)</summary>
/// <param name="models">要写入的数据</param>
/// <param name="append">是否附加在尾部</param>
/// <param name="append">是否附加在尾部。为false时从头写入,覆盖已有数据</param>
public void Write(IEnumerable<T> models, Boolean append)
{
if (!models.Any() && append) return;

var file = GetFile();
file.EnsureDirectory(true);

Expand Down Expand Up @@ -70,11 +103,23 @@ public void Write(IEnumerable<T> models, Boolean append)

/// <summary>尾部插入数据,性能极好</summary>
/// <param name="model"></param>
public void Add(T model) => Add([model]);
public void Add(T model)
{
if (_cache != null)
_cache.Add(model);
else
Write([model], true);
}

/// <summary>尾部插入数据,性能极好</summary>
/// <param name="models"></param>
public void Add(IEnumerable<T> models) => Write(models, true);
public void Add(IEnumerable<T> models)
{
if (_cache != null)
_cache.AddRange(models);
else
Write(models, true);
}

/// <summary>删除数据,性能很差,全部读取剔除后保存</summary>
/// <param name="model"></param>
Expand All @@ -101,6 +146,8 @@ public Int32 Remove(Func<T, Boolean> predicate)

lock (this)
{
if (_cache != null) return _cache.RemoveAll(x => predicate(x));

var list = FindAll();
if (list.Count == 0) return 0;

Expand All @@ -119,7 +166,13 @@ public Int32 Remove(Func<T, Boolean> predicate)
}

/// <summary>清空数据。只写头部</summary>
public void Clear() => Write([], false);
public void Clear()
{
if (_cache != null)
_cache.Clear();
else
Write([], false);
}

/// <summary>更新指定数据行,性能很差,全部读取替换后保存</summary>
/// <param name="model"></param>
Expand All @@ -137,7 +190,7 @@ private Boolean Set(T model, Boolean add)

lock (this)
{
var list = FindAll();
var list = _cache ?? FindAll();
if (!add && list.Count == 0) return false;

// 找到目标数据行,并替换
Expand All @@ -159,7 +212,10 @@ private Boolean Set(T model, Boolean add)
}

// 重新写回去
Write(list, false);
if (_cache == null)
{
Write(list, false);
}

return true;
}
Expand All @@ -182,22 +238,36 @@ private Boolean Set(T model, Boolean add)

/// <summary>获取所有数据行</summary>
/// <returns></returns>
public IList<T> FindAll() => Query(null).ToList();
public IList<T> FindAll() => _cache?.ToList() ?? Query(null).ToList();

/// <summary>获取满足条件的数据行,性能好,顺序查找</summary>
/// <param name="predicate"></param>
/// <param name="count"></param>
/// <returns></returns>
public IEnumerable<T> Query(Func<T, Boolean>? predicate, Int32 count = -1)
{
// 开启事务时,直接返回缓存数据
if (_cache != null)
{
foreach (var item in _cache)
{
if (predicate == null || predicate(item))
{
yield return item;

if (--count == 0) break;
}
}
yield break;
}

var file = GetFile();
if (!File.Exists(file)) yield break;

lock (this)
{
using var csv = new CsvFile(file, false) { Encoding = Encoding };

//var list = new List<T>();
var headers = new Dictionary<String, Int32>();
var pis = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
while (true)
Expand Down Expand Up @@ -236,7 +306,6 @@ public IEnumerable<T> Query(Func<T, Boolean>? predicate, Int32 count = -1)

if (predicate == null || predicate(model))
{
//list.Add(model);
flag = true;
}
}
Expand All @@ -254,15 +323,15 @@ public IEnumerable<T> Query(Func<T, Boolean>? predicate, Int32 count = -1)
if (--count == 0) break;
}
}

//return list;
}
}

/// <summary>获取数据行数,性能极好,文件行数(除头部)</summary>
/// <returns></returns>
public Int32 FindCount()
{
if (_cache != null) return _cache!.Count;

lock (this)
{
var file = GetFile();
Expand Down
Loading

0 comments on commit c8645c1

Please sign in to comment.