千家信息网

使用mongodb的三个大坑是否踩过

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章给大家介绍使用mongodb的三个大坑是否踩过,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一:背景1. 讲故事前段时间有位朋友在微信群问,在向 mongodb 中插入的
千家信息网最后更新 2026年11月02日使用mongodb的三个大坑是否踩过

这篇文章给大家介绍使用mongodb的三个大坑是否踩过,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

一:背景

1. 讲故事

前段时间有位朋友在微信群问,在向 mongodb 中插入的时间为啥取出来的时候少了 8 个小时,8 在时间处理上是一个非常敏感的数字,又吉利又是一个普适的话题,后来我想想初次使用 mongodb 的朋友一定还会遇到各种新坑,比如说: 插入的数据取不出来,看不爽的 ObjectID,时区不对等等,这篇就和大家一起聊一聊。

二: 1号坑 插进去的数据取不出来

1. 案例展示

这个问题是使用强类型操作 mongodb 你一定会遇到的问题,案例代码如下:

class Program    {        static void Main(string[] args)        {            var client = new MongoClient("mongodb://192.168.1.128:27017");            var database = client.GetDatabase("school");            var table = database.GetCollection("student");            table.InsertOne(new Student() { StudentName = "hxc", Created = DateTime.Now });            var query = table.AsQueryable().ToList();        }    }    public class Student    {        public string StudentName { get; set; }        public DateTime Created { get; set; }    }

我去,这么简单的一个操作还报错,要初学到放弃吗? 挺急的,在线等!

2. 堆栈中深挖源码

作为一个码农还得有钻研代码的能力,从错误信息中看说有一个 _id 不匹配 student 中的任何一个字段,然后把全部堆栈找出来。

System.FormatException  HResult=0x80131537  Message=Element '_id' does not match any field or property of class Newtonsoft.Test.Student.  Source=MongoDB.Driver  StackTrace:   at MongoDB.Driver.Linq.MongoQueryProviderImpl`1.Execute(Expression expression)   at MongoDB.Driver.Linq.MongoQueryableImpl`2.GetEnumerator()   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)   at Newtonsoft.Test.Program.Main(String[] args) in E:\crm\JsonNet\Newtonsoft.Test\Program.cs:line 32

接下来就用 dnspy 去定位一下 MongoQueryProviderImpl.Execute 到底干的啥,截图如下:

我去,这代码硬核哈,用了 LambdaExpression 表达式树,我们知道表达式树用于将一个领域的查询结构转换为另一个领域的查询结构,但要寻找如何构建这个方法体就比较耗时间了,接下来还是用 dnspy 去调试看看有没有更深层次的堆栈。

这个堆栈信息就非常清楚了,原来是在 MongoDB.Bson.Serialization.BsonClassMapSerializer.DeserializeClass 方法中出了问题,接下来找到问题代码,简化如下:

public TClass DeserializeClass(BsonDeserializationContext context){        while (reader.ReadBsonType() != BsonType.EndOfDocument)        {                TrieNameDecoder trieNameDecoder = new TrieNameDecoder(elementTrie);                string text = reader.ReadName(trieNameDecoder);                if (trieNameDecoder.Found)                {                        int value = trieNameDecoder.Value;                        BsonMemberMap bsonMemberMap = allMemberMaps[value];                }                else                {                        if (!this._classMap.IgnoreExtraElements)                        {                                throw new FormatException(string.Format("Element '{0}' does not match any field or property of class {1}.", text, this._classMap.ClassType.FullName));                        }                        reader.SkipValue();                }        }}

上面的代码逻辑非常清楚,要么 student 中存在 _id 字段,也就是 trieNameDecoder.Found, 要么使用 忽略未知的元素,也就是 this._classMap.IgnoreExtraElements,添加字段容易,接下来看看怎么让 IgnoreExtraElements = true,找了一圈源码,发现这里是关键:

也就是: foreach (IBsonClassMapAttribute bsonClassMapAttribute in classMap.ClassType.GetTypeInfo().GetCustomAttributes(false).OfType())这句话,这里的 classMap 就是 student,只有让 foreach 得以执行才能有望 classMap.IgnoreExtraElements 赋值为 true ,接下来找找看在类上有没有类似 IgnoreExtraElements 的 Attribute,嘿嘿,还真有一个类似的: BsonIgnoreExtraElements ,如下代码:

[BsonIgnoreExtraElements]    public class Student    {        public string StudentName { get; set; }        public DateTime Created { get; set; }    }

接下来执行一下代码,可以看到问题搞定:

如果你想验证的话,可以继续用 dnspy 去验证一下源码哈,如下代码所示:

接下来还有一种办法就是增加 _id 字段,如果你不知道用什么类型接,那就用object就好啦,后续再改成真正的类型。

三: 2号坑 DateTime 时区不对

如果你细心的话,你会发现刚才案例中的 Created 时间是 2020/8/16 4:24:57, 大家请放心,我不会傻到凌晨4点还在写代码,好了哈,看看到底问题在哪吧, 可以先看看 mongodb 中的记录数据,如下:

{    "_id" : ObjectId("5f38b83e0351908eedac60c9"),    "StudentName" : "hxc",    "Created" : ISODate("2020-08-16T04:38:22.587Z")}

从 ISODate 可以看出,这是格林威治时间,按照0时区存储,所以这个问题转成了如何在获取数据的时候,自动将 ISO 时间转成 Local 时间就可以了,如果你看过底层源码,你会发现在 mongodb 中每个实体的每个类型都有一个专门的 XXXSerializer,如下图:

接下来就好好研读一下里面的 Deserialize 方法即可,代码精简后如下:

public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args){        IBsonReader bsonReader = context.Reader;        BsonType currentBsonType = bsonReader.GetCurrentBsonType();        DateTime value;                switch (this._kind)        {                case DateTimeKind.Unspecified:                case DateTimeKind.Local:                        value = DateTime.SpecifyKind(BsonUtils.ToLocalTime(value), this._kind);                        break;                case DateTimeKind.Utc:                        value = BsonUtils.ToUniversalTime(value);                        break;        }        return value;}

可以看出,如果当前的 this._kind= DateTimeKind.Local 的话,就将 UTC 时间转成 Local 时间,如果你有上一个坑的经验,你大概就知道应该也是用特性注入的,

[BsonDateTimeOptions(Kind = DateTimeKind.Local)]    public DateTime Created { get; set; }

不信的话,我调试给你看看哈。

接下来再看看 this._kind 是怎么被赋的。

四: 3号坑 自定义ObjectID

在第一个坑中,不知道大家看没看到类似这样的语句: ObjectId("5f38b83e0351908eedac60c9") ,乍一看像是一个 GUID,当然肯定不是,这是mongodb自己组建了一个 number 组合的十六进制表示,姑且不说性能如何,反正看着不是很舒服,毕竟大家都习惯使用 int/long 类型展示的主键ID。

那接下来的问题是:如何改成我自定义的 number ID 呢? 当然可以,只要实现 IIdGenerator 接口即可,那主键ID的生成,我准备用 雪花算法,完整代码如下:

class Program    {        static void Main(string[] args)        {            var client = new MongoClient("mongodb://192.168.1.128:27017");            var database = client.GetDatabase("school");            var table = database.GetCollection("student");            table.InsertOne(new Student() { Created = DateTime.Now });            table.InsertOne(new Student() { Created = DateTime.Now });        }    }    class Student    {        [BsonId(IdGenerator = typeof(MyGenerator))]        public long ID { get; set; }        [BsonDateTimeOptions(Kind = DateTimeKind.Local)]        public DateTime Created { get; set; }    }    public class MyGenerator : IIdGenerator    {        private static readonly IdWorker worker = new IdWorker(1, 1);        public object GenerateId(object container, object document)        {            return worker.NextId();        }        public bool IsEmpty(object id)        {            return id == null || Convert.ToInt64(id) == 0;        }    }

然后去看一下 mongodb 生成的 json:

关于使用mongodb的三个大坑是否踩过就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

代码 接下来 时间 问题 类型 堆栈 字段 数据 源码 也就是 方法 时区 案例 三个 大坑 不对 清楚 信息 内容 就是 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技巨头监管 圆梦互联网科技 互联网核心科技是什么 互联网科技与金融科技 互联网科技金融 互联网 科技创新 上海挚品互联网科技 上海致骋互联网科技 互联网科技汛 甘肃拓世互联网科技有限公司 速维有品互联网信息科技(苏州)有限公司 链环科技传统企业怎么转型互联网 深圳育鹏互联网科技有限公司招聘 互联网科技数字货币 广州燕处互联网科技有限公司 济南小医互联网科技有限公司招聘 福州德业互联网科技 广西科技产业互联网是什么 保山浩联互联网科技有限公司 恒生科技互联网指数十大成分股 上海卓越科技互联网 互联网科技官网 互联网科技垄断 互联网络科技有限公司 互联网女皇 腾讯科技 齐家互联网科技有限公司 湖南诚度互联网科技有限公司 信承诺互联网科技有限公司 互联网的黑科技实验室 武汉亿视互联网科技有限公司
0