MongoDB初始化

首先在SimpleServer解决方案里添加新的类库,命名为MongoDB。

打开Nuget包管理器,搜索“MongoDB”,给MongoDB项目添加“MongoDB.Bson”、“MongoDB.Driver.Core”和“MongoDB.Driver”,然后再引用ServerBase项目。

给SimpleServer项目添加MongoDB项目的引用。

在MongoDB项目添加MongoDBMgr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using MongoDB.Driver;

namespace MongoDB
{
public class MongoDBMgr : SingletonPattern<MongoDBMgr>
{
private const string m_DBConnect = "mongodb://127.0.0.1:27017";//在服务器中也是本地连接,但如果是购买的阿里云的云数据库MongoDB,需要按照提示填写
private const string m_DBName = "MongoDBLearn";//数据库名

protected static IMongoClient? m_Client;//定义客户端
protected static IMongoDatabase? m_Database;//定义接口
public void Init()
{
var mongoUrl = new MongoUrlBuilder(m_DBConnect);
//创建并实例化客户端
m_Client = new MongoClient(mongoUrl.ToMongoUrl());
m_Database = m_Client.GetDatabase(m_DBName);
}
}
}

初始化分三步:创建数据库链接,从链接创建客户端,从客户端获取数据库。

在SimpleServer项目的Program.cs里面初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using SimpleServer.Net;
using MongoDB;
using MySql;

namespace SimpleServer
{
internal class Program
{
static void Main(string[] args)
{
MySqlMgr.Instance.Init();
MongoDBMgr.Instance.Init();//+++
ServerSocket.Instance.Init();
Console.ReadLine();
}
}
}

实体类

和使用SqlSugar一样,要记录进数据库就需要对应的实体类。

在MongoDB项目内新建MongoDBData文件夹。在其中新建PlayerAction.cs文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace MongoDB.MongoDBData
{
public class PlayerAction
{
public ObjectId _id { get; set; }//第一个变量必须这么写
public string? Key { get;set; }//玩家行为类型
public string? Data { get;set; }//行为的具体内容
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime? Time { get; set; }//行为时间
}
}

ObjectId的构成:

之前我们使用MySQL等关系型数据库时,主键都是设置成自增的。但在MongoDB这种分布式数据库环境下,自增主键就不可行了,会产生冲突。为此,MongoDB采用了一个称之为ObjectId的类型来做主键。ObjectId是一个12字节的 BSON 类型字符串。按照字节顺序,一次代表:

4字节:UNIX时间戳
3字节:表示运行MongoDB的机器
2字节:表示生成此_id的进程
3字节:由一个随机数开始的计数器生成的值

从ObjectId的构造上来看,内部就嵌入了时间类型。我们肯定可以从中获取时间信息:即插入此文档时的时间。MongoDB对ObjectId对象提供了getTimestamp()方法来获取ObjectId的时间。

增加玩家行为记录(使用BsonDoc)

MongoDBMgr中添加AddAction添加玩家行为的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// <summary>
/// 添加行为
/// </summary>
/// <param name="userId">玩家ID</param>
/// <param name="name">玩家名字</param>
/// <param name="key">行为类型</param>
/// <param name="data">行为数据</param>
/// <returns>是否成功添加</returns>
public bool AddAction(int userId,string name,string key,string data)
{
try
{
//表名,一个玩家一个表
string collectionName = "UserAction_" + userId + "_" + name;
//获取表
var collection = m_Database?.GetCollection<BsonDocument>(collectionName);
//编写数据
var document = new BsonDocument
{
{ "Key", key } ,
{ "Data", data },
{ "Time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff") }
};
//插入进表
collection?.InsertOne(document);
return true;
}
catch (Exception ex)
{
Debug.LogError("玩家行为写入错误:" + ex.ToString());
return false;
}
}

注意:

添加玩家行为是不断递增的表,在插入时不需要判断有没有记录。

玩家的记录有限,到达一定数据量(10000条)时会删除旧的。

这里我们使用BsonDocument来添加记录,BsonDocument可以直接添加记录,不需要实体类。

查找玩家行为记录

MongoDB提供了流式(fluent)查找的API。IMongoCollection<T>.Find<T>

MongoDBMgr中添加FindAction方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
/// 查询行为
/// </summary>
/// <param name="userId">玩家ID</param>
/// <param name="name">玩家名字</param>
public void FindAction(int userId, string name)
{
try
{
string collectionName = "UserAction_" + userId + "_" + name;
var collection = m_Database?.GetCollection<BsonDocument>(collectionName);
var dataList = collection.Find(new BsonDocument()).ToList();//查找
foreach (var data in dataList)
{
Debug.Log(data.ToString());
}
}
catch (Exception ex)
{
Debug.LogError("玩家行为查询错误:" + ex);
}
}

增加玩家行为记录(使用泛型)

MongoDBMgr中添加Add泛型方法,主要使用InsertOne,然后再在AddAction调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
      /// <summary>
/// 添加行为
/// </summary>
/// <param name="userId">玩家ID</param>
/// <param name="name">玩家名字</param>
/// <param name="key">行为类型</param>
/// <param name="data">行为数据</param>
/// <returns>是否成功添加</returns>
public bool AddAction(int userId,string name,string key,string data)
{
try
{
string collectionName = "UserAction_" + userId + "_" + name;
PlayerAction playerAction = new PlayerAction();
playerAction.Key = key;
playerAction.Data = data;
playerAction.Time = DateTime.Now;
return Add(playerAction, collectionName);//+++
}
catch (Exception ex)
{
Debug.LogError("玩家行为写入错误:" + ex.ToString());
return false;
}
}
/// <summary>
/// 同步添加一条数据
/// </summary>
/// <typeparam name="T">实体类</typeparam>
/// <param name="entity">实体</param>
/// <param name="collName">表名</param>
/// <returns>是否成功添加</returns>
public bool Add<T>(T entity,string collName) where T : class,new()
{
try
{
var collection = m_Database?.GetCollection<T>(collName);
if (collection != null)
{
collection.InsertOne(entity);
return true;
}
return false;
}
catch (Exception ex)
{
Debug.LogError("MongoDB添加数据错误:" + ex );
return false;
}
}

当然,不使用包装好的泛型方法也可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
/// 添加行为
/// </summary>
/// <param name="userId">玩家ID</param>
/// <param name="name">玩家名字</param>
/// <param name="key">行为类型</param>
/// <param name="data">行为数据</param>
/// <returns>是否成功添加</returns>
public bool AddAction(int userId,string name,string key,string data)
{
try
{
string collectionName = "UserAction_" + userId + "_" + name;
var collection = m_Database?.GetCollection<PlayerAction>(collectionName);
PlayerAction playerAction = new PlayerAction();
playerAction.Key = key;
playerAction.Data = data;
playerAction.Time = DateTime.Now;
collection?.InsertOne(playerAction);
return true;
}
catch (Exception ex)
{
Debug.LogError("玩家行为写入错误:" + ex.ToString());
return false;
}
}

增加玩家行为记录(异步方法)

MongoDB也提供了异步增加数据的APIInsertOneAsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// <summary>
/// 异步添加一条数据
/// </summary>
/// <typeparam name="T">实体类</typeparam>
/// <param name="entity">实体</param>
/// <param name="collName">表名</param>
/// <returns>是否成功添加</returns>
public async Task<bool> AddAsync<T>(T entity, string collName) where T : class, new()
{
try
{
var collection = m_Database?.GetCollection<T>(collName);
if (collection != null)
{
await collection.InsertOneAsync(entity);
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
Debug.LogError("MongoDB异步添加数据错误:" + ex);
return false;
}
}

增加多条记录(使用泛型)

MongoDB提供了批量增加数据的APIInsertMany

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <summary>
/// 同步批量添加数据
/// </summary>
/// <typeparam name="T">实体类</typeparam>
/// <param name="entities">实体集合</param>
/// <param name="collName">表名</param>
/// <returns>是否成功添加</returns>
public bool InsertMany<T>(List<T> entities, string collName) where T : class, new()
{
try
{
var collection = m_Database?.GetCollection<T>(collName);
if (collection != null)
{
collection.InsertMany(entities);
return true;
}
return false;
}
catch (Exception ex)
{
Debug.LogError("MongoDB批量添加数据错误:" + ex);
return false;
}
}

增加多条记录(异步方法)

MongoDB提供了异步批量增加数据的APIInsertManyAsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// <summary>
/// 异步批量添加数据
/// </summary>
/// <typeparam name="T">实体类</typeparam>
/// <param name="entities">实体集合</param>
/// <param name="collName">表名</param>
/// <returns>是否添加成功</returns>
public async Task<bool> InsertManyAsync<T>(List<T> entities, string collName) where T : class, new()
{
try
{
var collection = m_Database?.GetCollection<T>(collName);
if (collection != null)
{
await collection.InsertManyAsync(entities);
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
Debug.LogError("MongoDB异步批量添加数据错误:" + ex);
return false;
}
}

注意,所有添加记录操作能成功的前提是MongoDB中有这个玩家的表。而MongoDB可以隐式增加表,也就是说如果没有这个表会按照表名自动创建。