ASP.NET Core で Elasticsearch
を統合する⽅法
鈴⽊ 章太郎
Elastic テクニカルプロダクトマーケティングマネージャー/エバンジェリスト
内閣官房 IT 総合戦略室 政府 CIO 補佐官
Elastic
Technical Product Marketing
Manager/Evangelist
内閣官房 IT 総合戦略室
政府 CIO 補佐官
元 Microsoft Technical Evangelist
Twitter : @shosuz
Shotaro Suzuki
このセッションの⽬的
• Elastic をご存知でしょうか︖
• 超⾼速な OSS の検索エンジンで、どこにでもデプロイができ、様々なパブリック
クラウド上でも稼働します。
• 検索の⼒を活かして監視、セキュリティソリューションも提供しています。
• 今回は、下記の内容をご紹介します。
– かなりの部分が無償で使える Elastic 概要
– Azure への Elastic Cloud デプロイ
– Visual Studio Code の Elastic プラグイン
– .NET ⽤のクライアントライブラリ
– ASP.NET Core Web アプリへの⾼度な検索機能の追加⽅法(超基本)
Elastic 概要から Elastic Cloud まで
40以上の国に従業員がいます。
ニューヨーク証券取引所に上場して
います。
3 Solutions, 1 Stack, Deploy Anywhere
Elastic スタックで実現
Kibana
Elasticsearch
Beats Logstash
Elastic エンタープライズサーチ Elastic セキュリティElastic オブザーバビリティ
3 つのソリューション
SaaS
(AWS/Azure/GCP)
IaaS
(クラウド & オンプレ)
Elastic Cloud
on Kubernetes
Elastic Cloud Elastic Cloud
Enterprise
豊富なデプロイ選択肢
Kubernetes
(クラウド & オンプレ)
Elastic Cloud on Azure デプロイ https://www.elastic.co/jp/
Elastic Cloud on Azure デプロイ https://portal.azure.com
Kibana 起動時の認証情報をダウンロード (.csv)
Elastic Cloud on Azure デプロイ https://www.elastic.co/jp
/
なぜアプリ検索に Elasticsearch が必要か
なぜ Elasticsearch なのか︖
• 企業では主にリレーショナルデータベースを使⽤してデータを格納
• テーブルを簡単に結合し、必要なデータベースからこのデータを取得できる
• しかし、時間の経過とともに、データベースとテーブルが肥⼤化して、数百万のデータセットを含む⼤規模なデータ
ベースになると、操作を実⾏できなくなる
• ⼀⽅、Elasticsearch は、数百万のドキュメントを数秒で簡単に検索できる
• Elasticsearch は、柔軟で強⼒なオープンソースの分散型リアルタイム検索
および分析エンジン
• Elasticsearch はドキュメントベースのデータベースでデータを JSON 形式で保存
• Elasticsearch は、アプリケーションの強⼒な検索ツールとして使⽤できる
• インデックス、ドキュメント、フィールド等を作成し、データを Elasticsearch にプッシュで、検索の準備が整う
• Elasticsearch の2つのユニークで重要な機能
• 1. ⽔平スケール
• 2. ⾼可⽤性
アナリストが利⽤する資産運⽤での⾼速情報検索に向け Elasticsearch を導⼊。
35種類のデータソースを Elasticsearch に集約し、圧倒的な検索パフォーマンスで業務を⽀援
https://www.elastic.co/jp/customers/smd-am
事例︓三井住友 DS アセットマネジメント株式会社
Elasticsearch for VSCode
Elasticsearch for VSCode インストール
https://marketplace.visualstudio.com/items?itemName=ria.elastic
Elasticsearch for VSCode ホスト設定
•
• https URL ユーザ パスワード
•
Elasticsearch for VSCode ホスト設定
http://user:pass@host:9200
https://elastic:oRHGj80iJUO6CF7WBUMiwyu1x@8009bf958b6w592
3b3c56983d4048df824.japaneast.azure.elastic-cloud.com:9243
Elasticsearch ドキュメント CRUD 操作
と VS Code による Demo
Elasticsearch の概念
- インデックス・ドキュメント・フィールド
•
•
•
•
Elasticsearch
の概念
具体例
SQL Server, MySQL,
PostgreSQL 等
インデックス
書籍データが
格納される
場所
テーブル
ドキュメント 書籍データ レコード
フィールド
書籍タイトル、
著者、出版⽇、
等
カラム
Elasticsearch ドキュメントの操作
•
•
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
新しいドキュメントの作成 -1
PUT books/_doc/1
{
"id": ”1",
“title”: ”Elastic ⼊⾨",
"date":”Oct 1, 2020",
"author":{
"first_name": ”Shotaro",
"last_name": ”Suzuki"
}
}
•
•
•
•
•
新しいドキュメントの作成 -2
“title”: ”Elastic ⼊⾨ - Elasticsearch/Kibana/Logstash/Beats”
“title”: ”Elastic & ASP.NET Core 3.x プログラミング”
“title”: ”Elastic による AKS Log/Metric/APM 管理”
“title”: ”Visual Studio Code による Elasticsearch プログラミング”
“title”: ”Elastic Logstash/Beats 開発と管理 徹底解説”
•
•
ドキュメントの取得 - 1
•
•
(結果)
{
"_index": "books",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"title": "Elastic 入門",
"date": "Oct 1, 2020",
"author": {
"first_name": "Shotaro",
"last_name": "Suzuki"
}
}
}
GET books/_doc/1/
ドキュメントの取得 – 2 _source のみ
•
•
(結果)
{
"title": "Elastic 入門",
"date": "Oct 1, 2020",
"author": {
"first_name": "Shotaro",
"last_name": "Suzuki"
}
}
GET books/_doc/1/
ドキュメントの検索 – 1 全てのデータを検索
•
•
•
took – Elasticsearchが検索を実⾏するのにミリ秒単位の時間
timed_out –検索がタイムアウトしたかどうかを⽰します
_shards –検索されたシャードの数と、検索されたシャードの成功/失敗の数を⽰す
hits –検索結果
hits.total –検索条件に⼀致するドキュメントの総数
hits.hits –検索結果の実際の配列(デフォルトは最初の10ドキュメント)
hits.sort -結果のソートキー(スコアでソートする場合は⽋落)
POST books/_doc/_search
{
”query": {
“match_all” : {}
}
}
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
ドキュメントの検索 – 2 シャードとは
•
•
•
•
• コンテンツボリュームを⽔平に分割/スケーリングすることが可能
• シャード間(場合によっては複数のノード上)で操作を分散および並列化できるため パフォーマ
ンス/スループットが向上
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
ドキュメントの特定のデータの検索 – クエリ DSL とは︖
•
•
•
•
POST books/_doc/_search
{
"query": {
"match" : {
"title" : "Elasticsearch"
}
}
}
Elasticsearch データをクエリする⽅法はたくさんあります。詳細についてはこちら。
https://www.elastic.co/guide/en/elasticsearch/guide/current/_most_important_queries.html
ドキュメントの更新 - 1(データの変更)
•
POST books/_doc/1/_update
{
"doc": {
"title": "Elastic ⼊⾨ -
Elasticsearch/Kibana/Logstash/Beats"
}
}
PUT books/_doc/1
{
"title": "Elastic ⼊⾨ –
Elasticsearch/Kibana/Logstash/Beats",
"date": "Oct 21, 2020",
"author": {
"first_name": "Taro",
"last_name": "Sato"
}
}
ドキュメントの削除(データの削除)
•
• DELETE books
DELETE books/_doc/3
ASP.NET Core アプリとの統合
(最も単純なモデル)
.NET Core に基づくソリューションを実装する
• NEST パッケージをインストール
• NEST を使⽤すると、
• ドキュメントのインデックス作成と検索、
• ノードとシャードの管理、等々
すべての Elasticsearch 機能を
ネイティブに使⽤できる
dotnet add package NEST
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/index.html
ElasticsearchExtensions クラスを作成
• NEST プラグインを管理
public static class ElasticsearchExtensions
{
public static void AddElasticsearch(this IServiceCollection services,
IConfiguration configuration)
{
var url = configuration["Elasticsearch:url"];
var defaultIndex = configuration["Elasticsearch:index"];
var settings = new ConnectionSettings(new Uri(url))
.DefaultIndex(defaultIndex);
AddDefaultMappings(settings);
var client = new ElasticClient(settings);
services.AddSingleton(client);
CreateIndex(client, defaultIndex);
}
appsettings.json
•
•
•
https://elastic:oRHGj80iJUO6CF7WBUMiwyu1x@8009bf958b6w59
23b3c56983d4048df824.japaneast.azure.elastic-cloud.com:9243
"Elasticsearch": {
"index": "products",
"url": "http://localhost:9200/"
}
Product オブジェクト
public class Product
{
public int Id { get; set; }
public string SerialNo { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Brand { get; set; }
public string Category { get; set; }
public string Price { get; set; }
public int Quantity { get; set; }
public float Rating { get; set; }
public DateTime ReleasedDate { get; set; }
}
Product オブジェクト 個別索引付け
public async Task SaveIndexSingleAsync(Product product)
{
if (_cache.Any(p => p.Id == product.Id))
{
await _elasticClient.UpdateAsync<Product>(product,
u => u.Doc(product));
}
else
{
_cache.Add(product);
await _elasticClient.IndexDocumentAsync(product);
}
}
• _cache 配列を使⽤して製品リストをさらにキャッシュ
• _cache 配列のチェックを通じてドキュメントの挿⼊と変更の両⽅を管理
Product オブジェクト リスト索引付け
public async Task SaveIndexManyAsync(Product[] products)
{
_cache.AddRange(products);
var result = await _elasticClient.IndexManyAsync(products);
if (result.Errors)
{
// この応答はエラーで検知できる
foreach (var itemWithError in result.ItemsWithErrors)
{
_logger.LogError
("ドキュメントのインデックス化に失敗しました {0}: {1}",
itemWithError.Id, itemWithError.Error);
}
}
}
• _cache 配列を使⽤して製品リストをさらにキャッシュ
Product オブジェクト バルク索引付け
public async Task SaveIndexBulkAsync(Product[] products)
{
_cache.AddRange(products);
var result = await _elasticClient.BulkAsync(b =>
b.Index("products").IndexMany(products));
if (result.Errors)
{
// この応答はエラーで検知できる
foreach (var itemWithError in result.ItemsWithErrors)
{
_logger.LogError
("ドキュメントのインデックス化に失敗しました {0}: {1}",
itemWithError.Id, itemWithError.Error);
}
}
}
• ⼤量のドキュメントをより短い時間でインデックスに登録できログを挿⼊する際のエラーを管理できる
Product オブジェクト 削除
public async Task DeleteIndexAsync(Product product)
{
await _elasticClient.DeleteAsync<Product>(product);
if (_cache.Contains(product))
{
_cache.Remove(product);
}
}
NuGet Package Bogus
https://github.com/bchavez/Bogus
• 多くの概念実証(PoC)等で、頻繁
に必要とされるものの1つがテストデータ
• 顧客が期待するものに似たオブジェクト
の⼤規模なコレクションの⽣成機能
• 明らかな理由で、お客様のデータベースのコピーを取得したり、
Azure リソースにアクセスしたりすることはできない
• そのため、コードのテストに使⽤できる偽の、しかしライブのよう
なデータを⽣成するための効率的で信頼性が⾼く、⾼速な
⽅法が必要
• .NET/ .NET Core 両⽅をサポート
dotnet add package Bogus --version 31.0.3
NuGet Package Bogus による Fake データ⽣成
[HttpGet("fakeimport/{count}")]
public async Task<ActionResult> Import(int count = 0)
{
var productFaker = new Faker<Product>()
.CustomInstantiator(f => new Product())
.RuleFor(p => p.Id, f => f.IndexFaker)
.RuleFor(p => p.Ean, f => f.Commerce.Ean13())
.RuleFor(p => p.Name, f => f.Commerce.ProductName())
.RuleFor(p => p.Description, f => f.Lorem.Sentence(f.Random.Int(5, 20)))
.RuleFor(p => p.Brand, f => f.Company.CompanyName())
.RuleFor(p => p.Category, f => f.Commerce.Categories(1).First())
.RuleFor(p => p.Price, f => f.Commerce.Price(1, 1000, 2, "€"))
.RuleFor(p => p.Quantity, f => f.Random.Int(0, 1000))
.RuleFor(p => p.Rating, f => f.Random.Float(0, 1))
.RuleFor(p => p.ReleaseDate, f => f.Date.Past(2));
var products = productFaker.Generate(count);
await _productService.SaveIndexManyAsync(products.ToArray());
return Ok();
}
開発の⽬的で ReIndex メソッドを実装
• インデックス上のすべてのドキュメントを削除して、1つずつ再度インポートできる
• 既存のドキュメントとロードされていないドキュメントのリストをインポートする場合に役⽴つ
//開発用途
[HttpGet("/search/reindex")]
public async Task<IActionResult>ReIndex()
{
await _elasticClient.DeleteByQueryAsync<Product>(q => q.MatchAll());
var allProducts = (await _productService.GetProducts(int.MaxValue)).ToArray();
foreach (var product in allProducts)
{
await _elasticClient.IndexDocumentAsync(product);
}
return Ok($"{allProducts.Length} product(s) reindexed");
}
Demo
ASP.NET Core アプリへの
組み込み(最も単純なモデル)
まとめ
•
•
•
•
•
•
アプリケーション開発 オンデマンド ウェビナー特集
https://www.microsoft.com/ja-jp/events/top/apps-innovation-webinars.aspx#primaryR7
Elastic Meetup(次回予定)10.29
https://www.meetup.com/ja-JP/Tokyo-Elastic-Fantastics/
https://www.elastic.co/elasticon/security/japan-jp
基調講演担当します︕
11月のWebinar (隔週)
11⽉11⽇(⽔)| 12:00~13:00
DevRel ASIA 2020 11.14
https://devrel.dev/asia-2020/speakers/shotaro/
VS Code Conference Japan 11.21
https://vscode.connpass.com/event/184441
Thank you for your attention!
他の事例
オンライン決済サービスの監視を⽀える Elastic Stack
ECサイト向けに様々な決済⼿段を提供 / 加盟店に決済画⾯は決済APIを提供するシステム(年間取引額2兆円、2億件以上のトランザクション)
https://www.elastic.co/jp/customers/softbank-payment-service
事例︓ソフトバンク・ペイメント・サービス株式会社
オンライン決済サービスの状況をほぼリアルタイムで可視化
• ダッシュボードで、成功した決済はグリーン、失敗したものはレッド、決済手段ごと
に OK/NG の比率の推移を俯瞰。「1. グリーンが急激に少なくなったら要注意。2. レッ
ドが急激に多くなったら要注意。」
• 誰でも、いつでも、どこでも、簡単に、サービスの状況を把握
• 障害以外でも、爆売れ、不正利用といった、加盟店の変化に気がつく
• エンジニアの決済トランザクションへの関心が高まる
Machine Learning で決済トランザクションの異常を検知
• 決済トランザクションの合計の日次の増減を3周期で学習し、それが予測した範
囲から大きく外れれば、異常として検出
• 可視化だけでは埋蔵してしまう変化を ML にて新たに発見
可視化をビジネスデータにまで広げる
• 2年分の売上推移を部署別・案件別に表現。ヒートマップで営業部門や個人の目
標達成率を表現。年間の加盟店契約の獲得状況を都道府県マップに表示
• サービスサイト閲覧状況を、アクセス元の IP アドレスから企業名を推定し、契約
済み企業からのアクセス、未契約企業からのアクセスランキングを作成
• 非エンジニアでもデータ投入からダッシュボードの作成までできる
事例︓株式会社リコー
すべてのログを Elastic Stack 上に集約。35 ノードで1 ⽇2TB のログを監視
「リコーグループ全システムの IT デバイスで発⽣する1 ⽇2 テラバイトにおよぶログが、すべて Elastic Stack に送り込まれます。これを35ノード、約400テラバ
イトのクラスターで処理します。インデックス数は約10,000 でサイズは約250 テラバイト。ドキュメント数は3,450 億にも及びますが、これは昨年12 ⽉の話で、
現在はさらに増えている状況です」と話す。Elastic Stack の製品については、Elasticsearch、Logstash、Kibana、Filebeat、Packetbeat、
Winlogbeat、Monitoring、Alerting 等が使⽤されており、特に⾒える化でキーとなる Kibana については、セキュリティ統括部のオフィス室内で、⼤型
モニターに常時チャートが表⽰され、担当者がシステムの状況をリアルタイムかつ直感的に把握できるようになっている。
https://www.elastic.co/jp/customers/ricoh
事例︓ブローダービズ株式会社
機械学習を駆使して時系列の映像データを解析。⾷品製造⼯場での作業を"⾒守り"、
異常⾏動発⽣をリアルタイムに検知してトラブルを回避
https://www.elastic.co/jp/customers/broaderbiz
⾷品偽装防⽌と働き⽅改⾰
課題
•⻑時間にわたる⼯場作業を、管理者の⽬のみによって監視し
続けることは容易ではない
•担当者は、制服、帽⼦、マスクを着⽤しているため、⾒た⽬だ
けでは個⼈の特定が困難
•定量化された数値などによって状況を把握・記録することができ
ず、過去と照らし合わせることも困難
•録画されたビデオ映像は、問題が発⽣した後の証跡として利⽤
できるが、瞬時の対応には不向き
Solution
⼈⼿による作業を、AI を駆使したシステムによって“⾒守り”、担
当者の姿勢やある時間内での⾏動を、数値化された統計値と
の⽐較し、通常の⾏動を逸脱した異常事態の発⽣を瞬時に判
断することで、トラブルの拡⼤を未然に防ぐという仕組みの実現。
NEDO(国⽴研究開発法⼈新エネルギー・産業技術総合開
発機構)の共同開発⽀援事業として認められた。
事例︓⽇本経済新聞社
⽇経電⼦版の記事検索およびログ解析の両⽅を1つの仕組みで実現
利⽤範囲の広さで Elasticsearch を採⽤
Elasticsearch を選定したのは、記事検索とログの可視化の両⽅に
使える利⽤範囲の広さが最⼤の理由
15台のクラスタ構成で、1秒あたり9000リクエスト以上という⾼
い性能を発揮
記事検索とログ解析に Elasticsearch を活⽤
⽇経電⼦版の記事は、5年分で約200万件、データサイズは5GB。記事の更新は、1⽇あたり数千回、ピーク時には1分間に300件程度の記事が更
新。検索における利⽤者からのアクセスは、1秒あたり100回程度だが、1秒あたり数千回のアクセスも想定
ログ解析で Elasticsearch を利⽤した最⼤のメリットは、専⾨的な深い知識がなくてもログ解析が可能になること。Elasticsearch と Kibana を利⽤
したログ解析により、たとえば、記事検索のレスポンスにどれくらいの時間がかかっているかを容易に解析でき、アプリケーションのどこにボトルネックがあるのか、
改善するべき機能はどこなのかを迅速に把握できる
https://www.elastic.co/jp/customers/nikkei
“Elasticsearch と Kibana を活⽤することで、エンジニア秘伝の“タレ( shell 芸)”を使って可視化していたログ解析を URL や、
画像で即座に社内共有できるようになりました。これまではログ解析ができるエンジニアが2⼈しかいなかったのですが、Elasticsearch と
Kibana を利⽤することで、秘伝のタレが不要になり、ログ解析ができるエンジニアを6⼈以上に増やすことができましたようになりました。"
– 日本経済新聞社 デジタル編成局 編成部 梅崎裕利氏
事例︓スイス・ライフ
Elasticsearch が⽣み出す360°の顧客管理
Elasticsearch で1000万⼈の顧客データのインデックス作成から分析、パブリッシュまでリアルタイムに実⾏
スイス・ライフにおける課題
スイス・ライフは保険や資産管理サービスを⼿掛ける⼤⼿企業で、フランスでは個⼈向け保険商品を主⼒に⾼いシェアを持つ
スイス・ライフ・フランスの1000万⼈の顧客情報に対し、さまざまなフォーマットや、さまざまなタイプのユーザアクセスにより、データへの均質なアクセス
を維持することが難しくなり、情報が「サイロ化」していた
Elasticsearchによる解決
スムーズな運⽤と、情報への均質なアクセスを実現させるため、スイス・ライフ・フランスは Elasticsearch で顧客データのインデックスとパブリッシュを⾏った
あらゆる顧客の窓⼝となる Web サイトとモバイルアプリケーションにデータを提供するため、まず Elasticsearch ですべての顧客データを1か所に集約
顧客記録、契約データ、マーケット分類データ、年⾦と保険スコアのすべての情報を横断してリアルタイムにクエリ。
すべての顧客がポータルサイトにアクセスでき、顧客情報と契約情報をすばやく取得できる機能を提供。また、情報が更新された場合、ソースシステムで
10秒以内にインデックスを作成。
https://www.elastic.co/jp/customers/swiss-life
“スピードと信頼性は不可⽋です。Elasticsearch がデータをリアルタイムにインデックスするようになり、すべてが進
化しました。"
– クリスチャン・ファン・チョン, スイス・ライフ・フランス、チーフエンタープライズアーキテクト
事例︓PSCU(⽶国有数の信⽤組合サービス組織)
Elastic は信⽤組合に対する数百万ドル相当の不正⾏為被害を防⽌することでリスク回避を実現
Elastic 製システムをデプロイしてからわずか18か⽉間で、3,500万ドルもの不正⾏為を阻⽌
PSCU における課題
PSCUは、⽶国有数の信⽤組合サービス組織で1,500の信⽤組合にサービスを提供し、年間38億件もの取引を扱う。
会員に対して⽀払い処理、リスク管理、データ分析、オンラインバンキング、モバイルプラットフォームなどさまざまな⾦融サービスを提供
年⽉が経つにつれデータベースが巨⼤化しデータ⼊⼒が困難になり、前⽇のデータを読み込むのに丸1⽇かかる状態で適切なタイミングで不正検
知ができていなかった。
Elasticsearch による解決
会員のオンラインログイン、IP アドレス、住所、サポートセンターへの問い合わせ履歴など、数多くのデータソースを Elasticsearch に集約しログを可視化
当初は内部の不正⾏為検知が⽬的であったが問題なく稼働したため、さらに全⽶1500の信⽤組合の外部からの不正⾏為を阻⽌できるよう、幅広い⾦
融関連データソースからのログを追加
機械学習を使⽤した不正検知を導⼊し導⼊後18ヶ⽉で3,500万ドルもの不正⾏為を阻⽌
単なる不正検知にとどまらず、災害発⽣時の地域で検知された平時と異なる⾏動(⾼価な発電機、⼤量の⽸詰などを突然購⼊するなど)をブロックし
ない機能なども追加
https://www.elastic.co/jp/customers/pscu
"Elastic Stackを構築したことで、不正⾏為を従来よりもはるかに簡単に検知できるようになりました。Elasticプラットフォームに⼊⼒
するデータベースを⼤幅に増やしたことで、従来は決して気付かなかったデータの特徴をとらえられるようになりました。現在では、発⽣中
の不正⾏為を検知できるだけでなく、発⽣前にそれを検知できるようになりました。"
– ジョナソン・ロビンソン氏, PSCU、不正インテリジェンス部門マネージャー
事例︓古野電気株式会社
船舶運航の安全・安⼼・効率化を⽀えるサービスを提供
データ・分析結果を得るまでの平均時間(MTTR)の短縮率 94%
差別化されたカスタマーエクスペリエンス
古野電気は、同社の Elastic オブザーバビリティソリューショ
ンの⼀部として Kibana と Elastic Maps を採⽤したこと
で、船上でのデータ使⽤に関するデータ・分析結果をより多
く得られるようになっています。それらのデータ・分析結果は、
お客様の船舶と陸上の接続コストとパフォーマンスの改善に
役⽴ちます。
コストパフォーマンスに優れたプロアクティブなサービスを
提供
今後発⽣しそうな機器の障害、予兆を検知し、予防保守
を⾏う必要があれば、修理内容を特定し、部品を迅速に
⼿配することで費⽤を抑えながら効果的な対応をご⽀援し
ます。
安全で効率的な航⾏を実現
古野電気は、Elastic Cloud の Elasticsearch
Service を活⽤することで、ライフサイクルソリューションを効
果的に多くのお客様へ展開することができます。また、お客
様のニーズにより最適なサービスプランをパッケージ化し販売
することで、安全で効率的な航⾏へ貢献します。
https://www.elastic.co/jp/customers/furuno

Integrating elasticsearch with asp dot net core