广西藤 發表於 2020-10-18 15:09:00

初识 MongoDB 和 .NET Core 入门

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>浅入 MongoDB<ul><li>MonogoDB 是什么<ul><li>结构化数据</li></ul></li><li>MongoDB 与关系型数据库</li><li>MongoDB 入门命令</li><li>文档</li></ul></li><li>.NET Core 示例<ul><li>集合</li><li>统计数量</li><li>查询<ul><li>如何序列化文档</li><li>查询第一条记录</li></ul></li><li>不加条件可能导致的问题</li><li>查看所有文档</li><li>查询结束</li><li>转换查询</li><li>过滤器</li><li>Builders<code>&lt;TDocument&gt;</code></li><li>名称映射</li></ul></li></ul></div><br>
昨天搭建完毕 MongoDB 集群 后,开始计划了解 MongoDB ,并引入使用场景,这里介绍一下学习过程中的一些笔记,帮助读者快速了解 MongoDB 并使用 C# 对其进行编码。<p></p>
<h2 id="浅入-mongodb">浅入 MongoDB</h2>
<h3 id="monogodb-是什么">MonogoDB 是什么</h3>
<p>MongoDB 是 NoSQL 型数据库,主要特征是存储结构化数据,MongoDB 是基于分布式文件存储的开源数据库系统。</p>
<h4 id="结构化数据">结构化数据</h4>
<p>以往我们使用 Mysql、SqlServer 等数据库,数据都是一条条的。MongoDB 的结构化数据正是区别于这种列-行式的数据。</p>
<p>结构化数据具有层级关系:</p>
<p>例如:</p>
<pre><code class="language-json">{
   name: "MongoDB",
   type: "database",
   count: 1,
   info: {
         x: 203,
         y: 102
   }
}
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202010/1315495-20201018090055635-1380480128.png" alt="结构化数据" loading="lazy"></p>
<h3 id="mongodb-与关系型数据库">MongoDB 与关系型数据库</h3>
<p>由于 MongoDB 中,没有表、行、列,因此初学 MongoDB 时可能会有困扰,这里给出一些 MongoDB 与 普通SQL数据库对应的术语。</p>
<table>
<thead>
<tr>
<th style="text-align: left">SQL术语/概念</th>
<th style="text-align: left">MongoDB术语/概念</th>
<th style="text-align: left">解释/说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">database</td>
<td style="text-align: left">database</td>
<td style="text-align: left">数据库</td>
</tr>
<tr>
<td style="text-align: left">table</td>
<td style="text-align: left">collection</td>
<td style="text-align: left">数据库表/集合</td>
</tr>
<tr>
<td style="text-align: left">row</td>
<td style="text-align: left">document</td>
<td style="text-align: left">数据记录行/文档</td>
</tr>
<tr>
<td style="text-align: left">column</td>
<td style="text-align: left">field</td>
<td style="text-align: left">数据字段/域</td>
</tr>
<tr>
<td style="text-align: left">index</td>
<td style="text-align: left">index</td>
<td style="text-align: left">索引</td>
</tr>
<tr>
<td style="text-align: left">table joins</td>
<td style="text-align: left"></td>
<td style="text-align: left">非关系型数据库,表与表之间没关系</td>
</tr>
<tr>
<td style="text-align: left">primary key</td>
<td style="text-align: left">primary key</td>
<td style="text-align: left">主键,MongoDB自动将<strong>_id</strong>字段设置为主键</td>
</tr>
</tbody>
</table>
<p>资料来源:https://www.runoob.com/mongodb/mongodb-databases-documents-collections.html</p>
<h3 id="mongodb-入门命令">MongoDB 入门命令</h3>
<p>使用 mongo 进入 MongoDB shell 后,可使用命令(相当于SQL)执行操作。</p>
<p>注: MongoDB 中,有一个自动的 _id 字段,此字段 MongoDB 自动设置为主键并自动生成值。</p>
<p>显示所有数据库(包含系统数据库):</p>
<pre><code>show dbs
</code></pre>
<p>当前正在操作的数据库或集合:</p>
<pre><code>db
</code></pre>
<p>连接到指定数据库:</p>
<pre><code>use {数据库名称}
</code></pre>
<p>显示所有集合:</p>
<pre><code>show collections
# 或
show tables
</code></pre>
<p>查看集合中的所有文档:</p>
<pre><code class="language-shell"># MyCollection 是集合名称
db.getCollection("MyCollection").find()
db.getCollection("MyCollection").find().limit(1000).skip(0)
</code></pre>
<p>可能你不信,笔者百度了很久,第一页没找到一篇合适的友好的 "mongoDB 查看集合中的所有文档",特别是 CSDN 的垃圾文真的多。建议别瞎折腾了,去下一个 Navicat Premium,操作的时候,底部会提示所用的命令。</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202010/1315495-20201018095328633-1356570268.png" alt="使用工具查看MongoDB命令" loading="lazy"></p>
<p>另外 MongoDB 有很多实用工具:https://docs.mongodb.com/tools/</p>
<h3 id="文档">文档</h3>
<p>MongoDB 中的文档(Document)即关系型数据库中的一条记录(row)、一行数据。</p>
<p>但, MongoDB 中,一个集合(Collection-Table)中,是不需要具有相同字段的。例如:</p>
<p>A 文档:</p>
<pre><code class="language-json">{
   name: "MongoDB",
   type: "database",
   count: 1,
   info: {
         x: 203,
         y: 102
   }
}
</code></pre>
<p>B 文档:</p>
<pre><code class="language-json">{
   name: "MongoDB",
   typeName: "database",
   dataCount: 1,
   dataInfo: {
         m: 203,
         n: 102
   }
}
</code></pre>
<h2 id="net-core-示例">.NET Core 示例</h2>
<p>我们从一个基础模板开始。</p>
<p>创建一个控制台程序,打开 Nuget 搜索并安装 <code>MongoDB.Driver</code>。</p>
<pre><code class="language-csharp">            var client = new MongoClient("mongodb://{MongoDB}:27017");
            IMongoDatabase database = client.GetDatabase("Test");
</code></pre>
<h3 id="集合">集合</h3>
<p>可以通过 <code>CreateCollection()</code> 或 <code>CreateCollectionAsync()</code> 创建一个集合,跟普通数据库不同的是,创建集合时是不需要指定结构的,只需要指定名称即可:</p>
<pre><code class="language-csharp">await database.CreateCollectionAsync("Test");
</code></pre>
<p><strong>获取集合</strong></p>
<p><code>GetCollection()</code> 函数可以让我们获取到集合,如果集合不存在,则会自动创建。</p>
<pre><code class="language-csharp">IMongoCollection&lt;TDocument&gt; GetCollection&lt;TDocument&gt;()
</code></pre>
<p>由于同一个集合可以有不同字段和字段类型的文档,因此几个文档如果有所差别,是很难统一起来的,例如:</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202010/1315495-20201018104615835-2137897970.png" alt="集合的文档字段不同" loading="lazy"></p>
<p>(N/A) 代表此文档没有这个字段;如果一个文档有 10 个字段,另一个文档有 8 个字段,但是两者的字段完全不同时,要合并起来来,就有 18 个字段了。很明显,不应该汇集在一起,而是应该使用强类型对其 ”归档“ 。</p>
<p>创建两个类,分别为 Test1,Test2,其内容如下:</p>
<pre><code class="language-csharp">    public class Test1
    {
      public string Name { get; set; }
    }

    public class Test2
    {
      public string DataType { get; set; }
    }
</code></pre>
<p>以两种文档类型获取集合:</p>
<pre><code class="language-csharp">            var collection1 = database.GetCollection&lt;Test1&gt;("Test");
            var collection2 = database.GetCollection&lt;Test2&gt;("Test");
</code></pre>
<p>这个获取集合的意思是,获取此集合中这类格式的文档的操作能力。</p>
<p>往集合中插入数据:</p>
<pre><code class="language-csharp">            collection1.InsertOne(new Test1 { Name = "Test1" });
            collection2.InsertOne(new Test2 { DataType = "Test2" });
                        // await collection.InsertOneAsync(object);
</code></pre>
<p>启动,查看结果。</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202010/1315495-20201018105356890-1672081218.png" alt="" loading="lazy"></p>
<p><code>InsertMany()</code> 可以插入批量数据:</p>
<pre><code class="language-csharp">            Test1[] datas = new Test1[]
            {
                new Test1 { Name = "Test1" }
            };
            collection1.InsertMany(datas);
</code></pre>
<h3 id="统计数量">统计数量</h3>
<p>获取集合中所有的文档数:</p>
<pre><code class="language-csharp">collection1.CountDocuments(new BsonDocument())
// await collection1.CountDocumentsAsync(new BsonDocument());
</code></pre>
<p>任意一个文档集合对象,使用 <code>CountDocuments(new BsonDocument())</code> 都是获得此集合的所有文档数,而不是此类型的文档数。例如:</p>
<pre><code class="language-csharp">            var collection1 = database.GetCollection&lt;Test1&gt;("Test");
            collection1.CountDocuments(new BsonDocument())
</code></pre>
<p>获取的并不是 Test1 类型的文档数量,而是整个集合所有文档的数量。</p>
<p>原因是,<code>CountDocuments()</code> 是一个过滤器函数,可以使用指定条件来筛选符合条件的文档的数量。指定条件后面会介绍。</p>
<h3 id="查询">查询</h3>
<p>MongoDB 的查询并不像 LInq 中的表达式,基础了 <code>IEnumerable</code>或 <code>IEnumerable&lt;T&gt;</code> 接口,因此驱动没有 <code>Where</code>、<code>Select</code> 这种表达式的查询方法。</p>
<p><code>Find()</code> 函数是查询函数,里面可以添加丰富的表达式,来筛选文档,当数据加载到本地内存后,即可使用丰富的表达式。</p>
<p><code>BsonDocument</code> 是一个类型,代表了要查询的文档筛选条件,如果 <code>BsonDocument</code>对象没有添加任何属性,则代码没有筛选参数,则默认所有文档都符号条件。</p>
<p>我们把 Test1 和 Test2 类型,都加上一个属性:</p>
<pre><code class="language-csharp">      public ObjectId _id { get; set; }
</code></pre>
<p>不然会报格式化错误:<code>System.FormatException</code></p>
<h4 id="如何序列化文档">如何序列化文档</h4>
<p><code>document</code> 是文档对象, JsonSerializer 是 System.Text.Json 的静态类。</p>
<pre><code class="language-csharp">Console.WriteLine(JsonSerializer.Serialize(document));
</code></pre>
<h4 id="查询第一条记录">查询第一条记录</h4>
<pre><code class="language-csharp">var document = collection1.Find(new BsonDocument()).FirstOrDefault();
</code></pre>
<h3 id="不加条件可能导致的问题">不加条件可能导致的问题</h3>
<p>以下代码会导致程序报错:</p>
<pre><code class="language-csharp">            var documents = await collection1.Find(new BsonDocument()).ToListAsync();
            foreach(var item in documents)
            {
                Console.WriteLine(JsonSerializer.Serialize(item));
            }
</code></pre>
<p>因为 <code>collection1</code> 是标记为 <code>Test1</code> 的文档集合;但是 <code>.Find(new BsonDocument())</code> 是查询集合中的所有文档,因此获取到 Test2。</p>
<p>但是 Test2 是不能转为 Test1 的,因此,会导致程序报错。</p>
<h3 id="查看所有文档">查看所有文档</h3>
<pre><code class="language-csharp">var documents = collection1.Find(new BsonDocument()).ToList();
var documents = await collection1.Find(new BsonDocument()).ToListAsync();
</code></pre>
<p>前面已经说过,如果集合中存在其它格式的文档,获取全部文档时,因为 Test2 跟 Test1 没任何关系,会导致 <code>MongoDB.Driver</code> 报错。</p>
<p>如果文档数量比较大,要使用异步的 <code>ForEachAsync()</code> 查询,其原理是 回调。</p>
<pre><code class="language-csharp">            List&lt;Test1&gt; tests = new List&lt;Test1&gt;();
            Action&lt;Test1&gt; action = item =&gt;
            {
                tests.Add(item);
                Console.WriteLine(JsonSerializer.Serialize(item));
            };

            await collection1.Find(new BsonDocument()).ForEachAsync(action);
</code></pre>
<h3 id="查询结束">查询结束</h3>
<p>使用 <code>Find()</code> 以及后续函数查询后,要结束查询(延迟加载),可以使用 <code>ToCursor()</code> 函数结束,程序会立即开始查询并将数据返回内存。</p>
<h3 id="转换查询">转换查询</h3>
<p>使用 <code>ToEnumerable()</code> 可以使用 Linq 来查询文档。</p>
<pre><code>var list = collection1.Find(new BsonDocument()).ToCursor().ToEnumerable();
</code></pre>
<h3 id="过滤器">过滤器</h3>
<p>前面我们查询的时候都使用 <code>.Find(new BsonDocument())</code>,<code>BsonDocument</code>是过滤器对象,里面存储了过滤的规则,但是我们不能直接设置 <code>new BsonDocument()</code> 中的属性,而是使用构建器<code>FilterDefinitionBuilder</code>对象,而此对象可以通过 <code>MongoDB.Driver.Builders&lt;TDocument&gt;.Filter</code> 创建 。</p>
<p>假设有以下数据集(文档):</p>
<pre><code class="language-csharp">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);
</code></pre>
<p>使用构建器:</p>
<pre><code>FilterDefinition&lt;Test&gt; filter = Builders&lt;Test&gt;.Filter
</code></pre>
<p>查询 Age 大于 18 的文档:</p>
<pre><code class="language-csharp">FilterDefinition&lt;Test&gt; filter = filterBuilder.Where(item =&gt; item.Age &gt;= 18);
</code></pre>
<p>获取结果:</p>
<pre><code class="language-csharp">Test[] documents = collection.Find(filter).ToEnumerable&lt;Test&gt;().ToArray();
</code></pre>
<p>过滤器还有 <code>Gt()</code>、<code>In()</code>、<code>Lte()</code> 等非 Linq 的函数,需要查看文档学习。</p>
<h3 id="builderstdocument">Builders<code>&lt;TDocument&gt;</code></h3>
<p><code>Builders&lt;TDocument&gt;</code> 除了能够生成过滤构建器,还有其它几种构建器:</p>
<pre><code>                // 条件过滤
      public static FilterDefinitionBuilder&lt;TDocument&gt; Filter { get; }

                // 索引过滤
      public static IndexKeysDefinitionBuilder&lt;TDocument&gt; IndexKeys { get; }

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

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

                // 更新,更新某些字段的值等
      public static UpdateDefinitionBuilder&lt;TDocument&gt; Update { get; }
</code></pre>
<p>详细请参考 https://mongodb.github.io/mongo-csharp-driver/2.10/reference/driver/definitions/#projections</p>
<h3 id="名称映射">名称映射</h3>
<p>由于 MongoDB 区分字段的大小写,文档的字段一般使用驼峰命名法,首字母小写,而 C# 字段属性首字母是 大小开头的,因此需要不同名称对应起来。</p>
<p>可以使用 <code>BsonElement</code> 特性来设置映射的名称。</p>
<pre><code class="language-csharp">class Person
{
   
    public string FirstName { get; set; }

   
    public string LastName { get; set; }
}
</code></pre>
<p>以上就是 MongoDB 的初入门知识,但是使用了 MongoDB 有什么好处?可以参考阿里云的这篇文章:https://developer.aliyun.com/article/64352</p>
<p>整理场景如下:</p>
<ul>
<li>
<p>存储应用程序日志。日志结构化,查找方便,可以导出其它格式和二次利用。</p>
</li>
<li>
<p>增加字段不需要改动表结构,灵活变更。</p>
</li>
<li>
<p>支持 json 格式导入;类似 json 的数据结构;能够很容易还原对象的属性,一次性存储数据;如果使用传统数据库,则需要建立多个表并设置主键外界关系。</p>
</li>
<li>
<p>集群。分布式集群海量数据,容易拓展;故障转移保证服务可用;</p>
</li>
<li>
<p>解决分布式文件存储需求。</p>
</li>
<li>
<p>索引方式灵活。</p>
</li>
<li>
<p>... ...</p>
</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    痴者工良(https://whuanle.cn)<br><br>
来源:https://www.cnblogs.com/whuanle/p/13835306.html
頁: [1]
查看完整版本: 初识 MongoDB 和 .NET Core 入门