.NET Core + MongoDB .NET Driverを使用したWeb APIアプリケーションの作成

既にご存じのとおり、MongoDBは最も開発されたオープンソースのNoSQLソリューションの1つであり、ドキュメント指向のデータベースであり、クロスプラットフォームであり、高いパフォーマンス、可用性、スケーリングの容易さも提供します。

最新のオープンソースWebアプリケーションでは、NoSQLソリューションの使用は、非リレーショナル動作のために人気を博しています。 この記事では、MongoDBのコレクションに適用可能なCRUD操作をサポートする単純なASP.NET Core Web API「ノートブック」アプリケーションを段階的に実装します。

なぜmongodb


DBMSの選択は、主に作成するアプリケーションによって異なります。 つまり データベースは開発者によって選択されるのではなく、製品自体によって選択されます。 ドキュメントを操作するには、MongoDBが最適です。 このようなアプリケーションの印象的な例は、ブログやソーシャルネットワークで、各作成者が投稿を作成し、画像、ビデオ、音楽を追加できます。 他のユーザーは、これらのエントリにコメントしたり、「いいね」や「星」などのシステム評価を使用して評価したりできます。 しかし、このデータをどのように保存しますか? リレーショナルDBMSに精通している場合、同様のスキームがどのようになるか想像できます。

画像

単一のレコードの構造と、その表示方法を想像してみましょう。 レコードとそれに関連付けられたデータ(画像、音声、ビデオ、コメント、評価、ユーザー情報など)を取得するには、8つのテーブル接続について照会する必要があります。 ここで、処理されるレコードのストリーム(動的にロードされ、数千のクライアントに転送される)を想像すると、このタスクには多数のテーブル結合に対する数千のクエリが必要であることがわかります。 もちろん、データストレージには、SQL Serverなどのリレーショナルデータベースを使用できます。 SQLはJSON形式の動的データをサポートします 。 ただし、この特定のシナリオのアプローチを簡素化するオプションがあります-これはNoSQLデータベースです。 以下に示すように1つのドキュメントを使用してMongoDBに保存すると、アプリケーションのパフォーマンスを向上させ、テーブル結合のない単一のクエリで完全にレコードを取得できます。 これは、よりシンプルで効率的な方法です。

{ "id":"ew12-res2-234e-544f", "title":"post title", "date":"2016-01-01", "body":"this is an awesome post stored on NoSQL", "createdBy":"User", "images":["http://example.com/myfirstimage.png","http://example.com/mysecondimage.png"], "videos":[ {"url":"http://example.com/myfirstvideo.mp4", "title":"The first video"}, {"url":"http://example.com/mysecondvideo.mp4", "title":"The second video"} ], "audios":[ {"url":"http://example.com/myfirstaudio.mp3", "title":"The first audio"}, {"url":"http://example.com/mysecondaudio.mp3", "title":"The second audio"} ] } 

さらに、MongoDBを使用すると、開発者は高可用性と低遅延のアプリケーションを作成することで一貫性を緩めることができます。

MongoDBをリレーショナルデータベースの代替と呼ぶことは完全に正しいわけではなく、むしろ代替であることに注意してください。 このツールは、他の多くの人ができることと同じことを行うことができます-何かが優れていますが、そうではありません。

MongoDBの非構造化コレクションアーキテクチャは、開発者がデータレベルのスキーマを定義および保守することを義務付けていないため、製品開発中に迅速な反復が可能です。 オブジェクトを保存する必要がありますか? BSONにシリアル化し、MongoDBに送信します。 プロパティまたはタイプのマッピングはありません。

このシンプルさは、究極の開発者として間違いなくあなたに合っているでしょう。

バージョン1.8より前は、MongoDBはストレージに関して信頼性が低いと見なされていましたが、このバージョンではロギングメカニズムが追加され、デフォルトですでに有効になっていますが、構成ファイルで無効にできます。 パフォーマンスはわずかに向上しますが、ロギングメカニズムが無効になっていると、データ損失のリスクがあります。

技術スタック


MongoDBは、BSON(Binary JavaScript Object Notation)形式のドキュメントとデータベーススキーマを使用します。これは、追加の正規表現、バイナリデータ、日付などのデータを保存するためのJSON形式のスーパーセットです。 MongoDBのスキーマはコレクションと呼ばれ、このスキーマのエントリはドキュメントと呼ばれます

画像

ASP.NET Core Web APIは、HTTPサービスとして使用でき、デスクトップコンピューターから携帯電話まで、あらゆるクライアントアプリケーションで使用でき、Windows、MacOS、Linuxにもインストールできるため、大きな利点があります。

:現在、MongoDBはEF Coreではサポートされていません。 EF Coreの将来のバージョンが(Rowan Millerのコメントに基づいて)MongoDBをサポートする可能性はほとんどありませんが、この機能は将来実装できます。

以下は、操作に必要なすべてのコンポーネントです。


MongoDBの構成


MongoDBをインストールしたら 、データベースへのアクセスを構成するとともに、データの場所を示す必要があります。 これを行うには、 mongod.cfgファイルを作成します。 このファイルには、MongoDBサーバーデータフォルダーへのパスとMongoDBログファイルが含まれます。最初は認証なしです。

 systemLog: destination: file path: "C:\\data\\db\\log\\mongo.log" logAppend: true storage: dbPath: "C:\\data\\db" 

注:デフォルトでは、MongoDBはアドレス"C:\\data\\db"でデータベースディレクトリの初期化を試み、このアドレスを特定できない場合はエラーをスローします。

次に、コマンドラインで次のコマンドを実行します(構成ファイルの場所とMongoDBがインストールされているフォルダーの場所に応じてアドレスを更新する必要があります)。
"C:\Program Files\MongoDB\Server\3.4\bin\mongod.exe" --config C:\Dev\mongod.cfg
このコマンドは、MongoDBサーバーを起動し、作成済みの構成ファイルを指定します。

注:バイナリへの絶対パスを指定することはお勧めできません。 このアドレスを集中的に使用するには、PATHシステム変数にディレクトリパスを追加することをお勧めします。

サーバーが起動したら(ログファイルで詳細を確認できます)、コマンドラインからmongo.exeクライアントを実行します。

画像
注:クライアントは、アクセス制御が設定されていないという警告を発行する場合があります(これは後で修正します)

画像

コンソールクライアントで次のコマンドを実行して、管理者アカウントを作成します。

 use admin db.createUser( { user: "admin", pwd: "abc123!", roles: [ { role: "root", db: "admin" } ] } ); exit; 

次に、サーバーを停止し、セキュリティパラメーターを含むmongod.cfg構成ファイルを更新します。

 systemLog: destination: file path: "C:\\data\\db\\log\\mongo.log" logAppend: true storage: dbPath: "C:\\data\\db" security: authorization: enabled 

これからは、 adminユーザーを使用してMongoDBに接続します。

:通常の操作ではスーパーユーザーの役割(この場合は管理者)を使用しないことをお勧めしますが、単純さを維持するために、引き続き1人のユーザーのみを使用します。

ASP.NET Web APIプロジェクトの作成


完成した実装はGitHubからダウンロードできます

Visual Studio、ファイル→新規プロジェクト→.Net Core→ASP.NET Core Webアプリケーションを起動します。

画像

次に、Web APIテンプレートを選択し、[OK]をクリックします。

画像

プロジェクト構成


サポートされているいくつかの構成ファイル形式(JSON、XML、またはini)があり、デフォルトでは、Web APIプロジェクトテンプレートはJSON形式の構成でデプロイされます。 AppSettings.jsonファイルを更新して、データベース接続情報を追加します。

 { "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "Microsoft": "Information", "System": "Information" } }, "MongoConnection": { "ConnectionString": "mongodb://admin:abc123!@localhost", "Database": "NotesDb" } } 

依存性注入とパラメーターモデル


コンストラクター注入は、最も一般的な依存性注入アプローチの1つです。 ASP.NET Coreはソリューションでコンストラクターインジェクションを使用するため、これも使用します。 データベースに接続するためのパラメーターをマップするには、新しいクラスの設定を追加します。

 namespace NotebookAppApi.Model { public class Settings { public string ConnectionString; public string Database; } } 

Startup.csファイルのConfigureSettingsメソッドを変更して、パラメーターアクセスモデルに設定を追加します。 将来的には、IOptionsインターフェイスを使用して設定にアクセスします。

 public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); services.Configure<Settings>(options => { options.ConnectionString = Configuration.GetSection("MongoConnection:ConnectionString").Value; options.Database = Configuration.GetSection("MongoConnection:Database").Value; }); } 

MongoDB .NETドライバー


MongoDBに接続するには、MongoDB.DriverというパッケージをNugetプロジェクトに追加します。 これは、ASP.NET Coreでアプリケーションを完全にサポートする.NETの公式ドライバーです。

画像

モデル


ノートブックの各ノートを説明するクラスモデル(POCO)を以下に示します。

 using System; using MongoDB.Bson.Serialization.Attributes; namespace NotebookAppApi.Model { public class Note { [BsonId] public string Id { get; set; } public string Body { get; set; } = string.Empty; public DateTime UpdatedOn { get; set; } = DateTime.Now; public DateTime CreatedOn { get; set; } = DateTime.Now; public int UserId { get; set; } = 0; } } 

データベースコンテキスト


NoteContextクラスに作業単位パターンを実装します。

 public class NoteContext { private readonly IMongoDatabase _database = null; public NoteContext(IOptions settings) { var client = new MongoClient(settings.Value.ConnectionString); if (client != null) _database = client.GetDatabase(settings.Value.Database); } public IMongoCollection Notes { get { return _database.GetCollection("Note"); } } } 

リポジトリ


リポジトリインターフェイスを使用して、メモの管理に必要な機能を実装します。 また、アプリケーション(コントローラーセクションなど)に簡単にアクセスできるように、依存性注入(DI)も使用します。

  public interface INoteRepository { Task<IEnumerable<Note>> GetAllNotes(); Task<Note> GetNote(string id); Task AddNote(Note item); Task<DeleteResult> RemoveNote(string id); //   (body)  Task<UpdateResult> UpdateNote(string id, string body); } 

データベースへのアクセスは非同期になります。 完全な非同期スタックが追加されたバージョン2.0以上のドライバーを使用します。 例として、すべてのメモを取得するために、非同期リクエストを作成します。

 public async Task<IEnumerable<Note>> GetAllNotes() { return await _context.Notes.Find(_ => true).ToListAsync(); } 

以下は、基本的なCRUD操作の完全な実装です。

 namespace NotebookAppApi.Data { public class NoteRepository : INoteRepository { private readonly NoteContext _context = null; public NoteRepository(IOptions<Settings> settings) { _context = new NoteContext(settings); } public async Task<IEnumerable<Note>> GetAllNotes() { return await _context.Notes.Find(_ => true).ToListAsync(); } public async Task<Note> GetNote(string id) { var filter = Builders<Note>.Filter.Eq("Id", id); return await _context.Notes .Find(filter) .FirstOrDefaultAsync(); } public async Task AddNote(Note item) { await _context.Notes.InsertOneAsync(item); } public async Task<DeleteResult> RemoveNote(string id) { return await _context.Notes.DeleteOneAsync( Builders<Note>.Filter.Eq("Id", id)); } public async Task<UpdateResult> UpdateNote(string id, string body) { var filter = Builders<Note>.Filter.Eq(s => s.Id, id); var update = Builders<Note>.Update .Set(s => s.Body, body) .CurrentDate(s => s.UpdatedOn); return await _context.Notes.UpdateOneAsync(filter, update); } } } 

DIモデルを使用してNoteRepositoryにアクセスするには、ConfigureServicesに行を追加します。

 services.AddTransient<INoteRepository, NoteRepository>(); 

ここで:

メインコントローラー


以下は、サードパーティのアプリケーションで利用可能なすべてのCRUDメソッドが実装されているWeb APIアプリケーションのメインコントローラーのコードです。

:GetメソッドはNoCache属性でマークされており、Webクライアントが常にサーバーにリクエストを送信するようにします。

 namespace NotebookAppApi.Controllers { [Produces("application/json")] [Route("api/[controller]")] public class NotesController : Controller { private readonly INoteRepository _noteRepository; public NotesController(INoteRepository noteRepository) { _noteRepository = noteRepository; } [NoCache] [HttpGet] public Task<IEnumerable<Note>> Get() { return GetNoteInternal(); } private async Task<IEnumerable<Note>> GetNoteInternal() { return await _noteRepository.GetAllNotes(); } // GET api/notes/5 [HttpGet("{id}")] public Task<Note> Get(string id) { return GetNoteByIdInternal(id); } private async Task<Note> GetNoteByIdInternal(string id) { return await _noteRepository.GetNote(id) ?? new Note(); } // POST api/notes [HttpPost] public void Post([FromBody]string value) { _noteRepository.AddNote(new Note() { Body = value, CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now }); } // PUT api/notes/5 [HttpPut("{id}")] public void Put(string id, [FromBody]string value) { _noteRepository.UpdateNoteDocument(id, value); } // DELETE api/notes/23243423 [HttpDelete("{id}")] public void Delete(string id) { _noteRepository.RemoveNote(id); } } } 

データの初期化


このパートでは、デモンストレーションのみを目的として管理タスクを実行するように設計されたコントローラーを実装します(ダミーデータを使用してデータベースを初期化するために使用します)。

Initメソッドを1つ実装します。このメソッドは、必要なすべてのテストデータをデータベースに作成して入力します。 メソッドはhttp:// localhost:5000 / api / system / initまたはhttp:// localhost:53617 / api / system / init (IISを使用する場合)で利用できます。 実際には、このようなコントローラーは本格的な「管理パネル」に進化する可能性があります。

 [Route("api/[controller]")] public class SystemController : Controller { private readonly INoteRepository _noteRepository; public SystemController(INoteRepository noteRepository) { _noteRepository = noteRepository; } // Call an initialization - api/system/init [HttpGet("{setting}")] public string Get(string setting) { if (setting == "init") { _noteRepository.RemoveAllNotes(); _noteRepository.AddNote(new Note() { Id = "1", Body = "Test note 1", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now, UserId = 1 }); _noteRepository.AddNote(new Note() { Id = "2", Body = "Test note 2", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now, UserId = 1 }); _noteRepository.AddNote(new Note() { Id = "3", Body = "Test note 3", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now, UserId = 2 }); _noteRepository.AddNote(new Note() { Id = "4", Body = "Test note 4", CreatedOn = DateTime.Now, UpdatedOn = DateTime.Now, UserId = 2 }); return "Done"; } return "Unknown"; } } 

起動設定


プロジェクトの開始時にサーバーへのリクエストの結果を表示するには、LaunchSettings.jsonファイルを更新します

画像

以下は、URLがデフォルトで起動され、すべてのメモ(api / notes)が表示される設定の例です。

 { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:53617/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "launchUrl": "api/notes", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "NotebookAppApi": { "commandName": "Project", "launchBrowser": true, "launchUrl": "http://localhost:5000/api/notes", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } 

プロジェクトの開始


プロジェクトを開始する前に、MongoDBが実行されていることを確認します(Windowsサービスとして、または上記で行われたコマンドラインから)。

アプリケーションを最初に起動すると、すべてのエントリを含むページにリダイレクトされますが、 初期化プロセスはまだ開始されていないため、このページには空のセットが表示されます。

画像

コマンドを実行してデータベースを初期化し、 アドレスに移動してテストデータを入力します

画像

次に、アプリケーションの開始ページに戻り、データが表示されたことを確認します。

画像

ロボモンゴを使用する


Robomongoを使用すると、データベース内のレコードを確認できます。 資格情報を使用してデータベースに接続すると、4つのエントリすべてを確認できます。

画像

例外処理


asyncおよびawaitキーワードがC#5.0で導入され、TPL(タスク並列ライブラリ)の使用が簡素化されました。 try / catchブロックを使用して、例外をキャッチするだけです。Public async Task <IEnumerable> GetAllNotes()

 { try { return await _context.Notes.Find(_ => true).ToListAsync(); } catch (Exception ex) { // log or manage the exception throw; } } 

したがって、Taskの非同期インスタンスでエラーを処理できますが、さらに処理するために上記の例外をスローできます。

MongoDBドキュメントの完全な更新


最初のプロジェクト例では、更新機能は選択的なプロパティ更新の原則に基づいて機能していました。

ReplaceOneAsyncメソッドを使用して、ドキュメントを完全に更新できます。 IsUpsertパラメーターは、データベースにドキュメントがない場合は作成する必要があることを示します。

 public async Task<ReplaceOneResult> UpdateNote(string id, Note item) { return await _context.Notes .ReplaceOneAsync(n => n.Id.Equals(id) , item , new UpdateOptions { IsUpsert = true }); } 

クロスドメインリクエスト設定(CORS)


異なるドメインで実行されているアプリケーションを使用するには、ASP.NET Web APIソリューションでクロスドメインリクエスト処理(CORS)を有効にする必要があります。 たとえば、メイン要求の前のAngular 2は事前チェック要求を最初に実行します。これは、クロスドメイン要求が許可されているかどうかのチェックとして機能します。 最初に、Startup.csファイルのConfigureServicesメソッドでCORS機能を登録します。

 public void ConfigureServices(IServiceCollection services) { // Add service and create Policy with options services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); // .... services.AddMvc(); } 

次に、Configureメソッドでapp.useCorsメソッドを呼び出して、アプリケーションの各リクエストに対してポリシーをグローバルに有効にします。

 public void Configure(IApplicationBuilder app) { app.UseCors("CorsPolicy"); app.UseMvc(); } 

ファイルを操作する


バイナリファイルをMongoDBドキュメントに保存することもできますが、MongoDB 2.xの最大ドキュメントサイズは16 MBです(以前のバージョンでは4 MBのみ)。 したがって、大量の情報、特に大きなファイルを保存するために、MongoDBはGridFSシステムを使用します。 GridFSは、MongoDBに任意のサイズのファイルを保存するための規則であり、すべての公式ドライバーでサポートされています。 データベース内のファイルは、通常「fs.files」および「fs.chunks」と呼ばれる2つのコレクションに保存されます。 アップロードされた各ファイルには、「fs.files」コレクションに1つのドキュメントがあり、ファイルに関する情報と、「fs.chunks」コレクション内の多くのドキュメントピース(チャンク)が含まれます。

まず、NuGetを介してドライバー用のGridFSコンポーネントを追加する必要があります。

画像

次に、 GridFSBucketクラスのオブジェクトをコンテキストに追加する必要があります。 GridFSBucketは、コレクション「fs.files」と「fs.chunks」の一種であるGridFSを操作するための重要なオブジェクトであり、すべてのファイルを保存できる「バケット」です。

 _database = client.GetDatabase(settings.Value.Database); _bucket = new GridFSBucket(_database); 

追加のパラメーターを指定することもできます。 たとえば、コレクションの名前(プレフィックス「fs」の代わりに自分の名前になります)および1つのチャンクのサイズ。

  _database = client.GetDatabase(settings.Value.Database); var gridFSBucketOptions = new GridFSBucketOptions() { BucketName = "images", ChunkSizeBytes = 1048576, // 1 }; _bucket = new GridFSBucket(_database, gridFSBucketOptions); 

GridFSBucketインスタンスを使用する機能を追加します。

 public GridFSBucket Bucket { get { return _bucket; } } 

または、より短い表記を使用できます。

 public GridFSBucket Bucket => _bucket; 

次に、機能を更新します。 NoteRepository.csのデータベースにファイルを追加するメソッドを追加します

  public async Task<ObjectId> UploadFile(IFormFile file) { try { var stream = file.OpenReadStream(); var filename = file.FileName; return await _context.Bucket.UploadFromStreamAsync(filename, stream); } catch (Exception ex) { return new ObjectId(ex.ToString()); } } 

注:これらのコレクションに直接アクセスするのではなく、常にGridFSBucketオブジェクトを使用してGridFSコレクション(「fs.chunks」および「fs.files」)と対話する必要があります。

INoteRepository.csにメソッドシグネチャを追加します。

 Task<ObjectId> UploadFile(IFormFile file); 

POSTメソッドを追加して、NotesController.csを更新します。

 // POST api/notes/uploadFile [HttpPost("uploadFile")] public async Task<ObjectId> UploadFile(IFormFile file) { return await _noteRepository.UploadFile(file); } 

注:ヘッダーContent-Type:multipart / form-data(これがファイルの転送方法です)で送信されたデータを正しく処理するには、このタイプのデータのサポートを追加する必要があります。 これを行うには、NotesControllerコントローラークラスに対応するConsumes属性を追加します。

 [Consumes("application/json", "multipart/form-data")] public class NotesController : Controller 

ファイルIDで名前を返す簡単なメソッドの例を使用して、ファイルに関する情報を取得することを検討します。

  public async Task<String> GetFileInfo(string id) { GridFSFileInfo info = null; var objectId = new ObjectId(id); try { using (var stream = await _context.Bucket.OpenDownloadStreamAsync(objectId)) { info = stream.FileInfo; } return info.Filename; } catch (Exception) { return "Not Found"; } } 

APIをご覧ください


Web APIをテストするには、 Postmanを使用します。 たとえば、ID(api / notes / 2)でレコードを取得するためにチェックします。GETリクエストタイプを選択して、URL(http:// localhost:53617 / api / notes / 2)を入力し、ヘッダー(Content-Type:application / json)を追加します。 リクエストを送信した後、Id = 2のレコードに関する情報の形式でレスポンスを取得します。
画像

このエントリの内容を変更します。 これを行うには、要求タイプをPUTに変更し、[ボディ]> [未加工]タブに移動して、レコードの新しいコンテンツを設定します。
画像

その後、RoboMongoに移動して、レコードの値が更新されたことを確認します。

画像

GridFSメカニズムの動作を確認しましょう。 これを行うには、Content-Typeをmultipart / form-dataに変更し、URLをlocalhost :53617 / api / notes / uploadFileとして設定し、Body> form-dataセクションでファイルを追加して送信します。 応答として、このファイルに関する情報を受け取りました。
画像

その後、「images.files」および「images.chunks」コレクション内のこのファイルに対応するレコードが作成されました。

画像

IDでファイル名を取得する簡単な方法を確認しましょう。 これを行うには、アドレスバーにファイルIDを転送して、通常のGETリクエストを作成します。

画像

おわりに


MongoDBチームが開発者の利便性と有用性に十分な注意を払い、 トレーニングコースを実施し、 コミュニティの意見に積極的に関心を持ち、 問題を迅速に解決しているという事実(プロジェクトはボランティアだけでなく、常勤者の会社によっても書かれています)は、MongoDB新しい機会を導入し改善するための優れたペースを維持し続けます。

そして、MongoDBをWindowsだけでなく、十分なドライバーの成熟度とLinuxおよびMacOSでの.NET Coreのサポートに関するMicrosoftのコースと共に展開できるという事実は、たとえば、ソリューションをLinuxに完全に展開したい場合、明白なプラスです。 ただし、SQL Server 2017のオープンテストについて言及するしかありません。このソリューションはLinuxにも展開できます。 そして、プロジェクトを開発するときに使用する必要があるアプローチの選択:NoSQLまたはRDBMS-主にアプリケーションの詳細に依存します。 MongoDBは、データを操作するためのすべてのシナリオが適合し、将来的に主要なサンプルに適合し、コレクション全体を取得する場合に理想的です。 そのような確実性がない場合は、すぐにRDBMSを使用することをお勧めします。

ソース:

  1. http://www.qappdesign.com/using-mongodb-with-net-core-webapi/
  2. http://jsman.ru/mongo-book/Vvedenie.html
  3. http://mongodb.imtqy.com/mongo-csharp-driver/2.4/reference/
  4. https://docs.microsoft.com/en-us/azure/documentdb/documentdb-nosql-vs-sql

Source: https://habr.com/ru/post/J328954/


All Articles