當前位置: 妍妍網 > 碼農

Elastic學習之旅 (11) .NET 6套用整合ES - 上

2024-04-20碼農

大家好,我是Edison。

上一篇:

寫在開頭

有了前面10篇的基礎,我們大概清楚了ES的基本概念和使用(主要是查詢),這也是我們作為Developer應該了解的範疇,而至於更深入的聚合分析、運維管理就不在本系列的範疇中。有童鞋說,講講如何在.NET套用中整合和使用ES吧,額,這個其實網上也有很多教程了。不過你既然提了,那還是得安排上。

在.NET套用中整合ES一般涉及兩個方面:

(1)將ES當儲存用,類似於MongoDB,做文件的增刪查改,這一類操作偏CRUD。

(2)對ES中的數據做查詢分析,聚合統計、分組等等,這一類操作偏查詢分析。

針對這兩類操作,我們今天先來搞定CRUD!

準備工作

建立一個Web API應用程式,這裏我選擇的是.NET 6版本。

計畫結構如下圖所示:

其中,我們對Elastic的整合主要放在了Infrastructure目錄中,裏面存放了我們定義的Repository進而幫助我們在Controller中使用其提供的持久化能力。

在appsetting中我們定義了如下配置指明Elastic的相關資訊(URL地址 和 預設索引):

{ ......"Elastic": {"ServerUrl": "http://edt-elastic:9200","DefaultIndex": "app-logs" }}

定義模型

我們這裏定義一個Model,假設叫AppLog,來模擬日誌文件的CRUD到ES:

public classAppLog : ElasticModelBase{publicstring LogLevel { get; set; } = "Info";publicstring Message { get; set; } = string.Empty;public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.Now;publicAppLog() {this.Id = Guid.NewGuid().ToString(); }publicAppLog(string id) {this.Id = id; }}public classElasticModelBase{publicvirtualstring Id { get; set; }}

定義ElasticProxy

我們知道,.NET套用要操作Elastic得使用Elastic的.NET客戶端,因此按照國際慣例我們對其進行一個小封裝,以便於可以在Repository中使用。

對於ES 7.x版本的集群,推薦使用NEST這個客戶端包(目前已停止更新):

dotnetaddpackageNEST

對於ES 8.x及以上版本的集群,則推薦使用Elastic.Clients.Elasticsearch:

dotnet add package Elastic.Clients.Elasticsearch

這裏因為我自己搭的實驗ES集群環境是7.x版本,所以我使用的實NEST,不過操作都是類似的。

在Infrastructure目錄下,定義一個ElasticProxy用於獲取Elastic的.NET客戶端:

publicinterfaceIElasticProxy{IElasticClient GetClient(string indexName = null);}public classElasticProxy : IElasticProxy{privatereadonlystring ElasticServerUrl;privatereadonlystring DefaultIndexName;publicElasticProxy(IConfiguration configuration) { ElasticServerUrl = configuration["Elastic:ServerUrl"] ?? thrownew ArgumentNullException(); DefaultIndexName = configuration["Elastic:DefaultIndex"] ?? thrownew ArgumentNullException(); }public IElasticClient GetClient(string indexName = null) {var settings = new ConnectionSettings(new Uri(ElasticServerUrl)) .DefaultIndex(indexName ?? DefaultIndexName);returnnew ElasticClient(settings); }}

定義ElasticRepository基礎類別

為了方便後續定義具體的Repository,我們這裏先定義一個ElasticRepository的基礎類別,幫我們把CRUD操作做一個基礎的實作。

public interface IElasticRepositoryBase<T> where T : ElasticModelBase{Task AddAsync(T item);Task UpdateAsync(T item);Task DeleteAsync(string id); Task<Tuple<int, IList<T>>> QueryAsync(int page, int limit);}public abstract classElasticRepositoryBase<T> : IElasticRepositoryBase<T> where T : ElasticModelBase{private readonly IElasticProxy _elasticProxy;publicElasticRepositoryBase(IElasticProxy elasticProxy){ _elasticProxy = elasticProxy; }protected abstract string IndexName { get; }protected IElasticClient Client => _elasticProxy.GetClient(IndexName);publicvirtual async Task AddAsync(T item){ await this.Client.IndexAsync(item, x => x.Index(this.IndexName)); }publicvirtual async Task DeleteAsync(string id){ await this.Client.DeleteAsync<T>(id, x => x.Index(this.IndexName)); }publicvirtual async Task UpdateAsync(T item){ await this.Client.UpdateAsync<T>(item.Id, x => x.Index(this.IndexName).Doc(item)); }publicvirtual async Task<Tuple<int, IList<T>>> QueryAsync(int page, int limit) { var query = await this.Client.SearchAsync<T>(x => x.Index(this.IndexName) .From((page -1) * limit) .Size(limit));returnnew Tuple<int, IList<T>>(Convert.ToInt32(query.Total), query.Documents.ToList()); }}

有了上面的基礎類別,我們再去實作具體的Repository就方便的多啦。

實作AppLogRepository

下面我們可以很方便的實作一個AppLogRepository啦,是不是很方便!

public classAppLogRepository : ElasticRepositoryBase<AppLog>, IAppLogRepository{public AppLogRepository(IElasticProxy elasticProxy) : base(elasticProxy) { }protectedoverride string IndexName => "app-logs";}

定義依賴註入

下面我們把要用到的ElasticProxy和Repository註入到ServiceCollection中,後面Controller就可以用了!這裏我們在Infrastructure中實作一個擴充套件方法,便於在Program.cs中使用。

publicstatic classInfraServiceExtensions{publicstaticvoidAddApplicationInfrastructure(this IServiceCollection services) { services.AddSingleton<IElasticProxy, ElasticProxy>(); services.AddSingleton<IProductRepository, ProductRepository>(); services.AddSingleton<IAppLogRepository, AppLogRepository>(); }}

然後在Program.cs中使用這個擴充套件方法:

......// Add infra services / repositoriesbuilder.Services.AddApplicationInfrastructure();

實作AppLogController

下面我們就可以快速實作一個Controller來呼叫Repository實作ES的CRUD操作啦:

[ApiController][Route("[controller]")]public classAppLogController : ControllerBase{privatereadonly IAppLogRepository _appLogRepository;publicAppLogController(IAppLogRepository appLogRepository) { _appLogRepository = appLogRepository; } [HttpGet]publicasync Task<IActionResult> QueryAsync(int page = 1, int limit = 10) {var result = await _appLogRepository.QueryAsync(page, limit);return Ok(new { total = result.Item1, items = result.Item2 }); } [HttpPost]publicasync Task<IActionResult> AddAsync([FromBody] AppLog log) {await _appLogRepository.AddAsync(log);return Ok("Success"); } [HttpPut]publicasync Task<IActionResult> UpdateAsync([FromBody] AppLog log) {await _appLogRepository.UpdateAsync(log);return Ok("Success"); } [HttpDelete]publicasync Task<IActionResult> DeleteAsync([Required] string id) {await _appLogRepository.DeleteAsync(id);return Ok("Success"); }}

快速測試

下面我們就可以開啟Swagger來做一個測試啦:

透過Post方式建立一個applog,然後我們透過Get方式查詢一下結果如下:

這時我們還可以透過在ES中透過Query DSL來驗證一下:

GET app-logs/_search

查詢結果如下圖所示:

至於Put和Delete就留給你去驗證吧。

小結

本篇,我們了解了如何在ASP.NET 6套用中整合ElasticSearch,並透過範例了解了如何做CRUD操作,並透過一定的封裝提高了復用性。

下一篇我們了解下如何在ASP.NET 6套用對ES中的數據進行查詢 和 聚合。

源碼

Github: https://github.com/Coder-EdisonZhou/ElasticSamples

參考資料

部落格園,包子wxl,【ElasticSearch使用系列-.NET6對接ES】:https://www.cnblogs.com/wei325/p/15881650.html

CSDN,阿星Plus,【.NET Core下使用ES】:

https://blog.csdn.net/meowv/article/details/108613494

CSDN,風神.NET,【如何在ASP.NET Core中整合ES】:https://blog.csdn.net/WuLex/article/details/123354106

極客時間,阮一鳴,【ElasticSearch核心技術與實戰】

年終總結:

數位化轉型:

C#刷題:

.NET面試:

.NET大會: