初识 MongoDB 和 .NET Core 入门


昨天搭建完毕 MongoDB 集群 后,开始计划了解 MongoDB ,并引入使用场景,这里介绍一下学习过程中的一些笔记,帮助读者快速了解 MongoDB 并使用 C# 对其进行编码。

浅入 MongoDB

MonogoDB 是什么

MongoDB 是 NoSQL 型数据库,主要特征是存储结构化数据,MongoDB 是基于分布式文件存储的开源数据库系统。

结构化数据

以往我们使用 Mysql、SqlServer 等数据库,数据都是一条条的。MongoDB 的结构化数据正是区别于这种列-行式的数据。

结构化数据具有层级关系:

例如:

{
     name: "MongoDB",type: "database",count: 1,info: {
         x: 203,y: 102
     }
}

结构化数据

MongoDB 与关系型数据库

由于 MongoDB 中,没有表、行、列,因此初学 MongoDB 时可能会有困扰,这里给出一些 MongoDB 与 普通SQL数据库对应的术语。

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 非关系型数据库,表与表之间没关系
primary key primary key 主键,MongoDB自动将_id字段设置为主键

资料来源:https://www.runoob.com/mongodb/mongodb-databases-documents-collections.html

MongoDB 入门命令

使用 mongo 进入 MongoDB shell 后,可使用命令(相当于SQL)执行操作。

注: MongoDB 中,有一个自动的 _id 字段,此字段 MongoDB 自动设置为主键并自动生成值。

显示所有数据库(包含系统数据库):

show dbs

当前正在操作的数据库或集合:

db

连接到指定数据库:

use {数据库名称}

显示所有集合:

show collections
# 或
show tables

查看集合中的所有文档:

# MyCollection 是集合名称
db.getCollection("MyCollection").find()
db.getCollection("MyCollection").find().limit(1000).skip(0)

可能你不信,笔者百度了很久,第一页没找到一篇合适的友好的 "mongoDB 查看集合中的所有文档",特别是 CSDN 的垃圾文真的多。建议别瞎折腾了,去下一个 Navicat Premium,操作的时候,底部会提示所用的命令。

使用工具查看MongoDB命令

另外 MongoDB 有很多实用工具:https://docs.mongodb.com/tools/

文档

MongoDB 中的文档(Document)即关系型数据库中的一条记录(row)、一行数据。

但, MongoDB 中,一个集合(Collection-Table)中,是不需要具有相同字段的。例如:

A 文档:

{
     name: "MongoDB",y: 102
     }
}

B 文档:

{
     name: "MongoDB",typeName: "database",dataCount: 1,dataInfo: {
         m: 203,n: 102
     }
}

.NET Core 示例

我们从一个基础模板开始。

创建一个控制台程序,打开 Nuget 搜索并安装 MongoDB.Driver

            var client = new MongoClient("mongodb://{MongoDB}:27017");
            IMongoDatabase database = client.GetDatabase("Test");

集合

可以通过 CreateCollection()CreateCollectionAsync() 创建一个集合,跟普通数据库不同的是,创建集合时是不需要指定结构的,只需要指定名称即可:

await database.CreateCollectionAsync("Test");

获取集合

GetCollection() 函数可以让我们获取到集合,如果集合不存在,则会自动创建。

IMongoCollection<TDocument> GetCollection<TDocument>()

由于同一个集合可以有不同字段和字段类型的文档,因此几个文档如果有所差别,是很难统一起来的,例如:

集合的文档字段不同

(N/A) 代表此文档没有这个字段;如果一个文档有 10 个字段,另一个文档有 8 个字段,但是两者的字段完全不同时,要合并起来来,就有 18 个字段了。很明显,不应该汇集在一起,而是应该使用强类型对其 ”归档“ 。

创建两个类,分别为 Test1,Test2,其内容如下:

    public class Test1
    {
        public string Name { get; set; }
    }

    public class Test2
    {
        public string DataType { get; set; }
    }

以两种文档类型获取集合:

            var collection1 = database.GetCollection<Test1>("Test");
            var collection2 = database.GetCollection<Test2>("Test");

这个获取集合的意思是,获取此集合中这类格式的文档的操作能力。

往集合中插入数据:

            collection1.InsertOne(new Test1 { Name = "Test1" });
            collection2.InsertOne(new Test2 { DataType = "Test2" });
			// await collection.InsertOneAsync(object);

启动,查看结果。

InsertMany() 可以插入批量数据:

            Test1[] datas = new Test1[]
            {
                new Test1 { Name = "Test1" }
            };
            collection1.InsertMany(datas);

统计数量

获取集合中所有的文档数:

collection1.CountDocuments(new BsonDocument())
// await collection1.CountDocumentsAsync(new BsonDocument());

任意一个文档集合对象,使用 CountDocuments(new BsonDocument()) 都是获得此集合的所有文档数,而不是此类型的文档数。例如:

            var collection1 = database.GetCollection<Test1>("Test");
            collection1.CountDocuments(new BsonDocument())

获取的并不是 Test1 类型的文档数量,而是整个集合所有文档的数量。

原因是,CountDocuments() 是一个过滤器函数,可以使用指定条件来筛选符合条件的文档的数量。指定条件后面会介绍。

查询

MongoDB 的查询并不像 LInq 中的表达式,基础了 IEnumerableIEnumerable<T> 接口,因此驱动没有 WhereSelect 这种表达式的查询方法。

Find() 函数是查询函数,里面可以添加丰富的表达式,来筛选文档,当数据加载到本地内存后,即可使用丰富的表达式。

BsonDocument 是一个类型,代表了要查询的文档筛选条件,如果 BsonDocument 对象没有添加任何属性,则代码没有筛选参数,则默认所有文档都符号条件。

我们把 Test1 和 Test2 类型,都加上一个属性:

        public ObjectId _id { get; set; }

不然会报格式化错误:System.FormatException

如何序列化文档

document 是文档对象, JsonSerializer 是 System.Text.Json 的静态类。

Console.WriteLine(JsonSerializer.Serialize(document));

查询第一条记录

var document = collection1.Find(new BsonDocument()).FirstOrDefault();

不加条件可能导致的问题

以下代码会导致程序报错:

            var documents = await collection1.Find(new BsonDocument()).ToListAsync();
            foreach(var item in documents)
            {
                Console.WriteLine(JsonSerializer.Serialize(item));
            }

因为 collection1 是标记为 Test1 的文档集合;但是 .Find(new BsonDocument()) 是查询集合中的所有文档,因此获取到 Test2。

但是 Test2 是不能转为 Test1 的,因此,会导致程序报错。

查看所有文档

var documents = collection1.Find(new BsonDocument()).ToList();
var documents = await collection1.Find(new BsonDocument()).ToListAsync();

前面已经说过,如果集合中存在其它格式的文档,获取全部文档时,因为 Test2 跟 Test1 没任何关系,会导致 MongoDB.Driver 报错。

如果文档数量比较大,要使用异步的 ForEachAsync() 查询,其原理是 回调。

            List<Test1> tests = new List<Test1>();
            Action<Test1> action = item =>
            {
                tests.Add(item);
                Console.WriteLine(JsonSerializer.Serialize(item));
            };

            await collection1.Find(new BsonDocument()).ForEachAsync(action);

查询结束

使用 Find() 以及后续函数查询后,要结束查询(延迟加载),可以使用 ToCursor() 函数结束,程序会立即开始查询并将数据返回内存。

转换查询

使用 ToEnumerable() 可以使用 Linq 来查询文档。

var list = collection1.Find(new BsonDocument()).ToCursor().ToEnumerable();

过滤器

前面我们查询的时候都使用 .Find(new BsonDocument())BsonDocument 是过滤器对象,里面存储了过滤的规则,但是我们不能直接设置 new BsonDocument() 中的属性,而是使用构建器FilterDefinitionBuilder对象,而此对象可以通过 MongoDB.Driver.Builders<TDocument>.Filter 创建 。

假设有以下数据集(文档):

5f8bdf88e63d14cb5f01dd85	小明	19
5f8bdf88e63d14cb5f01dd86	小红	20
5f8bdf88e63d14cb5f01dd87	小张	16
5f8bdf88e63d14cb5f01dd88	小小	17
    
# -----插入数据的代码-----
    public class Test
    {
        public ObjectId _id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

            var datas = new Test[]
            {
                new Test{ Name="小明",Age=19},new Test{ Name="小红",Age=20},new Test{ Name="小张",Age=16},new Test{ Name="小小",Age=17}
            };

            collection.InsertMany(datas);

使用构建器:

FilterDefinition<Test> filter = Builders<Test>.Filter

查询 Age 大于 18 的文档:

FilterDefinition<Test> filter = filterBuilder.Where(item => item.Age >= 18);

获取结果:

Test[] documents = collection.Find(filter).ToEnumerable<Test>().ToArray();

过滤器还有 Gt()In()Lte() 等非 Linq 的函数,需要查看文档学习。

Builders<TDocument>

Builders<TDocument> 除了能够生成过滤构建器,还有其它几种构建器:

		// 条件过滤
        public static FilterDefinitionBuilder<TDocument> Filter { get; }

		// 索引过滤
        public static IndexKeysDefinitionBuilder<TDocument> IndexKeys { get; }

		// 映射器,相当于使用 Linq 的 .Select() 查询自己只需要的字段
        public static ProjectionDefinitionBuilder<TDocument> Projection { get; }

		// 排序,创建排序规则,如工具年龄排序
        public static SortDefinitionBuilder<TDocument> Sort { get; }

		// 更新,更新某些字段的值等
        public static UpdateDefinitionBuilder<TDocument> Update { get; }

详细请参考 https://mongodb.github.io/mongo-csharp-driver/2.10/reference/driver/definitions/#projections

名称映射

由于 MongoDB 区分字段的大小写,文档的字段一般使用驼峰命名法,首字母小写,而 C# 字段属性首字母是 大小开头的,因此需要不同名称对应起来。

可以使用 BsonElement 特性来设置映射的名称。

class Person
{
    [BsonElement("fn")]
    public string FirstName { get; set; }

    [BsonElement("ln")]
    public string LastName { get; set; }
}

以上就是 MongoDB 的初入门知识,但是使用了 MongoDB 有什么好处?可以参考阿里云的这篇文章:https://developer.aliyun.com/article/64352

整理场景如下:

  • 存储应用程序日志。日志结构化,查找方便,可以导出其它格式和二次利用。

  • 增加字段不需要改动表结构,灵活变更。

  • 支持 json 格式导入;类似 json 的数据结构;能够很容易还原对象的属性,一次性存储数据;如果使用传统数据库,则需要建立多个表并设置主键外界关系。

  • 集群。分布式集群海量数据,容易拓展;故障转移保证服务可用;

  • 解决分布式文件存储需求。

  • 索引方式灵活。

  • ... ...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发、订阅和处理的流程。这种实现太简单了,百十行代码就展示了一个基本工作原理。然而,要将这样的解决方案运用到实际生产环境,还有很长的路要走。今天,我们就研究一下在事件处理器中,对象生命周期的管理问题。事实上,不仅仅是在事件处理器
上文已经介绍了Identity Service的实现过程。今天我们继续,实现一个简单的Weather API和一个基于Ocelot的API网关。 回顾 《Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(一)》 Weather API Weather
最近我为我自己的应用开发框架Apworks设计了一套案例应用程序,并以Apache 2.0开源,开源地址是:https://github.com/daxnet/apworks-examples,目的是为了让大家更为方便地学习和使用.NET Core、最新的前端开发框架Angular,以及Apwork
HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务端接口的耦合度。很多当今流行的RESTful API开发框架,包括Spring REST,也都默认支
在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅、通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现。接下来对于事件驱动型架构的讨论,就需要结合一个实际的架构案例来进行分析。在领域驱动设计的讨论范畴,CQRS架构本身就是事件驱动的,因此,
HAL,全称为Hypertext Application Language,它是一种简单的数据格式,它能以一种简单、统一的形式,在API中引入超链接特性,使得API的可发现性(discoverable)更强,并具有自描述的特点。使用了HAL的API会更容易地被第三方开源库所调用,并且使用起来也很方便
何时使用领域驱动设计?其实当你的应用程序架构设计是面向业务的时候,你已经开始使用领域驱动设计了。领域驱动设计既不是架构风格(Architecture Style),也不是架构模式(Architecture Pattern),它也不是一种软件开发方法论,所以,是否应该使用领域驱动设计,以及什么时候使用
《在ASP.NET Core中使用Apworks快速开发数据服务》一文中,我介绍了如何使用Apworks框架的数据服务来快速构建用于查询和管理数据模型的RESTful API,通过该文的介绍,你会看到,使用Apworks框架开发数据服务是何等简单快捷,提供的功能也非常多,比如对Hypermedia的
在上一讲中,我们已经完成了一个完整的案例,在这个案例中,我们可以通过Angular单页面应用(SPA)进行登录,然后通过后端的Ocelot API网关整合IdentityServer4完成身份认证。在本讲中,我们会讨论在当前这种架构的应用程序中,如何完成用户授权。 回顾 《Angular SPA基于
Keycloak是一个功能强大的开源身份和访问管理系统,提供了一整套解决方案,包括用户认证、单点登录(SSO)、身份联合、用户注册、用户管理、角色映射、多因素认证和访问控制等。它广泛应用于企业和云服务,可以简化和统一不同应用程序和服务的安全管理,支持自托管或云部署,适用于需要安全、灵活且易于扩展的用
3月7日,微软发布了Visual Studio 2017 RTM,与之一起发布的还有.NET Core Runtime 1.1.0以及.NET Core SDK 1.0.0,尽管这些并不是最新版,但也已经从preview版本升级到了正式版。所以,在安装Visual Studio 2017时如果启用了
在上文中,我介绍了如何在Ocelot中使用自定义的中间件来修改下游服务的response body。今天,我们再扩展一下设计,让我们自己设计的中间件变得更为通用,使其能够应用在不同的Route上。比如,我们可以设计一个通用的替换response body的中间件,然后将其应用在多个Route上。 O
不少关注我博客的朋友都知道我在2009年左右开发过一个名为Apworks的企业级应用程序开发框架,旨在为分布式企业系统软件开发提供面向领域驱动(DDD)的框架级别的解决方案,并对多种系统架构风格提供支持。这个框架的开发和维护我坚持了很久,一直到2015年,我都一直在不停地重构这个项目。目前这个项目在
好吧,这个题目我也想了很久,不知道如何用最简单的几个字来概括这篇文章,原本打算取名《Angular单页面应用基于Ocelot API网关与IdentityServer4ʺSP.NET Identity实现身份认证与授权》,然而如你所见,这样的名字实在是太长了。所以,我不得不缩写“单页面应用”几个字
在前面两篇文章中,我介绍了基于IdentityServer4的一个Identity Service的实现,并且实现了一个Weather API和基于Ocelot的API网关,然后实现了通过Ocelot API网关整合Identity Service做身份认证的API请求。今天,我们进入前端开发,设计
Ocelot是ASP.NET Core下的API网关的一种实现,在微服务架构领域发挥了非常重要的作用。本文不会从整个微服务架构的角度来介绍Ocelot,而是介绍一下最近在学习过程中遇到的一个问题,以及如何使用中间件(Middleware)来解决这样的问题。 问题描述 在上文中,我介绍了一种在Angu
在大数据处理和人工智能时代,数据工厂(Data Factory)无疑是一个非常重要的大数据处理平台。市面上也有成熟的相关产品,比如Azure Data Factory,不仅功能强大,而且依托微软的云计算平台Azure,为大数据处理提供了强大的计算能力,让大数据处理变得更为稳定高效。由于工作中我的项目
在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容。下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了。其中绿色的部分就是上文中新实现的部分,包括一个简单的Event Store,一个事件处理器执行上下文的接
在之前《在ASP.NET Core中使用Apworks快速开发数据服务》一文的评论部分,.NET大神张善友为我提了个建议,可以使用Compile As a Service的Roslyn为语法解析提供支持。在此非常感激友哥给我的建议,也让我了解了一些Roslyn的知识。使用Roslyn的一个很大的好处
很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构。这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地订阅来自于某个渠道的事件消息,并对接收到的消息进行处理,于此同时,它还能够向该渠道发送事件消息,以便