LiteDB的基本数据结构
BsonDocument
BsonDocument
用于存储单一对象,其构造函数接收字典型数据,定义存储的具体内容。
BsonArray
BsonArray
用于存储多项同类型对象,其构造函数接收对象集合。
BsonValue
BsonValue
是BsonDocument
和BsonArray
的公共基类,可以在运行时确定其具体类型,通过一系列As*
方法将其转换为具体类型。
简单对象的存储
对于单一对象,灵活、地控制存储的存取对象数据,是通过自定义对象的序列化和反序列化函数实现。
//
// 自身直接支持LiteDB存储的Point类
//
public readonly struct Point
{
public Point(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public static Point Zero { get; } = new Point(, );
#region LiteDB
/// <summary>
/// 在静态构造函数中调用注册函数,
/// 确保LiteDB引擎能够找到此类对应的序列化和反序列化函数
/// </summary>
static Point() => RegisterType();
/// <summary>
/// 注册序列化与反序列化函数
/// </summary>
public static void RegisterType()
=> BsonMapper.Global.RegisterType(Serialize, Deserialize);
/// <summary>
/// 序列化
/// </summary>
public static BsonValue Serialize(Point parameter)
=> new BsonDocument(new Dictionary<string, BsonValue>
{
{"X", parameter.X}, // 定义需要存储的数据项
{"Y", parameter.Y},
});
/// <summary>
/// 反序列化
/// </summary>
public static Point Deserialize(BsonValue bsonValue)
{
var x = bsonValue["X"].AsDouble; // 提取、解析存储的数据项
var y = bsonValue["Y"].AsDouble;
return new Point(x, y);
}
#endregion
}
如果存储地对象属于基础类,或者来源于第三方库,为避免因支持LiteDB数据存储带来的“侵入性(invasive)”问题(即为了支持数据持久化儿需要特地修改类的源代码,添加一些与类自身内在逻辑无关的代码),对象的序列化和反序列化函数可以放在类的外部实现,并在必要时,手动注册。
//
// 不直接支持LiteDB存储的Point类
//
public readonly struct Point
{
public Point(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public static Point Zero { get; } = new Point(, );
}
//
// 用于支持Point类存储的工具类
//
public static class PointMapper
{
/// <summary>
/// 注册序列化与反序列化函数
/// </summary>
public static void RegisterType()
=> BsonMapper.Global.RegisterType(Serialize, Deserialize);
public static BsonValue Serialize(Point parameter)
=> new BsonDocument(new Dictionary<string, BsonValue>
{
{"X", parameter.X},
{"Y", parameter.Y},
});
public static Point Deserialize(BsonValue bsonValue)
{
var x = bsonValue["X"].AsDouble;
var y = bsonValue["Y"].AsDouble;
return new Point(x, y);
}
}
集合类的存储
对于需要存储的集合类型的属性,好单独定义一个集合类,然后在这一集合类中自定义如何存储集合中得数据。
通过数组存储集合类数据
public class PointCollection : List<Point>
{
public PointCollection() { }
public PointCollection(IEnumerable<Point> points) : base(points) { }
#region LiteDB
static PointCollection() => RegisterType();
public static void RegisterType()
=> BsonMapper.Global.RegisterType(Serialize, Deserialize);
/// <summary>
/// 通过BsonArray存储集合元素
/// 调用Point.Serialize执行Point对象的序列化
/// </summary>
public static BsonValue Serialize(PointCollection parameter)
=> new BsonArray(parameter.Select(Point.Serialize));
/// <summary>
/// 提取BsonArray中存储的集合元素
/// 调用Point.Deserialize执行Point对象的反序列化
/// </summary>
public static PointCollection Deserialize(BsonValue bsonValue)
=> new PointCollection(bsonValue.AsArray.Select(Point.Deserialize));
#endregion
}
此时LiteDB中存储的数据的格式为
{
"_id": {"$oid": "6060415bebed2131241a79d6"},
"Location": // 单个对象
{
"X": 100.0,
"Y": 200.0
},
"Boundary": // 集合
[
{
"X": 0.0,
"Y": 0.0
},
{
"X": 12.0,
"Y": 34.0
},
{
"X": 56.0,
"Y": 78.0
}
]
}
优化集合类数据的存储
当集合类中的数据项数较多,且元素的数据结构较简单时,可以优化数据的存储,采用更加紧凑的数据结构保存数据,可以提高数据存储的效率。
public class PointCollection : List<Point>
{
public PointCollection() { }
public PointCollection(IEnumerable<Point> points) : base(points) { }
#region LiteDB
static PointCollection() => RegisterType();
public static void RegisterType()
=> BsonMapper.Global.RegisterType(Serialize, Deserialize);
/// <summary>
/// 将点集合[(X1,Y1),(X2,Y2),(X3,Y3) ...],存储为一维数组[X1,Y1,X2,Y2,X3,Y3 ...]
/// </summary>
public static BsonValue Serialize(PointCollection parameter)
=> new BsonArray(parameter.SelectMany(point => new[] {point.X, point.Y})
.Select(v => new BsonValue(v)));
/// <summary>
/// 将一维数组[X1,Y1,X2,Y2,X3,Y3 ...],解析为点集合[(X1,Y1),(X2,Y2),(X3,Y3) ...]
/// </summary>
public static PointCollection Deserialize(BsonValue bsonValue)
{
var data = bsonValue.AsArray.Select(v => v.AsDouble).ToList();
Debug.Assert(data.Count % 2 == );
var points = new List<Point>(data.Count / 2);
for (var i = ; i < points.Count / 2; i++)
{
points.Add(new Point(data[i * 2], data[i * 2 + 1]));
}
return new PointCollection(points);
}
#endregion
}
此时LiteDB中存储的数据的格式为
{
"_id": {"$oid": "6060497febed2130105de9db"},
"Location":
{
"X": 100.0,
"Y": 200.0
},
"Boundary": // 数据存储更紧凑高效
[
0.0,
0.0,
12.0,
34.0,
56.0,
78.0
]
}
顶层对象的存储
对于顶层对象,LiteDB要求为顶层对象添加一个ID属性。同时,LiteDB在读取顶层对象时,需要指定顶层对象文档集合的名称,这一名称好也由顶层对象类提供。
public class Polygon
{
public Polygon(Point location, PointCollection boundary)
: this(ObjectId.NewObjectId(), location, boundary) { }
/// <summary>
/// 用于LiteDB反序列化的构造函数
/// </summary>
private Polygon(ObjectId id, Point location, PointCollection boundary)
{
Id = id;
Location = location;
Boundary = boundary;
}
public Point Location { get; }
public PointCollection Boundary { get; }
/// <summary>
/// 默认值
/// </summary>
public static Polygon Default { get; } = new Polygon(Point.Zero, new PointCollection());
#region LiteDB
#region LiteDB顶层对象存储
/// <summary>
/// 添加Id属性后,才能作为顶层对象在LiteDB中存储
/// </summary>
public ObjectId Id { get; set; }
/// <summary>
/// LiteDB顶层对象的文档集合名称
/// </summary>
public static string DbCollectionName { get; } = "PolygonData";
/// <summary>
/// 添加顶层对象数据
/// </summary>
public static void CreateData(LiteDatabase db, Polygon polygon)
{
var data = db.GetCollection<Polygon>(DbCollectionName);
if (data.Count() > )
{
data.DeleteAll();
}
data.Insert(polygon);
}
/// <summary>
/// 添加顶层对象数据的默认值
/// </summary>
public static void CreateDefaultData(LiteDatabase db)
=> CreateData(db, Default);
/// <summary>
/// 读取顶层对象数据
/// </summary>
public static Polygon GetData(LiteDatabase db)
=> db.GetCollection<Polygon>(DbCollectionName).FindAll().FirstOrDefault();
/// <summary>
/// 更新顶层对象数据
/// </summary>
public static void UpdateData(LiteDatabase db, Polygon polygon)
=> db.GetCollection<Polygon>(DbCollectionName).Update(polygon);
#endregion
#region 序列化与反序列化
static Polygon() => RegisterType();
public static void RegisterType()
=> BsonMapper.Global.RegisterType(Serialize, Deserialize);
public static BsonValue Serialize(Polygon parameter)
=> new BsonDocument(new Dictionary<string, BsonValue>()
{
{"_id", parameter.Id},
{"Location", Point.Serialize(parameter.Location)},
{"Boundary", PointCollection.Serialize(parameter.Boundary)},
});
public static Polygon Deserialize(BsonValue bsonValue)
{
Debug.Assert(!bsonValue.IsNull);
var id = bsonValue["_id"].AsObjectId;
var location = Point.Deserialize(bsonValue["Location"]);
var boundary = PointCollection.Deserialize(bsonValue["Boundary"]);
return new Polygon(id, location, boundary);
}
#endregion
#endregion
}
顶层对象的读写
var dbFilePath = @"D:\testLiteDB2.db";
using (var db = new LiteDatabase(dbFilePath))
{
Polygon.CreateDefaultData(db); // 创建默认对象
}
using (var db = new LiteDatabase(dbFilePath))
{
var polygon = Polygon.GetData(db); // 读取对象
// 修改对象状态
polygon.Boundary.AddRange(new[]
{
new Point(, ),
new Point(12, 34),
new Point(56, 78),
});
Polygon.UpdateData(db, polygon); // 更新对象
}
LiteDB数据库分析工具
可以通过LiteDB官方网站提供的LiteDB Studio工具查看LiteDB数据库的内容。
https://github.com/mbdavid/LiteDB.Studio