ASP.NET 6啓動時自動創建MongoDB索引

大家好,我是Edison。

最近,在使用MongoDB時,碰到這樣的一個需求:針對某個Collection手動在開發環境創建了索引,但在測試環境和生產環境不想再手動操作了,於是就想着通過代碼的方式在ASP.NET 6應用啓動時自動創建。

背景知識

索引本質上是樹,最小的值在最左邊的葉子上,最大的值在最右邊的葉子上,使用索引可以提高查詢速度(而不用全表掃描),也可以預防髒數據的插入(如唯一索引)。索引既支持普通字段,也支持內嵌文檔中某個鍵和數組元素進行索引。

在MongoDB中可以創建的索引類型:

  • 唯一索引 unique:保證數據的唯一不重複

  • 稀疏索引 sparse

  • 複合索引:用於提高查詢速度

  • TTL 索引 : 設置文檔的緩存時間,時間到了會自動刪除掉

  • 全文索引:便於大文本查詢(如概要、文章等長文本)

  • 二維平面索引:便於2d平面查詢

  • 地理空間索引:便於地理查詢

通過Mongo Shell管理索引:

// 創建索引
db.collection.createIndex(keys, options);
​
// 查詢索引
db.collection.getIndexes(filter);
​
// 刪除索引
db.collection.dropIndex("IndexName");
​
// 刪除所有索引
db.collection.dropIndexes()
​
// explain 查看查詢是否走索引
// "stage" : "COLLSCAN", 表示全集合掃描
// "stage" : "IXSCAN" ,基於索引的掃描
db.collection.find(query,options).explain(options)

準備工作

假設我們有一個Entity定義如下:

[Table("MyTasks")]
public class MyTaskEntity : IEntity
{
    [BsonId]        
    [BsonRepresentation(BsonType.ObjectId)]
    public ObjectId Id { get; set; }
    public string OrderNumber { get; set; }
    public List<TransmissionEntity> Transmissions { get; set; }

    public DispatchTaskEntity()
    {
        this.Transmissions = new List<TransmissionEntity>();
    }

    public DispatchTaskEntity(string orderNumber)
    {
        this.OrderNumber = orderNumber;
        this.Transmissions = new List<TransmissionEntity>();
    }
    
    ......
}

這裏,我們以之前分享的一篇文章《在ASP.NET 6中使用工作單元操作MongoDB》爲基礎,不熟悉的朋友可以先看看這篇文章。

下面,我們將使用基於上面提到的那篇文章中的 EDT.MongoProxy組件中 的內容 MongoDbConection,這是一個包裹MongoClient的單例對象:

public class MongoDbConnection : IMongoDbConnection
{
    public IMongoClient DatabaseClient { get; }
    public string DatabaseName { get; }

    public MongoDbConnection(MongoDatabaseConfigs configs, IConfiguration configuration)
    {
        DatabaseClient = new MongoClient(configs.GetMongoClientSettings(configuration));
        DatabaseName = configs.DatabaseName;
    }
}

方式一:使用Builders.IndexKeys

這裏創建一個靜態類AppDbContext用於進行MongoDB索引創建,假設我們需要創建一個針對OrderNumber字段升序排列的唯一索引,那麼創建的代碼如下所示:

public static class AppDbContext
{
    /// <summary>
    /// Create indexes in MongoDB when startup
    /// NOTE: It'll skip creation when the indexes already exists.
    /// </summary>
    public static void Initialize(IApplicationBuilder app)
    {
        var dbInstance = app.ApplicationServices.GetService<IMongoDbConnection>();
        var logger = app.ApplicationServices.GetService<ILogger<MongoRepository>>();
        var db = dbInstance.DatabaseClient.GetDatabase(dbInstance.DatabaseName);

        var collection = db.GetCollection("MyTasks");

        // Index definitions
        var indexKeysDefine = Builders<MyTaskEntity>.IndexKeys.Ascending(indexKey => indexKey.OrderNumber);

        // Create indexes by RunCommand
        try
        {
            await collection.Indexes.CreateOneAsync(new CreateIndexModel(indexKeysDefine)); 
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "{service}.{method} - throws an exception when creating indexes in database",
               nameof(AppDbContext), nameof(Initialize));
        }
    }
}

使用Builders.IndexKeys可以方便快速的聲明索引,並且它只會在對應索引不存在的時候創建,已存在時則會跳過

但是如果你想要給集合字段的某個字段聲明索引,則不太容易實現。這個時候,你可以考慮方式二。

方式二:使用RunCommand

這裏我們修改一下上面AppDbContext中Initialize方法,通過構造兩個Mongo Shell命令的方式來創建索引。

與上面不同的是,這裏我們還針對集合類型的幾個常用查詢字段創建了一個組合索引,代碼如下所示:

public static class AppDbContext
{
    /// <summary>
    /// Create indexes in MongoDB when startup
    /// NOTE: It'll skip creation when the indexes already exists.
    /// </summary>
    public static void Initialize(IApplicationBuilder app)
    {
        var dbInstance = app.ApplicationServices.GetService<IMongoDbConnection>();
        var logger = app.ApplicationServices.GetService<ILogger<MongoRepository>>();
        var db = dbInstance.DatabaseClient.GetDatabase(dbInstance.DatabaseName);
        // Index definitions
        var indexCommand1 = @"{ createIndexes: 'MyTasks', indexes: [ { key: { 'OrderNumber': 1 }, name:'Idx_OrderNumber', unique: true } ] }";
        var indexCommand2 = @"{ createIndexes: 'MyTasks', indexes: [ { key: { 'Transmissions.Type': 1, 'Transmissions.Status':1, 'Transmissions.Retries':1 }, name:'Idx_Transmission_TypeStatusRetries', unique: false } ] }";

        // Create indexes by RunCommand
        try
        {
            db.RunCommand<BsonDocument>(BsonDocument.Parse(indexCommand1));
            db.RunCommand<BsonDocument>(BsonDocument.Parse(indexCommand2));
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "{service}.{method} - throws an exception when creating indexes in database",
               nameof(AppDbContext), nameof(Initialize));
        }
    }
}

在Program.cs中使用

這裏我們僅僅需要在Program.cs中添加以下語句即可實現在ASP.NET 6應用啓動時創建MongoDB索引啦:

......
AppDbContext.Initialize(app);
......

小結

本文我們瞭解瞭如何在ASP.NET 6應用啓動時實現自動創建MongoDB的索引,相信會對你在ASP.NET 6中使用MongoDB有一定幫助!

參考資料

Kevin Smith,《Creating MongoDB indexes in ASP.NET Core 3.1

TheCodeBuzz,《Create MongoDB indexes in C#.NET Part 1

TheCodeBuzz,《Create MongoDB indexes in C#.NET Part 2

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章