Преглед изворни кода

增加:强大的MongoBson库.md

tanghai пре 7 година
родитељ
комит
85aa5ef6a0

+ 226 - 0
Book/3.2强大的MongoBson库.md

@@ -0,0 +1,226 @@
+# 强大的MongoBson库
+后端开发,统计了一下大概有这些场景需要用到序列化:
+1. 对象通过序列化反序列化clone
+2. 服务端数据库存储数据,二进制
+3. 分布式服务端,多进程间的消息,二进制
+4. 后端日志,文本格式
+5. 服务端的各种配置文件,文本格式
+
+C#序列化库有非常非常多了,protobuf,json等等。但是这些序列化库都无法应当所有场景,既要可读又要小。protobuf不支持复杂的对象结构(无法使用继承),做消息合适,做数据库存储和日志格式并不好用。json做日志格式合适,但是做网络消息和数据存储就太大。我们当然希望一个库能满足上面所有场景,理由如下:
+1. 你想想某天你的配置文件需要放到数据库中保存,你不需要进行格式转换,后端直接把前端发过来的配置消息保存到数据库中,这是不是能减少非常多错误呢?
+2. 某天有些服务端的配置文件不用文件格式了,需要放在数据库中,同样,只需要几行代码就可以完成迁移。
+3. 某天后端服务器crash,你需要扫描日志进行数据恢复,把日志进行反序列化成C#对象,一条条进行处理,再转成对象保存到数据库就完成了。
+4. 对象保存在数据库,直接就可以看到文本内容,可以做各种类sql的操作
+5. 想像一个场景,一个配置文本对象,反序列化到内存,通过网络消息发送,存储到数据库中。整个过程一气呵成。
+
+简单来说就是减少各种数据转换,减少代码,提高开发效率,提高可维护性。当然,Mongo Bson就能够满足。MongoDB库既可以序列化成文本也可以序列化成BSON的二进制格式,并且MongoDB本身就是一个游戏中使用非常多的数据库。Mongo Bson非常完善,是我见过功能最全使用最强大的序列化库,有些功能十分贴心。其支持功能如下:
+1. 支持复杂的继承结构
+2. 支持忽略某些字段序列化
+3. 支持字段默认值
+4. 结构多出多余的字段照样可以反序列化,这对多版本协议非常有用
+5. 支持ISupportInitialize接口使用,这个在反序列化的时候简直就是神器
+6. 支持文本json和二进制bson序列化
+7. MongoDB数据库支持
+
+简单的介绍下mongo bson库
+### 1.支持序列化反序列化成json或者bson
+```csharp
+    public sealed class Player
+    {
+        public long Id;
+
+        public string Account { get; private set; }
+
+        public long UnitId { get; set; }
+    }
+
+    Player player1 = new Player() { Id = 1 };
+    string json = player1.ToJson();
+    Console.WriteLine($"player1 to json: {json}");
+    Console.WriteLine($"player to bson: {player.ToBson().ToHex()}");
+    // output:
+    // player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
+    // player to bson: B000000125F69640001000000000000000A4163636F756E740012556E6974496400000000000000000000
+
+
+```
+注意mongo的json跟标准的json有点区别,如果想用标准的json,可以传入一个JsonWriterSettings对象,限制使用JsonOutputMode.Strict模式
+```csharp
+    // 使用标准json
+    Player player2 = new Player() { Id = 1 };
+    Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
+    // player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
+```
+
+反序列化json:
+```csharp
+            // 反序列化json
+        Player player11 = BsonSerializer.Deserialize<Player>(json);
+        Console.WriteLine($"player11 to json: {player11.ToJson()}");
+```
+反序列化bson:
+```csharp
+    // 反序列化bson
+    using (MemoryStream memoryStream = new MemoryStream(bson))
+    {
+        Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
+        Console.WriteLine($"player12 to json: {player12.ToJson()}");
+    }
+```
+
+### 2.可以忽略某些字段
+[BsonIgnore]该标签用来禁止字段序列化。
+```csharp
+	public sealed class Player
+	{
+        public long Id;
+
+		[BsonIgnore]
+		public string Account { get; private set; }
+		
+		public long UnitId { get; set; }
+    }
+
+    Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
+	Console.WriteLine($"player to json: {player.ToJson()}");
+    // player to json: { "_id" : 2, "UnitId" : 3 }
+```
+### 3.支持默认值以及取别名
+[BsonElement] 字段加上该标签,即使是private字段也会序列化(默认只序列化public字段),该标签还可以带一个string参数,给字段序列化指定别名。
+```csharp
+	public sealed class Player
+	{
+        public long Id;
+
+		public string Account { get; private set; }
+
+		[BsonElement("UId")]
+		public long UnitId { get; set; }
+    }
+    Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
+	Console.WriteLine($"player to json: {player.ToJson()}");
+    // player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }
+```
+### 4.升级版本支持
+[BsonIgnoreExtraElements] 该标签用在class上面,反序列化时用来忽略多余的字段,一般版本兼容需要考虑,低版本的协议需要能够反
+序列化高版本的内容,否则新版本加了字段,旧版本结构反序列化会出错
+```csharp
+	[BsonIgnoreExtraElements]
+	public sealed class Player
+	{
+        public long Id;
+
+		public string Account { get; private set; }
+
+		[BsonElement("UId")]
+		public long UnitId { get; set; }
+    }
+```
+### 5.支持复杂的继承结构
+mongo bson库强大的地方在于完全支持序列化反序列化继承结构。需要注意的是,继承反序列化需要注册所有的父类,有两种方法:
+a. 你可以在父类上面使用[BsonKnownTypes]标签声明继承的子类,这样mongo会自动注册,例如:
+```csharp
+    [BsonKnownTypes(typeof(Entity))]
+    public class Component
+    {
+    }
+    [BsonKnownTypes(typeof(Player))]
+    public class Entity: Component
+    {
+    }
+    public sealed class Player: Entity
+    {
+        public long Id;
+        
+        public string Account { get; set; }
+		
+        public long UnitId { get; set; }
+    }
+```
+这样有缺陷,因为框架并不知道一个类会有哪些子类,这样做对框架代码有侵入性,我们希望能解除这个耦合
+。可以扫描程序集中所有子类父类的类型,将他们注册到mongo驱动中
+```csharp
+			Type[] types = typeof(Game).Assembly.GetTypes();
+			foreach (Type type in types)
+			{
+				if (!type.IsSubclassOf(typeof(Component)))
+				{
+					continue;
+				}
+
+				BsonClassMap.LookupClassMap(type);
+			}
+
+			BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));
+```
+这样完全的自动化注册,使用者也不需要关系类是否注册。
+
+### 6.ISupportInitialize接口
+mongo bson反序列化时支持一个ISupportInitialize接口,ISupportInitialize有两个方法
+```csharp
+    public interface ISupportInitialize
+    {
+        void BeginInit();
+        void EndInit();
+    }
+```
+BeginInit在反序列化前调用,EndInit在反序列化后调用。这个接口非常有用了,可以在反序列化后执行一些操作。例如
+```csharp
+	[BsonIgnoreExtraElements]
+	public class InnerConfig: AConfigComponent
+	{
+		[BsonIgnore]
+		public IPEndPoint IPEndPoint { get; private set; }
+		
+		public string Address { get; set; }
+
+		public override void EndInit()
+		{
+			this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address);
+		}
+	}
+```
+InnerConfig是ET中进程内网地址的配置,由于IPEndPoint不太好配置,我们可以配置成string形式,然后反序列化的时候在EndInit中把string转换成IPEndPoint。
+同样我给protobuf反序列化方法也加上了这个调用,参考ProtobufHelper.cs,ET的protobuf因为要支持ilruntime,所以去掉了map的支持,假如我们想要一个map怎么办呢?这里我给生成的代码都做了手脚,把proto消息都改成了partial class,这样我们可以自己扩展这个class,比如:
+```csharp
+message UnitInfo
+{
+	int64 UnitId  = 1;
+
+	float X = 2;
+	float Y = 3;
+	float Z = 4;
+}
+
+// protobuf
+message G2C_EnterMap // IResponse
+{
+	int32 RpcId = 90;
+	int32 Error = 91;
+	string Message = 92;
+	// 自己的unit id
+	int64 UnitId = 1;
+	// 所有的unit
+	repeated UnitInfo Units = 2;
+}
+```
+这个网络消息有个repeated UnitInfo字段,在protobuf中其实是个数组,使用起来不是很方便,我希望转成一个Dictionary<Int64, UnitInfo>的字段,我们可以做这样的操作:
+```csharp
+    public partial class G2C_EnterMap: ISupportInitialize
+    {
+        public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>();
+        
+        public void BeginInit()
+        {
+        }
+
+        public void EndInit()
+        {
+            foreach (var unit in this.Units)
+            {
+                this.unitsDict.Add(unit.UnitId, unit);
+            }
+        }
+    }
+```
+通过这样一段代码把消息进行扩展一下,反序列化出来之后,自动转成了一个Dictionary。

+ 15 - 0
Book/Example/Example2_1/Example2_1.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp2.2</TargetFramework>
+        <RootNamespace>Example1._1</RootNamespace>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <Compile Include="..\..\..\Unity\Assets\Model\Base\OneThreadSynchronizationContext.cs">
+        <Link>OneThreadSynchronizationContext.cs</Link>
+      </Compile>
+    </ItemGroup>
+
+</Project>

+ 65 - 0
Book/Example/Example2_1/Program.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Threading;
+using ETModel;
+
+namespace Example2_1
+{
+    internal class Program
+    {
+        private static int loopCount = 0;
+
+        private static void Main()
+        {
+            OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
+            
+            WaitTimeAsync(5000, WaitTimeFinishCallback);
+            
+            while (true)
+            {
+                OneThreadSynchronizationContext.Instance.Update();
+                
+                Thread.Sleep(1);
+                
+                ++loopCount;
+                if (loopCount % 10000 == 0)
+                {
+                    Console.WriteLine($"loop count: {loopCount}");
+                }
+            }
+        }
+
+        private static void WaitTimeAsync(int waitTime, Action action)
+        {
+            Thread thread = new Thread(()=>WaitTime(waitTime, action));
+            thread.Start();
+        }
+        
+        private static void WaitTimeFinishCallback()
+        {
+            Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            WaitTimeAsync(4000, WaitTimeFinishCallback3);
+        }
+        
+        private static void WaitTimeFinishCallback3()
+        {
+            Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            WaitTimeAsync(3000, WaitTimeFinishCallback2);
+        }
+        
+        private static void WaitTimeFinishCallback2()
+        {
+            Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}");
+        }
+
+        /// <summary>
+        /// 在另外的线程等待
+        /// </summary>
+        private static void WaitTime(int waitTime, Action action)
+        {
+            Thread.Sleep(waitTime);
+            
+            // 将action扔回主线程执行
+            OneThreadSynchronizationContext.Instance.Post(o=>action(), null);
+        }
+    }
+}

+ 14 - 0
Book/Example/Example2_2/Example2_2.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp2.2</TargetFramework>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <Compile Include="..\..\..\Unity\Assets\Model\Base\OneThreadSynchronizationContext.cs">
+            <Link>OneThreadSynchronizationContext.cs</Link>
+        </Compile>
+    </ItemGroup>
+
+</Project>

+ 63 - 0
Book/Example/Example2_2/Program.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using ETModel;
+
+namespace Example2_2
+{
+    class Program
+    {
+        private static int loopCount = 0;
+        
+        static void Main(string[] args)
+        {
+            OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
+
+            Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
+            
+            Crontine();
+            
+            while (true)
+            {
+                OneThreadSynchronizationContext.Instance.Update();
+                
+                Thread.Sleep(1);
+                
+                ++loopCount;
+                if (loopCount % 10000 == 0)
+                {
+                    Console.WriteLine($"loop count: {loopCount}");
+                }
+            }
+        }
+
+        private static async void Crontine()
+        {
+            await WaitTimeAsync(5000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            await WaitTimeAsync(4000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            await WaitTimeAsync(3000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+        }
+        
+        private static Task WaitTimeAsync(int waitTime)
+        {
+            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+            Thread thread = new Thread(()=>WaitTime(waitTime, tcs));
+            thread.Start();
+            return tcs.Task;
+        }
+        
+        /// <summary>
+        /// 在另外的线程等待
+        /// </summary>
+        private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
+        {
+            Thread.Sleep(waitTime);
+            
+            // 将tcs扔回主线程执行
+            OneThreadSynchronizationContext.Instance.Post(o=>tcs.SetResult(true), null);
+        }
+    }
+}

+ 14 - 0
Book/Example/Example2_2_2/Example2_2_2.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp2.2</TargetFramework>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <Compile Include="..\..\..\Unity\Assets\Model\Base\OneThreadSynchronizationContext.cs">
+            <Link>OneThreadSynchronizationContext.cs</Link>
+        </Compile>
+    </ItemGroup>
+
+</Project>

+ 62 - 0
Book/Example/Example2_2_2/Program.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using ETModel;
+
+namespace Example2_2_2
+{
+    class Program
+    {
+        private static int loopCount = 0;
+        
+        static void Main(string[] args)
+        {
+            SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);
+
+            Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
+            
+            Crontine();
+            
+            while (true)
+            {
+                OneThreadSynchronizationContext.Instance.Update();
+                
+                Thread.Sleep(1);
+                
+                ++loopCount;
+                if (loopCount % 10000 == 0)
+                {
+                    Console.WriteLine($"loop count: {loopCount}");
+                }
+            }
+        }
+
+        private static async void Crontine()
+        {
+            await WaitTimeAsync(5000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            await WaitTimeAsync(4000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            await WaitTimeAsync(3000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+        }
+        
+        private static Task WaitTimeAsync(int waitTime)
+        {
+            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+            Thread thread = new Thread(()=>WaitTime(waitTime, tcs));
+            thread.Start();
+            return tcs.Task;
+        }
+        
+        /// <summary>
+        /// 在另外的线程等待
+        /// </summary>
+        private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
+        {
+            Thread.Sleep(waitTime);
+            Console.WriteLine($"SetResult线程: {Thread.CurrentThread.ManagedThreadId}");
+            tcs.SetResult(true);
+        }
+    }
+}

+ 8 - 0
Book/Example/Example2_3/Example2_3.csproj

@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp2.2</TargetFramework>
+    </PropertyGroup>
+
+</Project>

+ 79 - 0
Book/Example/Example2_3/Program.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Example2_3
+{
+    
+    class Program
+    {
+        private static int loopCount = 0;
+
+        private static long time;
+        private static Action action;
+        
+        static void Main(string[] args)
+        {
+            Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
+
+            Crontine();
+            
+            while (true)
+            {
+                Thread.Sleep(1);
+
+                CheckTimerOut();
+                
+                ++loopCount;
+                if (loopCount % 10000 == 0)
+                {
+                    Console.WriteLine($"loop count: {loopCount}");
+                }
+            }
+        }
+        
+        private static void Crontine()
+        {
+            WaitTimeAsync(5000, WaitTimeAsyncCallback1);
+        }
+
+        private static void WaitTimeAsyncCallback1()
+        {
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            WaitTimeAsync(4000, WaitTimeAsyncCallback2);
+        }
+        
+        private static void WaitTimeAsyncCallback2()
+        {
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            WaitTimeAsync(3000, WaitTimeAsyncCallback3);
+        }
+        
+        private static void WaitTimeAsyncCallback3()
+        {
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+        }
+
+        private static void CheckTimerOut()
+        {
+            if (time == 0)
+            {
+                return;
+            }
+            long nowTicks = DateTime.Now.Ticks / 10000;
+            if (time > nowTicks)
+            {
+                return;
+            }
+
+            time = 0;
+            action.Invoke();
+        }
+        
+        private static void WaitTimeAsync(int waitTime, Action a)
+        {
+            time = DateTime.Now.Ticks / 10000 + waitTime;
+            action = a;
+        }
+    }
+}

+ 8 - 0
Book/Example/Example2_3_2/Example2_3_2.csproj

@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp2.2</TargetFramework>
+    </PropertyGroup>
+
+</Project>

+ 68 - 0
Book/Example/Example2_3_2/Program.cs

@@ -0,0 +1,68 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Example2_3_2
+{
+    class Program
+    {
+        private static int loopCount = 0;
+
+        private static long time;
+        private static TaskCompletionSource<bool> tcs;
+        
+        static void Main(string[] args)
+        {
+            Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
+
+            Crontine();
+            
+            while (true)
+            {
+                Thread.Sleep(1);
+
+                CheckTimerOut();
+                
+                ++loopCount;
+                if (loopCount % 10000 == 0)
+                {
+                    Console.WriteLine($"loop count: {loopCount}");
+                }
+            }
+        }
+        
+        private static async void Crontine()
+        {
+            await WaitTimeAsync(5000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            await WaitTimeAsync(4000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+            await WaitTimeAsync(3000);
+            Console.WriteLine($"当前线程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
+        }
+
+        private static void CheckTimerOut()
+        {
+            if (time == 0)
+            {
+                return;
+            }
+            long nowTicks = DateTime.Now.Ticks / 10000;
+            if (time > nowTicks)
+            {
+                return;
+            }
+
+            time = 0;
+            tcs.SetResult(true);
+        }
+        
+        private static Task WaitTimeAsync(int waitTime)
+        {
+            TaskCompletionSource<bool> t = new TaskCompletionSource<bool>();
+            time = DateTime.Now.Ticks / 10000 + waitTime;
+            tcs = t;
+            return t.Task;
+        }
+    }
+}

+ 13 - 0
Book/Example/Example3_5/Example3_5.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp2.2</TargetFramework>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\..\..\Server\Model\Server.Model.csproj" />
+      <ProjectReference Include="..\..\..\Server\ThirdParty\MongoDBDriver\MongoDB.Bson\MongoDB.Bson.csproj" />
+    </ItemGroup>
+
+</Project>

+ 38 - 0
Book/Example/Example3_5/Program.cs

@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+using ETModel;
+using MongoDB.Bson;
+using MongoDB.Bson.IO;
+using MongoDB.Bson.Serialization;
+
+namespace Example3_5
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Player player1 = new Player() { Id = 1 };
+            string json = player1.ToJson();
+            Console.WriteLine($"player1 to json: {json}");
+
+            byte[] bson = player1.ToBson();
+            Console.WriteLine($"player1 to bson: {bson.ToHex()}");
+            // // player1 to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
+
+            // 反序列化json
+            Player player11 = BsonSerializer.Deserialize<Player>(json);
+            Console.WriteLine($"player11 to json: {player11.ToJson()}");
+            // 反序列化bson
+            using (MemoryStream memoryStream = new MemoryStream(bson))
+            {
+                Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
+                Console.WriteLine($"player12 to json: {player12.ToJson()}");
+            }
+            
+            
+            // 使用标准json
+            Console.WriteLine($"player to Strict json: {player1.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
+            // player1 to Strict json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
+        }
+    }
+}

+ 92 - 0
Client-Server.sln

@@ -43,6 +43,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver.Core", "Serv
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver.GridFS", "Server\ThirdParty\MongoDBDriver\MongoDB.Driver.GridFS\MongoDB.Driver.GridFS.csproj", "{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Book", "Book", "{BB93F5A1-C87D-4C69-8C9D-45338C186E01}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2_1", "Book\Example\Example2_1\Example2_1.csproj", "{ED41FEC0-492D-4708-AE43-131B8E2463BD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2_2", "Book\Example\Example2_2\Example2_2.csproj", "{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2_2_2", "Book\Example\Example2_2_2\Example2_2_2.csproj", "{472BEADB-963F-4D55-BC64-C20643A6D4DC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2_3", "Book\Example\Example2_3\Example2_3.csproj", "{893271BA-8471-4E60-B1A8-11F5D0C67191}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2_3_2", "Book\Example\Example2_3_2\Example2_3_2.csproj", "{A4D185F9-C446-47D5-9250-A741B996E349}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example3_5", "Book\Example\Example3_5\Example3_5.csproj", "{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -233,6 +247,78 @@ Global
 		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x64.Build.0 = Release|Any CPU
 		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x86.ActiveCfg = Release|Any CPU
 		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x86.Build.0 = Release|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Debug|x64.Build.0 = Debug|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Debug|x86.Build.0 = Debug|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Release|Any CPU.Build.0 = Release|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Release|x64.ActiveCfg = Release|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Release|x64.Build.0 = Release|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Release|x86.ActiveCfg = Release|Any CPU
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD}.Release|x86.Build.0 = Release|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Debug|x64.Build.0 = Debug|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Debug|x86.Build.0 = Debug|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Release|x64.ActiveCfg = Release|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Release|x64.Build.0 = Release|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Release|x86.ActiveCfg = Release|Any CPU
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2}.Release|x86.Build.0 = Release|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Debug|x64.Build.0 = Debug|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Debug|x86.Build.0 = Debug|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Release|x64.ActiveCfg = Release|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Release|x64.Build.0 = Release|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Release|x86.ActiveCfg = Release|Any CPU
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC}.Release|x86.Build.0 = Release|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Debug|x64.Build.0 = Debug|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Debug|x86.Build.0 = Debug|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Release|Any CPU.Build.0 = Release|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Release|x64.ActiveCfg = Release|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Release|x64.Build.0 = Release|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Release|x86.ActiveCfg = Release|Any CPU
+		{893271BA-8471-4E60-B1A8-11F5D0C67191}.Release|x86.Build.0 = Release|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Debug|x64.Build.0 = Debug|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Debug|x86.Build.0 = Debug|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Release|x64.ActiveCfg = Release|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Release|x64.Build.0 = Release|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Release|x86.ActiveCfg = Release|Any CPU
+		{A4D185F9-C446-47D5-9250-A741B996E349}.Release|x86.Build.0 = Release|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Debug|x64.Build.0 = Debug|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Debug|x86.Build.0 = Debug|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Release|x64.ActiveCfg = Release|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Release|x64.Build.0 = Release|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Release|x86.ActiveCfg = Release|Any CPU
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -255,6 +341,12 @@ Global
 		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
 		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
 		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
+		{ED41FEC0-492D-4708-AE43-131B8E2463BD} = {BB93F5A1-C87D-4C69-8C9D-45338C186E01}
+		{5BB1E6A2-A2AD-4F58-88A2-11C366464BF2} = {BB93F5A1-C87D-4C69-8C9D-45338C186E01}
+		{472BEADB-963F-4D55-BC64-C20643A6D4DC} = {BB93F5A1-C87D-4C69-8C9D-45338C186E01}
+		{893271BA-8471-4E60-B1A8-11F5D0C67191} = {BB93F5A1-C87D-4C69-8C9D-45338C186E01}
+		{A4D185F9-C446-47D5-9250-A741B996E349} = {BB93F5A1-C87D-4C69-8C9D-45338C186E01}
+		{65474CD4-F380-4F14-AC5A-F274E9D1BD8B} = {BB93F5A1-C87D-4C69-8C9D-45338C186E01}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {EABC01E3-3EB5-47EF-B46E-AAD8BB3585F1}

+ 7 - 1
Unity/ProjectSettings/ProjectSettings.asset

@@ -106,6 +106,11 @@ PlayerSettings:
   xboxOneDisableEsram: 0
   xboxOnePresentImmediateThreshold: 0
   switchQueueCommandMemory: 0
+  switchQueueControlMemory: 16384
+  switchQueueComputeMemory: 262144
+  switchNVNShaderPoolsGranularity: 33554432
+  switchNVNDefaultPoolsGranularity: 16777216
+  switchNVNOtherPoolsGranularity: 16777216
   vulkanEnableSetSRGBWrite: 0
   m_SupportedAspectRatios:
     4:3: 1
@@ -556,6 +561,7 @@ PlayerSettings:
   switchAllowsRuntimeAddOnContentInstall: 0
   switchDataLossConfirmation: 0
   switchUserAccountLockEnabled: 0
+  switchSystemResourceMemory: 16777216
   switchSupportedNpadStyles: 3
   switchNativeFsCacheSize: 32
   switchIsHoldTypeHorizontal: 0
@@ -657,7 +663,7 @@ PlayerSettings:
   webGLLinkerTarget: 1
   webGLThreadsSupport: 0
   scriptingDefineSymbols:
-    1: NET452;ILRuntime
+    1: NET452
     4: NET452;ILRuntime
     7: NET452;ILRuntime
     13: NET452;ILRuntime