实体框架核心(Entity Framework Core)は、.NETアプリケーション用の人気のORM(オブジェクト関連マッパー)であり、開発者は.NETオブジェクトを使用してデータベースと作業することができます。MongoDBなど多くのデータベースタイプと共に使用できます。

この記事で、Entity Framework CoreをMongoDBと共に使用する方法を学ぶことができます。この記事では、基本を説明し、利点を説明し、ステップバイステップのチュートリアルを提供します。MongoDBやEntity Framework Coreに新しい人であれ、これらのツールを.NETプロジェクトに統合することを望んでいる人であれ、このガイドは、関連的なデータベースとNoSQLデータベースを介して橋渡しる手助けを提供します。

記事は、MongoDBとMicrosoftのEntity Framework Coreについて簡単な紹介を始めます。その後、MongoDB EF Coreプロバイダを使用する方法をカバーします。技術的な詳細を含んだ基本的な例を見ていき、MongoBとEntity Framework Coreを使用して完全なプロジェクトを作成し、すべてが一緒に機能することを確認することができます。プロジェクトは、MongoDB Atlasのサンプルデータを使用してレストラン予約システムを作成します。

この記事のビデオバージョンもあり、freeCodeCamp.orgのYouTubeチャンネルで見ることができます。

MongoDBの紹介

MongoDBは、大きな量のデータを処理し、高い性能、スケーラビリティ、柔軟性を提供する人気のNoSQLデータベースであり、従来の関連型データベースとは異なり、柔軟なJSON-likeの文書にデータを格納します。このドキュメント指向のアプローチは、より自然で直観的な方法で複雑なデータ構造を格納することができます。

MongoDBでは、データはコレクションに格納されています。これらのコレクションは、関連データベースのテーブルに似ていますが、固定的なスキーマを持たないので、同じコレクションに異なる構造の文書を持つことができます。この柔軟性は、MongoDBを使用する主な利点の一つであり、特に非構造化または半構造化のデータを处理する際に大きな利点です。

MongoDB文書の一例を見てみましょう。アプリケーションの情報を格納するusersという名前のコレクションを考えます。典型的な文書は以下のようになります。

{
    "_id": "12345",
    "name": "John Doe",
    "email": "[email protected]",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "state": "CA",
        "zip": "12345"
    },
    "hobbies": ["reading", "travelling", "coding"]
}

この文書では、nameemailageaddressなどの様々なフィールドがあります。addressフィールドは、streetcitystatezipなどの複数のサブフィールドを含んだ埋め込まれた文書です。また、hobbiesフィールドは文字列の配列です。

JSONと似ていますが、MongoDBはBSON(Binary JSON)という二進法形式でデータを保存します。BSONはJSONモデルを拡張し、整数、浮動小数点数、日付、二進的データなど追加のデータ型を提供します。この二進法形式はパフォーマンスと柔軟性に最適化されており、MongoDBは効率的にデータの保存と取り出しを行えます。

MongoDBのもう一つ重要な機能は、水平方向にスケールする能力です。これは、データを複数のサーバーに分布し、大規模なデータセットを管理する際に便利であり、高可用性を確保することができます。MongoDBは、豊富なクエリ、索引、聚合をサポートしており、幅広いアプリケーションに适しています。

たとえば、特定の市に住むすべてのユーザーを見つけるクエリを実行することができます。

db.users.find({ "address.city": "Anytown" })

特定の趣味を持っているユーザーを探すこともできます:

db.users.find({ "hobbies": "coding" })

MongoDBは、E-commerceやコンテント管理から実時間分析やIoT(Internet of Things)アプリケーションまで、さまざまな産業で広く使用されています。その柔軟性とスケーラビリティが modern applicationに适していて、多样且つ動的なデータを処理する必要がある場で最適な選択肢となっています。

MongoDBが何であり、どのように人気があるかを基本的に理解したら、私たちの技術スタックで重要なツールの他に、MicrosoftのEntity Framework Coreに移行しましょう。

Microsoft Entity Framework Coreについての紹介

Entity Framework Coreは、.NET用のモダンなオブジェクト-データベースマッパーであり、.NETオブジェクトを使用してデータベースと作業を行い、開発者が通常書く必要のあるデータアクセスコードの大部分を消去することができます。

EF Coreは、人気のあるEntity Framework(EF)データアクセス技術の軽量で拡張性に優れた、 Cross-platformのバージョンであり、SQL Server、SQLite、MongoDBなどさまざまなデータベースエンジンをサポートしています。

EF Coreを使用する主な利点は、開発者がデータに対してより直観的かつオブジェクト指向な方法で作業できることです。RAW SQLクエリを書く代わりに、LINQ(Language Integrated Query)と強く型定義されたクラスを使用して、データベースとやりとりすることができます。

基本的な例を見てみましょう。たとえば、Productクラスがあるとすると、

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

これはたった3つのフィールドしかない非常に簡単なものです。EF Coreを使用して、データベースとの会話を表すコンテキストクラスを作成でき、各エンティティ型に対してクエリや保存するためのDbSetを含めます。

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)   
    {
        optionsBuilder.Use<Your_SQL_Database_function>("YourConnectionStringHere");
    }
}

このコードは、Entity Framework CoreのDbContextクラスを継承したAppDbContextという名前のクラスを定義しています。このクラスは、データベースとのやり取りを行うためのものです。このクラスの内部には、DbSet<Product>という名前のProductsプロパティがあり、これはProduct实体のコレクションを表し、データベース内のProductsという名前のテーブルに対応しています。OnConfiguringメソッドは、データベースの接続を設定するために上書きされ、様々なデータベースをデータベースプロバイダとして指定することができます。このメソッドは、実際のデータベース接続文字列を含むPlaceholderと共に、optionsBuilderを使用して接続を設定します。この接続文字列は明らかに、データベースに接続するために必要な詳細を含む実際のものに置き換えられなければならないです。アプリケーションでAppDbContextのインスタンスを作成すると、この設定を使用してProduct实体のProductsテーブルに查询や保存などの操作を行うことができます。

この設定により、EF Coreを使用してCRUD(作成、読み取り、更新、削除)操作を行うことができます。たとえば、新しい商品をデータベースに追加するために、このコードを使用することができます。

using (var context = new AppDbContext())
{
    var product = new Product { Name = "Laptop", Price = 999.99M };
    context.Products.Add(product);
    context.SaveChanges();
}

このコードは、Entity Framework Coreを使用して新しい商品をデータベースに追加する方法を示しています。AppDbContextのインスタンスが作成され、このコンテキスト内で、名前が”Laptop”、価格が999.99の新しいProductオブジェクトが生成されます。この新しい商品は、AppDbContextに管理されるProductsコレクションに追加されます。最後に、SaveChangesメソッドを呼び出し、この変更をデータベースに保存し、新しい商品をProductsテーブルに挿入します。

商品を照会するには、LINQを使用できます。

using (var context = new AppDbContext())
{
    var products = context.Products.Where(p => p.Price > 500).ToList();
    foreach (var product in products)
    {
        Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");  
    }
}

このコードは、Entity Framework Coreを使用してデータベースを照会する方法を示しています。AppDbContextのインスタンスが作成され、このコンテキスト内で、価格が500を超えるすべての商品を取得するためのクエリが行われます。これらの結果はproductsという名前のリストに格納されます。その後、このリストの各商品にループを回し、各商品の名前と価格をコンソールに出力します。

EF Coreは、これらのLINQクエリをデータベースに合わせた適切なSQLコマンドに翻訳し、データアクセスを簡略化し、保守性を向上させます。

EF Coreは、変更追跡、懒Loading、および移行などの高度な機能もサポートしています。これらは、データベーススキーマの変更を随時管理するのを助けます。

要するに、EF Coreは、.NETアプリケーションでデータアクセスを簡素化する強力なORMであり、.NETオブジェクトとLINQを使用してデータに取り組むことができます。マルチデータベースエンジンのサポートと拡張性により、幅広いアプリケーションに適した選択肢となります。

How the MongoDB EF Core Provider Bridges the Gap

The MongoDB Entity Framework Core Provider is a tool that enables developers to use MongoDB with Entity Framework Core (EF Core), combining MongoDB of the flexibility with the familiar API and design patterns of EF Core.このプロバイダを使うと、リレーショナルデータベースで使うのと同じコードファーストやLINQクエリの手法を使ってMongoDBで作業できるようになり、開発が効率化され、すでにEF Coreに慣れている人にとっては学習曲線が短くなります。

MongoDB EF Core Providerは、基本的なCRUD操作、LINQクエリ、埋め込みドキュメントなどの機能をサポートすることで、MongoDBとEF Coreのギャップを埋めています。

  1. コードファーストワークフロー: データモデルを C# で定義し、EF Core を使って MongoDB スキーマを生成できます。コードでデータベースの構造を管理したい開発者には特に便利です。
  2. CRUD操作: プロバイダは基本的な作成、読み取り、更新、および削除操作をサポートしています。たとえば、先ほど見た同じコードを使用してデータベースに新しいレコードを追加することができます。

     using (var context = new AppDbContext())
     {
         var product = new Product { Name = "Laptop", Price = 999.99M };
         context.Products.Add(product);
         context.SaveChanges();
     }
    
  3. LINQ クエリサポート: MongoDBに対するクエリをLINQを使用して行うことができ、C#と.NETについて既存の知識を活用してデータベースとのやり取りを行うことができます。

     using (var context = new AppDbContext())
     {
         var products = context.Products.Where(p => p.Price > 500).ToList();
         foreach (var product in products)
         {
             Console.WriteLine($"Product: {product.Name}, Price: {product.Price}");
         }
     }
    
  4. 変更追跡: EF Coreの変更追跡機能をサポートしており、データエンティティに対する変更を自動的に検出し、保存することができます。

  5. 埋め込まれたドキュメント: プロバイダは埋め込まれたドキュメントをサポートしており、MongoDBで一般的なパターンである单一のドキュメントに関連するデータを格納することができます。

  6. クラスマッピングとシリアライズ: C#のクラスがMongoDBのコレクションにマッピングされ、様々なデータ型とシリアライズ設定のサポートにより、データが正しく保存されます。

MongoDB Atlasを使用したデータモデリングとCRUD操作

今回は、MongoDB EF Coreプロバイダの使用方法の簡単な例を見ていただきます。しかし、間もなく、Visual Studio Codeで完全なプロジェクトを作成し、全てを具体的に見ることができるようになります。

この節で、MongoDB Entity Framework Core (EF) Providerを使用して、MongoDB Atlasを介してデータモデルを定義し、CRUD (作成、読み取り、更新、削除) 操作を行う方法を探索します。この統合で、MongoDBの柔軟性を利用しながら、EF Coreに熟悉なパターンを活用することができます。

環境の設定

始めるためには、必要なNuGetパッケージをプロジェクトに追加する必要があります。

dotnet add package MongoDB.EntityFrameworkCore

MongoDB EF Coreプロバイダパッケージを追加すると、MS EF CoreパッケージとMongoDB C#ドライバーが依存関係として追加されます。これらのパッケージにより、アプリケーションはMongoDBとEF Coreを通じてインタラクションすることができ、関連付け数据库と同じコンテキストとエンティティ定義を使用します。

MongoDB Atlasの設定

CRUD操作を行う前に、MongoDB Atlasクラスタを設定し、アプリケーションをそれに接続する必要があります。

以下は手順です。これらをプロジェクトを作成した際に詳細に解説します。

  1. MongoDB Atlasアカウントを作成する: MongoDB Atlasで無料のアカウントを登録してください。

  2. クラスタの作成: 新しいクラスタを設定します。MongoDB Atlasは、開発と小规模のアプリケーションに最適な無料チャージを提供しています。

  3. 接続文字列の取得: MongoDB Atlasのダッシュボードから接続文字列を取得してください。以下のようになります:

     mongodb+srv://<username>:<password>@cluster0.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
    

データモデルの定義

实体のモデルとして使用するクラスを定義します。この例では、Customerクラスを作成します:

public class Customer
{
    public ObjectId Id { get; set; }
    public String Name { get; set; }
    public String Order { get; set; }
}

このCustomerクラスは、MongoDBコレクションに格納される文書の構造を表します。

DB コンテキストクラスの作成

Entity Framework Coreを使用するために、DBContextから派生したコンテキストクラスを作成します。DbContext派生クラスのインスタンスは、データベースセッションを表し、これはあなたの实体のインスタンスをクエリおよび保存するために使用されます。

DBContextクラスはDBSetプロパティを公開し、それはそのコンテキストを使用して互換性を持つ实体について情報を提供します。

この例では、DBContextの派生クラスのインスタンスを作成し、CustomerオブジェクトをDBSet属性として指定します。

public class MyDbContext : DbContext
{
    public DbSet<Customer> Customers { get; init; }

    public MyDbContext(DbContextOptions options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Customer>().ToCollection("customers");
    }
}

コード優先の workflow

MongoDB EF プロバイダーを使用して、コード優先の workflow を実行することができます。これは、まずクラスを定義し、EF Coreが底层の MongoDB スキーマの作成と管理を行うようにするものです。これは、スキーマを強制しないMongoDBに特に有用で、柔軟で動的なデータ構造を許可します。

MongoDB を使用する

DBContextクラスを作成した後、DbContextOptionsBuilderオブジェクトを作成し、そのUseMongoDB()メソッドを呼び出す必要があります。このメソッドは、MongoClientインスタンスと、操作するコレクションの保管されるデータベース名を受け取る2つの引数を取ります。

UseMongoDB()メソッドはDbContextOptionsオブジェクトを返します。このオブジェクトのOptions属性をDBContextクラスのコンストラクタに渡す必要があります。

var mongoClient = new MongoClient("<Your MongoDB Connection URI>");

var dbContextOptions =
    new DbContextOptionsBuilder<MyDbContext>().UseMongoDB(mongoClient, "<Database Name");  

var db = new MyDbContext(dbContextOptions.Options);

CRUD 操作

次に、CRUD 操作のコーディング方法を見ていきましょう。各操作について单独的に取り扱います。

作成操作

MongoDBに新しいドキュメントを作成するには、DbSetAddメソッドを使用し、SaveChangesを呼び出す必要があります。これは新しい顧客を作成する例です。

using (var context = new MyDbContext(options))
{
    var customer = new Customer { Name = "Beau Carnes", Order = "Laptop" };
    context.Customers.Add(customer);
    context.SaveChanges();
}

このコードは新しいCustomerインスタンスを作成し、Customersコレクションに追加します。SaveChangesメソッドは、新しい顧客をMongoDB データベースに保存します。

読み取り操作

MongoDBコレクションからドキュメントを読み取るには、DbSetに対するLINQクエリを使用することができます。これは、すべての顧客を取得する例です。

using (var context = new MyDbContext(options))
{
    var customers = context.Customers.ToList();
    foreach (var customer in customers)
    {
        Console.WriteLine($"Customer: {customer.Name}, Order: {customer.Order}"); 
    }
}

このコードはCustomersコレクションからすべての顧客を取得し、その詳細を印刷します。

更新操作

既存のドキュメントを更新するには、ドキュメントを取得し、そのプロパティを変更し、SaveChangesを呼び出す必要があります。これは、顧客の注文を更新する例です。

using (var context = new MyDbContext(options))
{
    var customer = context.Customers.FirstOrDefault(c => c.Name == "Beau Carnes"); 
    if (customer != null)
    {
        customer.Order = "Smartphone";
        context.SaveChanges();
    }
}

このコードは名前が”Beau Carnes”の顧客を見つけ、彼の注文を”スマートフォン”に更新します。

削除操作

ドキュメントを削除するには、ドキュメントを取得し、DbSetから削除し、SaveChangesを呼び出す必要があります。これは、顧客を削除する例です。

using (var context = new MyDbContext(options))
{
    var customer = context.Customers.FirstOrDefault(c => c.Name == "Beau Carnes"); 
    if (customer != null)
    {
        context.Customers.Remove(customer);
        context.SaveChanges();
    }
}

このコードは名前が”Beau Carnes”の顧客を見つけ、Customersコレクションから彼を削除します。

変更追跡

EF Coreの変更追跡機能は完全にサポートされており、ドキュメントの効率的な更新を可能にします。实体を変更し、SaveChangesを呼び出すと、EF CoreはMongoDBの必要なコマンドを生成し、変更したフィールドだけを更新します。

MongoDB EFプロバイダを使用することで、MongoDBの柔軟なドキュメントモデルとEF Coreの強力なORM機能を密接に統合し、.NET開発者がモダンなアプリケーションを構築するための強力なツールセットを提供できます。

チュートリアル

今はすべてをまとめて、レストラント予約システムを作成しましょう。

前提条件

このチュートリアルに従っていくためには、いくつか必要なものがあります。

プロジェクトを作成する

ASP.NET Coreは非常にフレキシブルなウェブフレームワークで、UIや構造に少しの違いがある異なる種類のウェブアプリケーションをスキャンフォードすることができます。このチュートリアルでは、静的ファイルとコントローラを使用するMVCプロジェクトを作成します。Reactなどの他のフロントエンドを使用することもできますが、.cshtmlビューを使用するMVCは最も一般的です。プロジェクトを作成するために、.NET CLIを使用します:

dotnet new mvc -o RestRes

CLIを使用したため、簡単にはなりますが、csprojファイルだけを作成し、Visual Studioで開くことができる解決策ファイルを作成しないでいますので、それを修正しましょう。

cd RestRes
dotnet new sln
dotnet sln .\RestRes.sln add .\RestRes.csproj

NuGetパッケージを追加する

新しいプロジェクトが作成された後、必要なNuGetパッケージを追加したいです。NuGetパッケージマネージャを使用するか、以下の.NET CLIコマンドを使用して、MongoDB MongoDB.EntityFrameworkCoreパッケージを追加してください。

dotnet add package MongoDB.EntityFrameworkCore

モデルを作成する

私たちが追加した新しいパッケージを実装する前に、MongoDB Atlasに文書として保存される、我々のレストラン予約システムに必要なエンティティを表すモデルを作成する必要があります。以下の小節で、以下のモデルを作成します。

  • Restaurant

  • Reservation

  • MongoDBSettings

Restaurant

まず、我々のシステムで予約可能なレストランを表すレストランモデルを作成する必要があります。

  1. Modelsフォルダに新しいファイルを作成し、Restaurant.csと名付けます。

  2. 以下のコードを追加してください。

using MongoDB.Bson;
using MongoDB.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;


namespace RestRes.Models
{
    [Collection("restaurants")]    
    public class Restaurant
    {

        public ObjectId Id { get; set; }

        [Required(ErrorMessage = "You must provide a name")]
        [Display(Name = "Name")]
        public string? name { get; set; }


        [Required(ErrorMessage = "You must add a cuisine type")]
        [Display(Name = "Cuisine")]
        public string? cuisine { get; set; }


        [Required(ErrorMessage = "You must add the borough of the restaurant")]
        public string? borough { get; set; }

    }
}

クラスの前のコレクション属性は、アプリケーションに何がデータベース内のどのコレクションを使用するかを示します。これにより、クラスとコレクションの名前や大小文字の区別を行うことができます。

Reservation

また、我々のシステムで受け取る予約を表す予約クラスも作成する必要があります。

  1. Modelsフォルダ内に新しいファイルを作成し、Reservation.csと名付けます。

  2. 以下のコードを追加してください。

using MongoDB.Bson;
using MongoDB.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;


namespace RestRes.Models
{
    [Collection("reservations")]
    public class Reservation
    {
        public ObjectId Id { get; set; }


        public ObjectId RestaurantId { get; set; }


        public string? RestaurantName { get; set; }

        [Required(ErrorMessage = "The date and time is required to make this reservation")]
        [Display(Name = "Date")]
        public DateTime date { get; set; }

    }
}

MongoDBSettings

データベースに存在しない文書ですが、MongoDBに関連した設定をアプリケーション全体で使用するために、モデルクラスが必要です。

  1. Models フォルダに MongoDBSettings.cs という名前の別のファイルを作成します。

  2. 以下のコードを追加してください。

namespace RestRes.Models
{
  public class MongoDBSettings
  {
      public string AtlasURI { get; set; }
      public string DatabaseName { get; set; }
  }
}

EF Core の設定

これは興味深い部分です。EF Coreを実装し、新しいMongoDBプロバイダを利用することにします。EF Coreを既に使っている人にとって、いくつかの点は熟悉でするかもしれません。

RestaurantReservationDbContext

  1. Service フォルダを作成し、RestaurantReservationDbContext.cs という名前のファイルを作成します。

  2. namespace の中のコードを以下のように置き換えてください。

using Microsoft.EntityFrameworkCore;
using RestRes.Models;

namespace RestRes.Services
{
    public class RestaurantReservationDbContext : DbContext
    {
        public DbSet<Restaurant> Restaurants { get; init; }      


        public DbSet<Reservation> Reservations { get; init; }


        public RestaurantReservationDbContext(DbContextOptions options)
        : base(options)
        {
        }


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);


            modelBuilder.Entity<Restaurant>();
            modelBuilder.Entity<Reservation>();
        }
    }
}

EF Core を使っている人はこれに熟悉でするはずです。このクラスは DbContext を拡張し、データベースに存在するmodelを保持する DbSet プロパティを作成します。また、OnModelCreating メソッドを上書きしています。SQL Serverを使用している場合とは異なり、.ToTable()を呼び出していません。ToCollectionを呼び出すこともできますが、ここでは必須ではありません。なぜなら、クラスに属性を使用してコレクションを指定するからです。

appsettingsに接続文字列とデータベースの詳細を追加してください。

以前、MongoDBSettingsモデルを作成しましたが、今度はプロパティがマップされる値をappsettingsに追加する必要があります。

  1. appsettings.jsonとappsettings.Development.jsonの両方に、次の新しいセクションを追加します:

      "MongoDBSettings": {
        "AtlasURI": "mongodb+srv://<username>:<password>@<url>",
        "DatabaseName": "restaurants"
      }
    
  2. Atlas URIを自分のAtlasの接続文字列に置き換えてください。

program.csの更新

モデルとDbContextを構成したので、program.csファイルに追加する時が来ました。

既存の行の後に builder.Services.AddControllersWithViews();、次のコードを追加します:

var mongoDBSettings = builder.Configuration.GetSection("MongoDBSettings").Get<MongoDBSettings>();
builder.Services.Configure<MongoDBSettings>(builder.Configuration.GetSection("MongoDBSettings"));

builder.Services.AddDbContext<RestaurantReservationDbContext>(options =>
options.UseMongoDB(mongoDBSettings.AtlasURI ?? "", mongoDBSettings.DatabaseName ?? ""));

サービスの作成

現在、RestaurantBookingDbContextを介してデータベースと通信するために使用するサービスを追加する時間です。各サービスには、それぞれのインターフェースとその実装クラスを作成します。

IRestaurantServiceとRestaurantService

最初に実装するインターフェースとサービスは、レストランツコレクション上のCRUD操作を行うためのものです。これはリポジトリパターンと呼ばれます。DbContextを直接操作する人がいるかもしれません。しかし、ほとんどの人がこのパターンを使用しているため、私たちもここでそれを含めます。

  1. まだない場合は、新しいクラスを保存するServicesフォルダを作成してください。

  2. IRestaurantServiceインターフェースを作成し、以下のコードをメソッドとして実装します:

using MongoDB.Bson;
using RestRes.Models;

namespace RestRes.Services
{
    public interface IRestaurantService
    {
        IEnumerable<Restaurant> GetAllRestaurants();
        Restaurant? GetRestaurantById(ObjectId id);

        void AddRestaurant(Restaurant newRestaurant);

        void EditRestaurant(Restaurant updatedRestaurant);

        void DeleteRestaurant(Restaurant restaurantToDelete);
    }
}
  1. RestaurantServiceクラスファイルを作成します。

  2. RestaurantServiceクラス宣言を更新し、私たちが刚刚に作成したIRestaurantServiceを実装します:

using Microsoft.EntityFrameworkCore;
using MongoDB.Bson;
using MongoDB.Driver;
using RestRes.Models;

namespace RestRes.Services
{
  public class RestaurantService : IRestaurantService
  {
    private readonly RestaurantReservationDbContext _restaurantDbContext;
    public RestaurantService(RestaurantReservationDbContext restaurantDbContext)
    {
        _restaurantDbContext = restaurantDbContext;
    }

    public void AddRestaurant(Restaurant restaurant)
    {
      _restaurantDbContext.Restaurants.Add(restaurant);

      _restaurantDbContext.ChangeTracker.DetectChanges();
      Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

      _restaurantDbContext.SaveChanges();
    }

    public void DeleteRestaurant(Restaurant restaurant)
    {
      var restaurantToDelete = _restaurantDbContext.Restaurants.Where(c => c.Id == restaurant.Id).FirstOrDefault();

      if(restaurantToDelete != null) {
          _restaurantDbContext.Restaurants.Remove(restaurantToDelete);
        _restaurantDbContext.ChangeTracker.DetectChanges();
          Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);
          _restaurantDbContext.SaveChanges();
          }
        else {
            throw new ArgumentException("The restaurant to delete cannot be found.");
        }
    }

    public void EditRestaurant(Restaurant restaurant)
    {
          var restaurantToUpdate = _restaurantDbContext.Restaurants.FirstOrDefault(c => c.Id == restaurant.Id);

        if(restaurantToUpdate != null)
        {                
            restaurantToUpdate.name = restaurant.name;
            restaurantToUpdate.cuisine = restaurant.cuisine;
            restaurantToUpdate.borough = restaurant.borough;

            _restaurantDbContext.Restaurants.Update(restaurantToUpdate);

            _restaurantDbContext.ChangeTracker.DetectChanges();
            Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

            _restaurantDbContext.SaveChanges();

        }
      else
        {
            throw new ArgumentException("The restaurant to update cannot be found. ");
        }
    }        

    public IEnumerable<Restaurant> GetAllRestaurants()
    {
      return _restaurantDbContext.Restaurants.OrderByDescending(c => c.Id).Take(20).AsNoTracking().AsEnumerable<Restaurant>();
    }

    public Restaurant? GetRestaurantById(ObjectId id)
    {
      return _restaurantDbContext.Restaurants.FirstOrDefault(c  => c.Id == id);
    }
  }

}

IReservationServiceとReservationService

次に、IReservationServiceとReservationServiceです。

IReservationServiceインターフェースを作成し、以下のメソッドを追加します:

using MongoDB.Bson;
using RestRes.Models;

namespace RestRes.Services
{
    public interface IReservationService
    {
        IEnumerable<Reservation> GetAllReservations();
        Reservation? GetReservationById(ObjectId id);

        void AddReservation(Reservation newReservation);

        void EditReservation(Reservation updatedReservation);

        void DeleteReservation(Reservation reservationToDelete);
    }
}

予約サービスクラスを作成し、以下のコードに置き換えてすべてのメソッドを実装してください。

using Microsoft.EntityFrameworkCore;
using MongoDB.Bson;
using RestRes.Models;

namespace RestRes.Services
{
    public class ReservationService : IReservationService
    {
        private readonly RestaurantReservationDbContext _restaurantDbContext;

        public ReservationService(RestaurantReservationDbContext restaurantDbContext)
        {
            _restaurantDbContext = restaurantDbContext;
        }
        public void AddReservation(Reservation newReservation)
        {
            var bookedRestaurant = _restaurantDbContext.Restaurants.FirstOrDefault(c => c.Id == newReservation.RestaurantId);
            if (bookedRestaurant == null)
            {
                throw new ArgumentException("The restaurant to be reserved cannot be found.");
            }

            newReservation.RestaurantName = bookedRestaurant.name;

            _restaurantDbContext.Reservations.Add(newReservation);

            _restaurantDbContext.ChangeTracker.DetectChanges();
            Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

            _restaurantDbContext.SaveChanges();
        }

        public void DeleteReservation(Reservation reservation)
        {
            var reservationToDelete = _restaurantDbContext.Reservations.FirstOrDefault(b => b.Id == reservation.Id);

            if(reservationToDelete != null)
            {
                _restaurantDbContext.Reservations.Remove(reservationToDelete);

                _restaurantDbContext.ChangeTracker.DetectChanges();
                Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);

                _restaurantDbContext.SaveChanges();
            }
            else
            {
                throw new ArgumentException("The reservation to delete cannot be found.");
            }
        }

        public void EditReservation(Reservation updatedReservation)
        {
           var reservationToUpdate = _restaurantDbContext.Reservations.FirstOrDefault(b => b.Id == updatedReservation.Id);


            if (reservationToUpdate != null)
            {               
                reservationToUpdate.date = updatedReservation.date;

                _restaurantDbContext.Reservations.Update(reservationToUpdate);

                _restaurantDbContext.ChangeTracker.DetectChanges();
                _restaurantDbContext.SaveChanges();

                Console.WriteLine(_restaurantDbContext.ChangeTracker.DebugView.LongView);
            }  
            else 
            { 
                throw new ArgumentException("Reservation to be updated cannot be found");
            }

        }

        public IEnumerable<Reservation> GetAllReservations()
        {
            return _restaurantDbContext.Reservations.OrderBy(b => b.date).Take(20).AsNoTracking().AsEnumerable<Reservation>();
        }

        public Reservation? GetReservationById(ObjectId id)
        {
            return _restaurantDbContext.Reservations.AsNoTracking().FirstOrDefault(b => b.Id == id);
        }

    }
}

このコードは、レストランサービスクラスのコードと非常に似ていますが、予約用です。

ディプロイメントインジェクションに追加します。

サービスに最後のステップは、ディプロイメントインジェクションコンテナに追加することです。

プログラム.csの中で、今までに追加したコードの後に以下のコードを追加してください。

builder.Services.AddScoped<IRestaurantService, RestaurantService>();
builder.Services.AddScoped<IReservationService, ReservationService>();

ビューモデルの作成

フロントエンドを実装する前に、必要な場所でフロントとバックエンドをメッセンジャーとして機能するビューモデルを追加する必要があります。アプリケーションは相当にシンプルですが、ビューモデルの実装はまだ良い慣習で、アプリのパーツを解耦合する助けとなります。

レストラントリストビューモデル

最初に追加するのは、後で使用するためのレストラントリストビューモデルです。これは、後で使用するためのレストラントリストビューモデルです。

  1. プロジェクトのルートに新しいフォルダーを作成し、名前をViewModelsにしてください。

  2. 新しいファイルを作成し、名前をRestaurantListViewModel.csにしてください。

  3. 以下のコードを追加してください。

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class RestaurantListViewModel
    {        
        public IEnumerable<Restaurant>? Restaurants { get; set; }
    }
}

レストラント追加ビューモデル

後で追加するAddビューに使用するビューモデルも必要です。

  1. ViewModelsフォルダーの中で新しいファイルを作成し、名前をRestaurantAddViewMode.csにしてください。

  2. Add:

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class RestaurantAddViewModel
    {
        public Restaurant? Restaurant { get; set; } 
    }
}

ReservationListViewModel

今度は、予約に似たことをしてみましょう。始めにReservationListViewModelを作成します。

  1. ViewModelsフォルダーに新しいファイルを作成し、ReservationListViewModel.csと名付けてください。

  2. Add:

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class ReservationListViewModel
    {
        public IEnumerable<Reservation>? Reservations { get; set; }
    }
}

ReservationAddViewModel

最後に、私たちのReservationAddViewModelが完成しました。

このファイルを作成し、以下のコードを追加してください。

using RestRes.Models;

namespace RestRes.ViewModels
{
    public class ReservationAddViewModel
    {
        public Reservation? Reservation { get; set; }
    }
}

_ViewImportsに追加する

後で、ビューにて私たちのモデルとビューモデルに参照を追加します。アプリケーションがそれらを認識するために、Viewsフォルダー内の_ViewImports.cshtmlファイルに参照を追加する必要があります。

既にいくつかの参照が入っていますが、其中包括了TagHelpers。私たちは、.Modelsと.ViewModelsフォルダーに参照を追加します。のようにファイルの先頭に以下のようにします。

@using RestRes
@using RestRes.Models
@using RestRes.ViewModels

コントローラーの作成

今度は、バックエンド実装と参照するビューモデルを持った後で、前端に取り組む準備が整いました。レストラurantと予約に各1つのコントローラーを作成します。

RestaurantController

最初に追加するコントローラーは、レストラurant用です。

  1. 既存のControllersフォルダーに新しいコントローラーファイルRestaurantController.csを追加します。Visual Studioを使用している場合、MVC Controller – Empty controller テンプレートを使用してください。

  2. 以下のコードを追加してください。

using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;
using RestRes.Models;
using RestRes.Services;
using RestRes.ViewModels;

namespace RestRes.Controllers
{
    public class RestaurantController : Controller
    {
        private readonly IRestaurantService _RestaurantService;

        public RestaurantController(IRestaurantService RestaurantService)
        {
            _RestaurantService = RestaurantService;
        }
        public IActionResult Index()
        {
            RestaurantListViewModel viewModel = new()
            {
                Restaurants = _RestaurantService.GetAllRestaurants(),
            };
            return View(viewModel);
        }

        public IActionResult Add()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Add(RestaurantAddViewModel restaurantAddViewModel)
        {
            if(ModelState.IsValid)
            {
                Restaurant newRestaurant = new()
                {
                    name = restaurantAddViewModel.Restaurant.name,
                    borough = restaurantAddViewModel.Restaurant.borough,
                    cuisine = restaurantAddViewModel.Restaurant.cuisine
                };

                _RestaurantService.AddRestaurant(newRestaurant);
                return RedirectToAction("Index");
            }

            return View(restaurantAddViewModel);         
        }

        public IActionResult Edit(ObjectId id)
        {
            if(id == null || id == ObjectId.Empty)
            {
                return NotFound();
            }

            var selectedRestaurant = _RestaurantService.GetRestaurantById(id);
            return View(selectedRestaurant);
        }

        [HttpPost]
        public IActionResult Edit(Restaurant restaurant)
        {
            try
            {
                if(ModelState.IsValid)
                {
                    _RestaurantService.EditRestaurant(restaurant);
                    return RedirectToAction("Index");
                }
                else
                {
                    return BadRequest();
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", $"Updating the restaurant failed, please try again! Error: {ex.Message}");
            }

            return View(restaurant);
        }

        public IActionResult Delete(ObjectId id) {
            if (id == null || id == ObjectId.Empty)
            {
                return NotFound();
            }

            var selectedRestaurant = _RestaurantService.GetRestaurantById(id);
            return View(selectedRestaurant);
        }

        [HttpPost]
        public IActionResult Delete(Restaurant restaurant)
        {
            if (restaurant.Id == ObjectId.Empty)
            {
                ViewData["ErrorMessage"] = "Deleting the restaurant failed, invalid ID!";
                return View();
            }

            try
            {
                _RestaurantService.DeleteRestaurant(restaurant);
                TempData["RestaurantDeleted"] = "Restaurant deleted successfully!";

                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                ViewData["ErrorMessage"] = $"Deleting the restaurant failed, please try again! Error: {ex.Message}";
            }

            var selectedRestaurant = _RestaurantService.GetRestaurantById(restaurant.Id);
            return View(selectedRestaurant);
        }        
    }
}

ReservationController

次に、予約コントローラーについて。これはRestaurantControllerと非常に似ていますが、レストラントと予約サービスの両方への参照を持っています。なぜなら、現在、EF Core Providerは实体間の関係をサポートしていないため、異なる方法で实体間の関連付けを行う必要があります。

  1. 空のMVC ControllerファイルReservationController.csを追加します。

  2. 以下のコードを貼り付けてください。

using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;
using RestRes.Models;
using RestRes.Services;
using RestRes.ViewModels;

namespace RestRes.Controllers
{
    public class ReservationController : Controller
    {
        private readonly IReservationService _ReservationService;
        private readonly IRestaurantService _RestaurantService;        

        public ReservationController(IReservationService ReservationService, IRestaurantService RestaurantService)
        {
            _ReservationService = ReservationService;
            _RestaurantService = RestaurantService;
        }

        public IActionResult Index()
        {
            ReservationListViewModel viewModel = new ReservationListViewModel()
            {
                Reservations = _ReservationService.GetAllReservations()
            };
            return View(viewModel);
        }

        public IActionResult Add(ObjectId restaurantId)
        {
            var selectedRestaurant = _RestaurantService.GetRestaurantById(restaurantId);

            ReservationAddViewModel reservationAddViewModel = new ReservationAddViewModel();

            reservationAddViewModel.Reservation = new Reservation();
            reservationAddViewModel.Reservation.RestaurantId = selectedRestaurant.Id;
            reservationAddViewModel.Reservation.RestaurantName = selectedRestaurant.name;
            reservationAddViewModel.Reservation.date = DateTime.UtcNow;

            return View(reservationAddViewModel);
        }

        [HttpPost]
        public IActionResult Add(ReservationAddViewModel reservationAddViewModel)
        {
                Reservation newReservation = new()
                {
                    RestaurantId = reservationAddViewModel.Reservation.RestaurantId,                   
                    date = reservationAddViewModel.Reservation.date,
                };

                _ReservationService.AddReservation(newReservation);
                return RedirectToAction("Index");   
        }

        public IActionResult Edit(string Id)
        {
            if(Id == null || string.IsNullOrEmpty(Id))
            {
                return NotFound();
            }

            var selectedReservation = _ReservationService.GetReservationById(new ObjectId(Id));
            return View(selectedReservation);
        }

        [HttpPost]
        public IActionResult Edit(Reservation reservation)
        {
            try
            {
                var existingReservation = _ReservationService.GetReservationById(reservation.Id);
                if (existingReservation != null)
                {
                    _ReservationService.EditReservation(reservation);
                    return RedirectToAction("Index");
                }
                else
                {
                    ModelState.AddModelError("", $"Reservation with ID {reservation.Id} does not exist!");
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", $"Updating the reservation failed, please try again! Error: {ex.Message}");
            }

            return View(reservation);
        }

        public IActionResult Delete(string Id)
        {
            if (Id == null || string.IsNullOrEmpty(Id))
            {
                return NotFound();
            }

            var selectedReservation = _ReservationService.GetReservationById(new ObjectId(Id));
            return View(selectedReservation);
        }

        [HttpPost]
        public IActionResult Delete(Reservation reservation)
        {
            if(reservation.Id == null)
            {
                ViewData["ErrorMessage"] = "Deleting the reservation failed, invalid ID!";
                return View();
            }

            try
            {
                _ReservationService.DeleteReservation(reservation);
                TempData["ReservationDeleted"] = "Reservation deleted successfully";

                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                ViewData["ErrorMessage"] = $"Deleting the reservation failed, please try again! Error: {ex.Message}";
            }

            var selectedRestaurant = _ReservationService.GetReservationById(reservation.Id);
            return View(selectedRestaurant);
        }
    }
}

Viewの作成

今度は、レストラン予約システムのエンドポイントに準備された後端とコントローラーを実装し、ビューを実装します。これはRazor pagesを使用します。Bootstrapのクラスに参照することもありますが、これはMVCアプリケーションに付属しているCSSフレームワークです。私たちは、両方の一覧と予約のCRUD操作のビューを提供します。

レストラントの一覧の作成

まず、/Restaurantのルートにマップされるビューを提供しますが、これは慣例により実装したIndexメソッドを参照します。

ASP.NET Core MVCは、エンドポイント/メソッドの名前を使用する.cshtmlファイルを、そのコントローラーの名前のフォルダー内に配置するという慣例パターンを使用します。

  1. Viewsフォルダー内に、Restaurantという新しいサブフォルダーを作成します。

  2. そのRestaurantフォルダー内に、Index.cshtmlというファイルを作成して新しいビューを追加します。利用可能なテンプレートを使用する場合は、Razor View – Emptyを選択します。ビューの名前はIndexにします。

  3. 次のコードを追加します:

@model RestaurantListViewModel

@if (TempData["RestaurantDeleted"] != null)
{
    <p class="text-success">@TempData["RestaurantDeleted"]</p>
}


@if (!Model.Restaurants.Any())
{
    <p>No results</p>
}
else
{
    <table class="table table-condensed table-bordered">
        <tr>
            <th>
                Name
            </th>
            <th>
                Cuisine
            </th>
            <th>
                Borough
            </th>            
            <th>
                Actions
            </th>
        </tr>

        @foreach (var restaurant in Model.Restaurants)
        {
            <tr>
                <td>@restaurant.name</td>
                <td>@restaurant.cuisine</td>
                <td>@restaurant.borough</td>                
                <td>
                    <a asp-action="Edit" asp-route-id="@restaurant.Id.ToString()">Edit</a>
                    <a asp-action="Delete" asp-route-id="@restaurant.Id.ToString()">Delete</a>
                    <a asp-controller="Reservation" asp-action="Add" asp-route-restaurantId="@restaurant.Id.ToString()">Reserve</a>
                </td>
            </tr>
        }

    </table>
}

<p>
    <a class="btn btn-primary" asp-action="Add">Add new restaurant</a>
</p>

では、デフォルトルートをHomeから/Restaurantに更新しましょう。

Program.csのapp.MapControllerRoute内で、パターン行を次のように置き換えます:

pattern: "{controller=Restaurant}/{action=Index}/{id?}");

今これを実行すると、ボタンが404エラーになるでしょう。なぜなら、まだそれらを実装していないからです。では、今それを行いましょう。

レストランの追加

新しいレストランを追加するフォームから始めます。

  1. Restaurantサブフォルダー内に、新しい空のRazor ViewをAdd.cshtmlという名前で追加します。

  2. 以下のコードを追加してください。

@model RestaurantAddViewModel

<h2>Create a new restaurant</h2>
<hr />

@if (ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<form method="post" asp-controller="Restaurant" asp-action="Add">
    <div asp-validation-summary="All" class="text-danger"></div>

    <div class="mb-3">
        <label asp-for="Restaurant.name" class="form-label"></label>
        <input asp-for="Restaurant.name" class="form-control" />
        <span asp-validation-for="Restaurant.name" class="text-danger"></span>
    </div>

    <div class="mb-3">
        <label asp-for="Restaurant.cuisine" class="form-label"></label>
        <input asp-for="Restaurant.cuisine" class="form-control" />
        <span asp-validation-for="Restaurant.cuisine" class="text-danger"></span>
    </div>

      <div class="mb-3">
        <label asp-for="Restaurant.borough" class="form-label">Borough</label>
        <input asp-for="Restaurant.borough" class="form-control" />
        <span asp-validation-for="Restaurant.borough" class="text-danger"></span>
    </div>

    <input type="submit" value="Add restaurant" class="btn btn-primary" />
</form>

<div>
    <a asp-controller="Restaurant" asp-action="Index">Back to list</a>
</div>

レストラン編集

編集ページのコードは追加とはほぼ同じですが、レストランをモデルとして使い、渡されたレストランを使用してフォームを編集前の状態に预けるためには、レストランを使用しています。

  1. レストランサブフォルダに新しいビューを追加し、Edit.cshtmlと呼び名を付けてください。

  2. 以下のコードを追加してください。

@model Restaurant

<h2>Update @Model.name</h2>
<hr />

<form method="post" asp-controller="Restaurant" asp-action="Edit">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="Id" />

    <div class="mb-3">
        <label asp-for="name" class="form-label">Name</label>
        <input asp-for="name" class="form-control" />
        <span asp-validation-for="name" class="text-danger"/>
    </div>
    <div class="mb-3">
        <label asp-for="cuisine" class="form-label"></label>
        <input asp-for="cuisine" class="form-control" />
        <span asp-validation-for="cuisine" class="text-danger"/>
    </div>
    <div class="mb-3">
        <label asp-for="borough" class="form-label">Borough</label>
        <input asp-for="borough" class="form-control" />
        <span asp-validation-for="borough" class="text-danger"/>
    </div>
    <input type="submit" value="Update restaurant" class="btn btn-primary" />
</form>
<div>
    <a asp-controller="Restaurant" asp-action="Index">Back to list</a>
</div>

レストランの削除

削除ボタンが押された時に呼ばれるページを最後に実装する必要があります。

  1. 削除用の新しい空のビューを作成し、Delete.cshtmlと呼び名を付けてください。

  2. 以下のコードを追加してください。

@model Restaurant

<h2>Deleting @Model.name</h2>
<hr />

@if(ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<div>
    <dl class="row">
        <dt class="col-sm-4">
            <label asp-for="name">Name</label>
        </dt>
        <dd class="col-sm-10">
            @Model?.name
        </dd>
        <dt class="col-sm-2">
            <label asp-for="cuisine"></label>
        </dt>
        <dd class="col-sm-10">
            @Model?.cuisine
        </dd>
        <dt class="col-sm-2">
            <label asp-for="borough">Borough</label>
        </dt>
        <dd class="col-sm-10">
            @Model?.borough
        </dd>

    </dl>
</div>

<form method="post" asp-action="Delete">
    <input type="hidden" asp-for="Id" />
    <input type="submit" value="Delete restaurant" class="btn btn-danger" onclick="javascript: return confirm('Are you sure you want to delete this restaurant?');" />
</form>

<div>
    <a asp-controller="Restaurant" asp-action="Index">Back to list</a>
</div>

予約の一覧表示

レストランのビューを追加したので、次に予約のビューを追加しましょう。

  1. Viewsフォルダ内に新しいフォルダを作成し、Reservationと呼び名を付けてください。

  2. 新しい空のビューファイルを作成し、Index.cshtmlと呼び名を付けてください。

  3. 予約が存在する場合、以下のコードを表示してください:

@model ReservationListViewModel

@if (TempData["ReservationDeleted"] != null)
{
    <p class="text-success">@TempData["ReservationDeleted"]</p>
}

@if (!Model.Reservations.Any())
{
    <p>No results</p>
}

else
{    
    <table class="table table-condensed table-bordered">
        <tr>
            <th>
                Booked Restaurant
            </th>
            <th>
                Date and Time
            </th>
            <th>
                Actions
            </th>
        </tr>

        @foreach(var reservation in Model.Reservations)
        {
            <tr>
                <td>@reservation.RestaurantName</td>
                <td>@reservation.date.ToLocalTime()</td>
                <td>
                    <a asp-action="Edit" asp-route-id="@reservation.Id.ToString()">Edit</a>
                    <a asp-action="Delete" asp-route-id="@reservation.Id.ToString()">Delete</a>
                </td>
            </tr>
        }

    </table>   

}

予約の追加

予約の追加が次です。

  1. 空のビュー Add.cshtml を作成してください。

  2. 以下のコードを追加してください。

@model ReservationAddViewModel


@if (ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<form method="post" asp-controller="Reservation" asp-action="Add">
    <div asp-validation-summary="All" class="text-danger"></div>
    <input type="hidden" asp-for="Reservation.Id" />
    <input type="hidden" asp-for="Reservation.RestaurantId" />

    <div class="mb-3">
        <label asp-for="Reservation.date" class="form-label"></label>
        <input asp-for="Reservation.date" type="datetime-local" class="form-control" value="@DateTime.Now.ToString("yyyy-MM-ddTHH:mm")" />
        <span asp-validation-for="Reservation.date" class="text-danger"></span>
    </div>

    <input type="submit" value="Reserve table" class="btn btn-primary" />
</form>

予約の編集

予約の編集が次です。

  1. 空のビュー Edit.cshtml を作成してください。

  2. 以下のコードを追加してください。

@model Reservation

<h2>Editing reservation for @Model.RestaurantName on @Model.date.ToLocalTime()</h2>
<hr />

<form method="post" asp-controller="Reservation" asp-action="Edit">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="Id" />

    <div class="mb-3">
        <label asp-for="date" class="form-label"></label>
        <input asp-for="date" value="@Model.date.ToLocalTime().ToString("yyyy-MM-ddTHH:mm")" class="form-control" />
        <span asp-validation-for="date" class="text-danger" />
    </div>
    <input type="submit" value="Update reservation" class="btn btn-primary" />
</form>
<div>
    <a asp-controller="Reservation" asp-action="Index">Back to reservations</a>
</div>

予約の削除

予約の削除が次です。

  1. 空のビュー Delete.cshtml を作成してください。

  2. 以下のコードを追加してください。

@model Reservation

<h2>Delete reservation</h2>
<hr />

@if (ViewData["ErrorMessage"] != null)
{
    <p class="text-danger">@ViewData["ErrorMessage"]</p>
}

<div>
    <dl class="row">
        <dt class="col-sm-2">
            <label asp-for="RestaurantName">Name</label>
        </dt>
        <dd class="col-sm-10">
            @Model?.RestaurantName
        </dd>
        <dt class="col-sm-2">
            <label asp-for="date"></label>
        </dt>
        <dd class="col-sm-10">
            @Model?.date.ToLocalTime()
        </dd>
        </dl>
</div>

<form method="post" asp-action="Delete">
    <input type="hidden" asp-for="Id" />
    <input type="hidden" asp-for="RestaurantId" />
    <input type="submit" value="Delete reservation" class="btn btn-danger" onclick="javascript: return confirm('Are you sure you want to delete this reservation?');" />
</form>

<div>
    <a asp-controller="Reservation" asp-action="Index">Back to list</a>
</div>

NavBar の更新

最後に、アプリケーションのナビゲーションバーを更新しましょう。これで、レストランと予約を簡単に切り替えることができます。

ファイル Views/Shared/_Layout.cshtml に移動してください。クラス navbar-collapsediv を見つけます。その整个の節を削除し、以下のコードを追加してください。

<div class="collapse navbar-collapse justify-content-between">
    <ul class="navbar-nav flex-grow-1">
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-controller="Restaurant" asp-action="Index">Restaurants</a>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="" asp-controller="Reservation" asp-action="Index">Reservations</a>
        </li>
    </ul>
</div>

アプリケーションのテスト

新しい MongoDB Provider for EF Core を使用した機能を持つアプリケーションを作成しました。今、すべてのことをテストし、エンドポイントを訪れて機能が正しいか確認する時間です。

ターミナルで以下のコマンドを実行してください。

dotnet run

レストランを編集して予約を追加してみてください。その後、MongoDB Atlas データベースのページに移動して、変更がデータベースに反映されていることを確認してください。

MongoDB の高度な操作: Atlas 検索とベクター 検索

EF Core プロバイダは、MongoDB C# ドライバーの上に構築されています。DbContext を作成する際には既に MongoClient にアクセスできるので、Atlas 検索やベクター 検索などの高度な MongoDB 操作を行うことができます。これらの機能は、熟悉の EF Core フレームワークを利用しながら、強力な検索機能を実装することで、アプリケーションの機能を強化します。

Atlas 検索は、MongoDB Atlas から提供される全文検索エンジンです。MongoDB データに対して複雑な検索クエリを実行することができます。Atlas 検索を使用すると、自動補完、階層的な検索、および関連性に基づく並べ替えなどの機能を実装できます。

EF Core プロバイダで Atlas 検索を使用するには、以下の手順に従います。

  1. MongoDB Atlas でインデックスを設定する:

    • MongoDB Atlas クラスタに移動します。

    • “Search” タブに移動し、コレクションに新しいインデックスを作成します。searchable にするフィールドを定義します。

  2. Define Searchable Fields in Your Models: C#モデルの中で、検索したいフィールドが適切に定義されていることを確認してください。以下は、Productモデルの定義の例です。
     public class Product{public ObjectId Id { get set; }public string Name { get set; }public string Description { get set; }public decimal Price { get set; }public string Category { get set; }}
  3. 搜索引擎の使用:MongoDB .NET Driverを使用してテキスト検索を行います。EF Core自体はMongoDB固有の検索構文を直接サポートしていないため、EF Coreと共にドライバを使用する必要があります。以下は例です。

     using MongoDB.Driver;
     using MongoDB.Driver.Linq;
    
     var client = new MongoClient("your-mongodb-connection-string");
     var database = client.GetDatabase("your-database-name");
     var collection = database.GetCollection<Product>("Products");
    
     var searchResult = collection.Aggregate()
         .Match(Builders<Product>.Filter.Text("search term"))
         .ToList();
    

この例は、Products コレクションに対してテキスト検索を行う方法を示しています。Text フィルターは、Atlas Search インデックス内で定義された全ての索引化されたフィールドを跨いでsearchするのに役立ちます。

MongoDBのベクターサーチは、ベクターの類似性に基づいてドキュメントをsearchするために使用されます。これは、機械学習、推奨、自然言語処理を含むアプリケーションに特に有用です。ベクターサーチは、テキスト、画像、または他の高次元データを表すベクターを使用して、ドキュメントにqueryを立てることができます。

  1. ベクターの作成と保存: まず、ドキュメントにベクターが含まれていることを確認する必要があります。データを前処理する必要があり、これらのベクターを生成するために機械学習モデルを使用するかもしれません。

  2. MongoDB Atlasでベクターを索引化: MongoDB Atlasのベクターフィールドに特別な索引を作成して、効率的なベクター類似性searchを可能にします。

  3. ビクトル検索の実行:MongoDB .NET ドライバを使用して、ベクトル類似度に基づいてクエリを実行します。

EF Coreとの統合

MongoDB EF CoreプロバイダはCRUD操作を簡素化しますが、Atlas SearchやVector Searchなどの高度な機能にはMongoDB .NETドライバの直接使用が必要です。しかし、ドライバを検索機能に、EF Coreを他のデータ管理タスクに使用することで、EF Coreベースのアプリケーション内でこれらの操作を統合することができます。

EF CoreとMongoDBの高度な機能を組み合わせることで、構造化データアクセスパターンのEF CoreとMongoDB Atlasの強力な検索機能の両方を活用する強力で柔軟性のあるアプリケーションを構築できます。