MongoDB 近幾年來發展的很穩定,也是我心目中 NoSQL 選擇方案之一,對於 .NET 的開發者而言,開發體驗也算是不錯,在這裡我會簡單的分享怎麼建立 .NET + MongoDB 開發環境以及簡單的 CRUD 操作
開發環境
- Windows 11
- Rider 2023.2
- .NET 7
- MongoDB.Driver 2.21.0
利用 Docker 建立 MongoDB 開發環境
通過 Docker 搭建 MongoDB 的開發環境,很容易在本機搭建起 Local MongoDB Server,作為測試替身也是非常的合適的 mongo - Official Image | Docker Hub
有了 MongoDB 再搭配一套 Client,mongo-express 是一套 Web UI 的 MongoDB 管理工具,搭配他用來除錯是一個不錯的選擇,他也提供了 Docker mongo-express - Official Image | Docker Hub
將以下內容另存 docker-compose.yml
version: "3.8"
services:
mongo:
image: mongo
container_name: mongo_test
ports:
- 27017:27017
mongo-express:
image: mongo-express
container_name: mongo_express_test
ports:
- 8081:8081
用 Shell 啟動它
docker-compose.yml up -d
MongoDB Client
mongo-express
訪問 http://localhost:8081,就可以進入 Web UI 管理介面
Jetbrains
Jetbrains 系列的 IDE 本身就內建連接 MongoDB 的 Driver,下圖是我用 Rider 連接 MongoDB 的演示
MongoDB Compass
官方提供的 MongoDB Compass
選擇自己所需的平台下載
MongoDB Compass Download (GUI) | MongoDB
我選擇用 winget
winget install MongoDB.Compass.Full
打開它,連接 MongoDB 後,可以觀看效能分析,真的讚
上述 MongoDB Client 挑一個喜歡的就可以
使用 MongoDB.Driver 訪問 MongoDB Server
開啟一個新的 .NET 7 的 Test 專案
dotnet new mstest --language "C#" --framework "net7.0" -o Lab.MongoDB.CRUD.TestProject
安裝套件
dotnet add package MongoDB.Driver --version 2.21.0
連接 MongoDB
// MongoDB 連線字串
var connectionString = "mongodb://localhost:27017";
// 產生 MongoClient 物件
var mongoClient = new MongoClient(connectionString);
// 取得 MongoServer 物件
var mongoServer = mongoClient.GetServer();
// 取得 MongoDatabase 物件
var mongoDatabase = mongoServer.GetDatabase("test");
// 取得 Collection
mongoCollection = mongoDatabase.GetCollection<Product>("Products");
更多的連接設定,請參考
https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/connection/
官方的 MongoDB.Driver 直接提供 Strong Object 對應 MongoDB Document 的機制,搭配 LINQ 查詢資料,開發體驗真的不賴。
定義一個 Product 物件
public record Product
{
public string Id { get; init; }
public string Name { get; init; }
public decimal Price { get; init; }
public string Remark { get; init; }
}
更多的定義,請參考
https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/data-formats/poco/
開始之前,還需要準備測試環境、還原測試資料
private static MongoDbContainer MongoDbContainer;
private static MongoClient MongoClient;
private readonly string TestData = "出發吧,讓我們航向偉大的航道";
[ClassInitialize]
public static async Task ClassInitialize(TestContext context)
{
var mongoClientSettings = new MongoClientSettings()
{
Server = new MongoServerAddress("localhost", 27017),
};
MongoClient = new MongoClient(mongoClientSettings);
}
[TestCleanup]
public void TestCleanup()
{
//復原資料
var mongoCollection = MongoClient.GetDatabase("example").GetCollection<Product>("product");
var filter = Builders<Product>.Filter
.Eq(r => r.Remark, this.TestData);
var data = mongoCollection.DeleteMany(filter);
}
除了 Strong Object 之外,也可以用 BsonElement,用起來就有點像 ADO.NET DataColumn、DataRow,詳細用法請參考
Work with BSON — C#/.NET (mongodb.com)
新增資料
[TestMethod]
public async Task 新增一筆資料()
{
var mongoCollection = MongoClient.GetDatabase("example").GetCollection<Product>("product");
var expected = new Product
{
Id = "1",
Name = "TV",
Price = 33.11m,
Remark = this.TestData
};
//新增一筆資料
await mongoCollection.InsertOneAsync(expected);
//驗證
var actual = await mongoCollection.AsQueryable().FirstOrDefaultAsync(p => p.Id == "1");
Assert.AreEqual(expected, actual);
}
更新資料
- 先用 Builders<Product>.Filter 定義更新甚麼資料
- 再用 Builders<Product>.Update 指定要更新的欄位
- 最後用 MongoCollection.UpdateOneAsync 寫入資料
完整代碼如下:
[TestMethod]
public async Task 更新一筆資料()
{
var mongoCollection = MongoClient.GetDatabase("example").GetCollection<Product>("product");
var expected = new Product
{
Id = "1",
Name = "TV",
Price = 33.11m,
Remark = this.TestData
};
//產生資料
var products = this.GenerateProducts();
await mongoCollection.InsertManyAsync(products);
var filter = Builders<Product>.Filter
.Eq(restaurant => restaurant.Id, "1");
var update = Builders<Product>.Update
.Set(restaurant => restaurant.Name, "TV");
//更新資料
await mongoCollection.UpdateOneAsync(filter, update);
//驗證
var actual = await mongoCollection.AsQueryable().FirstOrDefaultAsync(p => p.Id == "1");
Assert.AreEqual(expected, actual);
}
異動、刪除、查詢資料前的準備資料,完整代碼如下:
private List<Product> GenerateProducts()
{
var products = new List<Product>()
{
new()
{
Id = "1",
Name = "Air jordan 11",
Price = 33.11m,
Remark = this.TestData
},
new()
{
Id = "2",
Name = "Air jordan 12",
Price = 33.12m,
Remark = this.TestData
},
new()
{
Id = "3",
Name = "Air jordan 13",
Price = 33.13m,
Remark = this.TestData
}
};
return products;
}
刪除資料
- 先用 Builders<Product>.Filter 定義更新甚麼資料
- 最後用 MongoCollection.DeleteOneAsync 刪除資料
完整代碼如下:
[TestMethod]
public async Task 刪除資料()
{
var mongoCollection = MongoClient.GetDatabase("example").GetCollection<Product>("product");
//產生資料
var products = this.GenerateProducts();
await mongoCollection.InsertManyAsync(products);
var filter = Builders<Product>.Filter
.Eq(restaurant => restaurant.Id, "1");
//更新資料
await mongoCollection.DeleteOneAsync(filter);
//驗證
var actual = await mongoCollection.AsQueryable().FirstOrDefaultAsync(p => p.Id == "1");
Assert.AreEqual(null, actual);
}
查詢資料
有兩種查詢方式,分別是 Builder<T>.Filter、LINQ
Builder<T>.Filter
var filter = Builders<Product>.Filter.Eq(x => x.Id, "1");
var find = await mongoCollection.FindAsync(filter);
var data1 = await find.FirstOrDefaultAsync();
LINQ 用法,先用 AsQueryable
var data2 = await mongoCollection.AsQueryable().FirstOrDefaultAsync(p => p.Id == "1");
完整代碼如下:
[TestMethod]
public async Task 查詢()
{
var mongoCollection = MongoClient.GetDatabase("example").GetCollection<Product>("product");
var expected = new Product
{
Id = "1",
Name = "Air jordan 11",
Price = 33.11m,
Remark = this.TestData
};
//產生資料
var products = this.GenerateProducts();
await mongoCollection.InsertManyAsync(products);
//查詢1
var filter = Builders<Product>.Filter.Eq(x => x.Id, "1");
var find = await mongoCollection.FindAsync(filter);
var data1 = await find.FirstOrDefaultAsync();
//驗證
Assert.AreEqual(expected, data1);
//查詢2
var data2 = await mongoCollection.AsQueryable().FirstOrDefaultAsync(p => p.Id == "1");
//驗證
Assert.AreEqual(expected, data2);
}
更多的範例
https://www.mongodb.com/docs/drivers/csharp/current/usage-examples/
https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/crud/
Builder<T>.Filter 對應 MongoDB 的操作
https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/crud/read-operations/specify-query/
停止 docker-compose,套用 TestContainer,套用上篇學到的東西
[ClassInitialize]
public static async Task ClassInitialize(TestContext context)
{
MongoDbContainer = new MongoDbBuilder()
.WithPortBinding(27017, true)
.Build();
await MongoDbContainer.StartAsync();
var mongoClientSettings = MongoClientSettings.FromConnectionString(MongoDbContainer.GetConnectionString());
MongoClient = new MongoClient(mongoClientSettings);
}
範例位置
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET