DAY29 MongoDB 使用 C# Driver 操作

DAY29 MongoDB 使用 C# Driver 操作

不知道有没有人记得 DAY1 文章说本篇使用 .NET 平台,结果一直到第 29 天都还没有写到任何有关的东西,其实原本是想全程使用 .NET 来举例,但考量到观看者不一定使用 C#,还是乖乖回头用 javascript 来说明。

会写这一篇呢是想帮助过去的我自己,当时使用的语言是 C#,因专案要接触 MongoDB,在语法上着实恼了一阵子,明明是很简单的东西却不习惯使用 Mongo C# Driver,导致额外花了不少时间在熟悉工具,要是有这一篇的话,肯定能快速上手(卖瓜?)

本篇文章会像是 Cheat sheet 性质,直接给予语法,一个速食的概念,实际应用在专案上,可以再重构得更漂亮些。另外本篇使用的语法都是强型别为主,毕竟使用 C# 就要发挥它的特性,如果使用字串代替栏位,虽然开发期很轻松快速,但很容易因为 typo 导致要抓 bug 抓很久,别问我为什麽知道,因为开发初期就吃了很多苦XD


以下的范例我们会使用下面这个 entity 来当作举例

[BsonIgnoreExtraElements]
public class SampleClassEntity
{
    [BsonId]
    public string Id { get; set;}
    
    public int IntField { get; set;}
    
    public string StringField { get; set;}    
}

[BsonIgnoreExtraElements] 是忽略没有 mapping 到 c# 端的栏位

[BsonId] 是 Mongo C# driver 的 attribute,代表这个 property 是对应到 MongoDB _id 栏位。


连线 (Connection)

const string MongoAddress = "mongodb://localhost:27017";
const string HelloDatabase = "hellomongodb";
const string Collection = "sample";

var client  = new MongoClient(MongoAddress);

public IMongoCollection<SampleClassEntity> GetCollection()
{
    var _client = new MongoClient(MongoAddress);
    return _client.GetDatabase(HelloDatabase).GetCollection<SampleClassEntity>(Collection);
}

建立 (Create)

await MongoHelper.GetCollection().InsertOneAsync(new SampleClassEntity());

一次建立多笔(bulk write)会在 Update 那边一起使用,即 MongoDB 的 upsert

读取 (Read)

  • 方法一
var builder = Builders<SampleClassEntity>.Filter;
var dbFilter = builder.Gt(x => x.IntField, 1)
               & builder.Eq(x => x.StringField, "ComparedString");

首先我们建立一个 Filter 类型的 builder,接着拼凑出我们要的查询条件。
以上面的范例是 IntField > 1StringField == ComparedString

var someCondition = true;

if (someCondition)
    dbFilter &= builder.Gt(x => x.IntField, 2);

也会有一些状况是特定条件下才会有的,上面是如果 someCondition == true,那就会需要多家这个过滤条件。

var result = await MongoHelper.GetCollection().Find(dbFilter).ToListAsync();

接着我们就可以将此 filter 放入 Find() 内当参数使用。
这边的 await 与 ToListAsync 是非同步用法,如果是同步的呼叫那就不需要加上去。

  • 方法二
// Find by cursor
var proj = Builders<SampleClassEntity>.Projection
        .Include(x => x.Id)
        .Include(x => x.IntField);

using var cursor  = await MongoHelper.GetCollection()
    .WithReadPreference(ReadPreference.SecondaryPreferred)
    .FindAsync(dbFilter, new FindOptions<SampleClassEntity>
    {
        Sort = Builders<SampleClassEntity>.Sort.Descending(x => x.Id),
        Skip = 1,
        Limit = 2,
        Projection = proj
    });

var result = cursor.ToEnumerable().ToArray();

这边除了使用 cursor 方式之外多了一些东西。

  • Projection
    这就相当於要取出甚麽栏位,分别使用 IncludeExclude
  • WithReadPreference(ReadPreference.SecondaryPreferred)
    Read倾向使用哪个节点,使用时可以查看 ReadPreference enum 定义了那些
  • FindOptions
    查询的额外设定,有排序、跳过、数量限制以及 Project 设定

更新 (Update)

更新分为 Upsert 与 Replace 两个项目来讲,但本质上不会差太多的,一个是更新(设定)特定栏位,另一个是整个物件取代。
这边的范例是直接用 BulkWrite 方式,批量进行修改了。

  • Upsert
var src = Array.Empty<SampleClassEntity>();
var bulks = src.Select(entity =>
{
    return new UpdateOneModel<SampleClassEntity>
    (
        Builders<SampleClassEntity>.Filter.Eq(x => x.Id, ""),
        Builders<SampleClassEntity>.Update
            .Inc(x => x.IntField, 1)
            .Set(x => x.StringField, "2")
    ) {IsUpsert = true};
}).ToArray();

await MongoHelper.GetCollection().BulkWriteAsync(bulks, new BulkWriteOptions{IsOrdered = false});

Update 的方式须特别指定要更新的栏位,这边举了
Inc 即 Increment,增加数量的意思
Set 更新成新值
SetOnInsert 是在第一次写入时才会使用该值,後面任何 Update 都不会修改
IsOrdered 整批量个更新不需要按顺序执行(之前文章有提到)

  • Replace
var src = Array.Empty<SampleClassEntity>();
var bulks = src.Select(x =>
{
    var replace = new ReplaceOneModel<SampleClassEntity>(
            Builders<SampleClassEntity>.Filter.Eq(m => m.Id, x.Id), x)
        {IsUpsert = true};
    return replace;

}).ToArray();

await MongoHelper.GetCollection().BulkWriteAsync(bulks);

Replace 相对单纯一些,只需要 Filter 找到特定的文件,进行整个内容取代。

删除 (Delete)

var filter = Builders<SampleClassEntity>.Filter.In(x => x.Id, new[] {"ToDeleteId"});

await MongoHelper.GetCollection().DeleteManyAsync(filter);

删除部分是直接用 DeleteMany,可以按需求使用 DeleteOne

最後还是要强调,这只是语法的演示,实务上需要再调整写法与效能。


本系列文章会同步发表於我个人的部落格 Pie Note


<<:  人脸辨识-day14 系统建置

>>:  Day 14 淘家集运流程设计分享 (今天休一天,分享最近接的案)

Day29 - this&Object Prototypes Ch3 Objects - Review

Iteration for (var i=0; i<myArr.length; i++){}...

TailwindCSS 从零开始 - 元件相同时,把共同样式拉出来

单一按钮样式元件 前篇有写到单一按钮,可以把样式拆成元件来使用。如下: @layer compon...

Day 16 - 卷积神经网络 CNN (1)-壹页AI战国史

历史重要性 CNN历史已发生,为何要花时间了解它?个人认为了解CNN历史可以让我们选择以同方式解决不...

Unity与Photon的新手相遇旅途 | Day9-第三人称设定

今天的内容是该如何简单地做到第三人称视角 ...

【D15】制作食谱:运用厨具,制作订阅的Class(py档)

前言 把手边的工具都了解一遍,像是合约、订阅等等。尤其是昨天的订阅,让我们更前一步,但是实际上这样的...