diff --git a/aspnetcore/tutorials/first-mongo-app.md b/aspnetcore/tutorials/first-mongo-app.md index c5373bec67..d406267c99 100644 --- a/aspnetcore/tutorials/first-mongo-app.md +++ b/aspnetcore/tutorials/first-mongo-app.md @@ -5,7 +5,7 @@ description: This tutorial demonstrates how to create an ASP.NET Core web API us monikerRange: '>= aspnetcore-3.1' ms.author: scaddie ms.custom: "mvc, seodec18" -ms.date: 08/17/2019 +ms.date: 11/09/2021 no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR] uid: tutorials/first-mongo-app --- @@ -26,36 +26,34 @@ In this tutorial, you learn how to: > * Perform MongoDB CRUD operations from a web API > * Customize JSON serialization -[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/tutorials/first-mongo-app/samples) ([how to download](xref:index#how-to-download-a-sample)) - ## Prerequisites # [Visual Studio](#tab/visual-studio) -* [.NET Core SDK 3.0 or later](https://dotnet.microsoft.com/download/dotnet-core) -* [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/?utm_medium=microsoft&utm_source=docs.microsoft.com&utm_campaign=inline+link&utm_content=download+vs2019) with the **ASP.NET and web development** workload * [MongoDB](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/) +* [Visual Studio 2022 or later](https://visualstudio.microsoft.com/downloads/?utm_medium=microsoft&utm_source=docs.microsoft.com&utm_campaign=inline+link&utm_content=download+vs2022) with the **ASP.NET and web development** workload +* [!INCLUDE[](~/includes/6.0-SDK.md)] # [Visual Studio Code](#tab/visual-studio-code) -* [.NET Core SDK 3.0 or later](https://dotnet.microsoft.com/download/dotnet-core) +* [MongoDB](https://docs.mongodb.com/manual/administration/install-community/) * [Visual Studio Code](https://code.visualstudio.com/download) * [C# for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) -* [MongoDB](https://docs.mongodb.com/manual/administration/install-community/) +* [!INCLUDE[](~/includes/6.0-SDK.md)] # [Visual Studio for Mac](#tab/visual-studio-mac) -* [.NET Core SDK 3.0 or later](https://dotnet.microsoft.com/download/dotnet-core) -* [Visual Studio for Mac version 7.7 or later](https://visualstudio.microsoft.com/downloads/) * [MongoDB](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/) +* [Visual Studio for Mac 2022 or later](https://visualstudio.microsoft.com/vs/mac/): Select the *Preview* channel from within Visual Studio. For more information, see [Install a preview version of Visual Studio for Mac](/visualstudio/mac/install-preview). +* [!INCLUDE[](~/includes/6.0-SDK.md)] --- ## Configure MongoDB -If using Windows, MongoDB is installed at *C:\\Program Files\\MongoDB* by default. Add *C:\\Program Files\\MongoDB\\Server\\\\\bin* to the `Path` environment variable. This change enables MongoDB access from anywhere on your development machine. +On Windows, MongoDB is installed at *C:\\Program Files\\MongoDB* by default. Add *C:\\Program Files\\MongoDB\\Server\\\\\bin* to the `Path` environment variable. This change enables MongoDB access from anywhere on your development machine. -Use the mongo Shell in the following steps to create a database, make collections, and store documents. For more information on mongo Shell commands, see [Working with the mongo Shell](https://docs.mongodb.com/manual/mongo/#working-with-the-mongo-shell). +Use the mongo Shell in the following steps to create a database, make collections, and store documents. For more information on mongo Shell commands, see [`mongo`](https://docs.mongodb.com/v4.4/reference/program/mongo/). 1. Choose a directory on your development machine for storing the data. For example, *C:\\BooksData* on Windows. Create the directory if it doesn't exist. The mongo Shell doesn't create new directories. 1. Open a command shell. Run the following command to connect to MongoDB on default port 27017. Remember to replace `` with the directory you chose in the previous step. @@ -73,10 +71,10 @@ Use the mongo Shell in the following steps to create a database, make collection 1. Run the following command in a command shell: ```console - use BookstoreDb + use BookStore ``` - A database named *BookstoreDb* is created if it doesn't already exist. If the database does exist, its connection is opened for transactions. + A database named *BookStore* is created if it doesn't already exist. If the database does exist, its connection is opened for transactions. 1. Create a `Books` collection using following command: @@ -93,65 +91,63 @@ Use the mongo Shell in the following steps to create a database, make collection 1. Define a schema for the `Books` collection and insert two documents using the following command: ```console - db.Books.insertMany([{'Name':'Design Patterns','Price':54.93,'Category':'Computers','Author':'Ralph Johnson'}, {'Name':'Clean Code','Price':43.15,'Category':'Computers','Author':'Robert C. Martin'}]) + db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }]) ``` - The following result is displayed: + A result similar to the following is displayed: ```console { - "acknowledged" : true, - "insertedIds" : [ - ObjectId("5bfd996f7b8e48dc15ff215d"), - ObjectId("5bfd996f7b8e48dc15ff215e") - ] - } + "acknowledged" : true, + "insertedIds" : [ + ObjectId("61a6058e6c43f32854e51f51"), + ObjectId("61a6058e6c43f32854e51f52") + ] + } ``` > [!NOTE] - > The ID's shown in this article will not match the IDs when you run this sample. + > The `ObjectId`s shown in the preceding result won't match those shown in your command shell. 1. View the documents in the database using the following command: ```console - db.Books.find({}).pretty() + db.Books.find().pretty() ``` - The following result is displayed: + A result similar to the following is displayed: ```console { - "_id" : ObjectId("5bfd996f7b8e48dc15ff215d"), - "Name" : "Design Patterns", - "Price" : 54.93, - "Category" : "Computers", - "Author" : "Ralph Johnson" - } - { - "_id" : ObjectId("5bfd996f7b8e48dc15ff215e"), - "Name" : "Clean Code", - "Price" : 43.15, - "Category" : "Computers", - "Author" : "Robert C. Martin" - } + "_id" : ObjectId("61a6058e6c43f32854e51f51"), + "Name" : "Design Patterns", + "Price" : 54.93, + "Category" : "Computers", + "Author" : "Ralph Johnson" + } + { + "_id" : ObjectId("61a6058e6c43f32854e51f52"), + "Name" : "Clean Code", + "Price" : 43.15, + "Category" : "Computers", + "Author" : "Robert C. Martin" + } ``` The schema adds an autogenerated `_id` property of type `ObjectId` for each document. -The database is ready. You can start creating the ASP.NET Core web API. - ## Create the ASP.NET Core web API project # [Visual Studio](#tab/visual-studio) 1. Go to **File** > **New** > **Project**. -1. Select the **ASP.NET Core Web Application** project type, and select **Next**. -1. Name the project *BooksApi*, and select **Create**. -1. Select the **.NET Core** target framework and **ASP.NET Core 3.0**. Select the **API** project template, and select **Create**. -1. Visit the [NuGet Gallery: MongoDB.Driver](https://www.nuget.org/packages/MongoDB.Driver/) to determine the latest stable version of the .NET driver for MongoDB. In the **Package Manager Console** window, navigate to the project root. Run the following command to install the .NET driver for MongoDB: +1. Select the **ASP.NET Core Web API** project type, and select **Next**. +1. Name the project *BookStoreApi*, and select **Next**. +1. Select the **.NET 6.0 (Long-term support)** framework and select **Create**. +1. In the **Package Manager Console** window, navigate to the project root. Run the following command to install the .NET driver for MongoDB: ```powershell - Install-Package MongoDB.Driver -Version {VERSION} + Install-Package MongoDB.Driver ``` # [Visual Studio Code](#tab/visual-studio-code) @@ -159,25 +155,25 @@ The database is ready. You can start creating the ASP.NET Core web API. 1. Run the following commands in a command shell: ```dotnetcli - dotnet new webapi -o BooksApi - code BooksApi + dotnet new webapi -o BookStoreApi + code BookStoreApi ``` - A new ASP.NET Core web API project targeting .NET Core is generated and opened in Visual Studio Code. + The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code. -1. After the status bar's OmniSharp flame icon turns green, a dialog asks **Required assets to build and debug are missing from 'BooksApi'. Add them?**. Select **Yes**. -1. Visit the [NuGet Gallery: MongoDB.Driver](https://www.nuget.org/packages/MongoDB.Driver/) to determine the latest stable version of the .NET driver for MongoDB. Open **Integrated Terminal** and navigate to the project root. Run the following command to install the .NET driver for MongoDB: +1. Once the OmniSharp server starts up , a dialog asks **Required assets to build and debug are missing from 'BookStoreApi'. Add them?**. Select **Yes**. +1. Open the **Integrated Terminal** and run the following command to install the .NET driver for MongoDB: ```dotnetcli - dotnet add BooksApi.csproj package MongoDB.Driver -v {VERSION} + dotnet add package MongoDB.Driver ``` # [Visual Studio for Mac](#tab/visual-studio-mac) -1. In Visual Studio for Mac earlier than version 8.6, select **File** > **New Solution** > **.NET Core** > **App** from the sidebar. In version 8.6 or later, select **File** > **New Solution** > **Web and Console** > **App** from the sidebar. +1. Slect **File** > **New Solution** > **Web and Console** > **App** from the sidebar. 1. Select the **ASP.NET Core** > **API** C# project template, and select **Next**. -1. Select **.NET Core 3.1** from the **Target Framework** drop-down list, and select **Next**. -1. Enter *BooksApi* for the **Project Name**, and select **Create**. +1. Select **.NET 6.0** from the **Target Framework** drop-down list, and select **Next**. +1. Enter *BookStoreApi* for the **Project Name**, and select **Create**. 1. In the **Solution** pad, right-click the project's **Dependencies** node and select **Add Packages**. 1. Enter *MongoDB.Driver* in the search box, select the *MongoDB.Driver* package, and select **Add Package**. 1. Select the **Accept** button in the **License Acceptance** dialog. @@ -189,146 +185,121 @@ The database is ready. You can start creating the ASP.NET Core web API. 1. Add a *Models* directory to the project root. 1. Add a `Book` class to the *Models* directory with the following code: - ```csharp - using MongoDB.Bson; - using MongoDB.Bson.Serialization.Attributes; - - namespace BooksApi.Models - { - public class Book - { - [BsonId] - [BsonRepresentation(BsonType.ObjectId)] - public string Id { get; set; } - - [BsonElement("Name")] - public string BookName { get; set; } - - public decimal Price { get; set; } - - public string Category { get; set; } - - public string Author { get; set; } - } - } - ``` + :::code language="csharp" source="first-mongo-app/samples_snapshot/6.x/Book.cs"::: In the preceding class, the `Id` property is: * Required for mapping the Common Language Runtime (CLR) object to the MongoDB collection. - * Annotated with [`[BsonId]`](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonIdAttribute.htm) to make this property the document's primary key. - * Annotated with [`[BsonRepresentation(BsonType.ObjectId)]`](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonRepresentationAttribute.htm) to allow passing the parameter as type `string` instead of an [ObjectId](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Bson_ObjectId.htm) structure. Mongo handles the conversion from `string` to `ObjectId`. + * Annotated with [`[BsonId]`](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonIdAttribute.htm) to make this property the document's primary key. + * Annotated with [`[BsonRepresentation(BsonType.ObjectId)]`](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonRepresentationAttribute.htm) to allow passing the parameter as type `string` instead of an [ObjectId](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_ObjectId.htm) structure. Mongo handles the conversion from `string` to `ObjectId`. - The `BookName` property is annotated with the [`[BsonElement]`](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonElementAttribute.htm) attribute. The attribute's value of `Name` represents the property name in the MongoDB collection. + The `BookName` property is annotated with the [`[BsonElement]`](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonElementAttribute.htm) attribute. The attribute's value of `Name` represents the property name in the MongoDB collection. ## Add a configuration model 1. Add the following database configuration values to *appsettings.json*: - :::code language="json" source="first-mongo-app/samples/3.x/SampleApp/appsettings.json" highlight="2-6"::: + :::code language="json" source="first-mongo-app/samples/6.x/BookStoreApi/appsettings.json" highlight="2-6"::: -1. Add a *BookstoreDatabaseSettings.cs* file to the *Models* directory with the following code: +1. Add a `BookStoreDatabaseSettings` class to the *Models* directory with the following code: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Models/BookstoreDatabaseSettings.cs"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs"::: - The preceding `BookstoreDatabaseSettings` class is used to store the *appsettings.json* file's `BookstoreDatabaseSettings` property values. The JSON and C# property names are named identically to ease the mapping process. + The preceding `BookStoreDatabaseSettings` class is used to store the *appsettings.json* file's `BookStoreDatabase` property values. The JSON and C# property names are named identically to ease the mapping process. -1. Add the following highlighted code to `Startup.ConfigureServices`: +1. Add the following highlighted code to *Program.cs*: - :::code language="csharp" source="first-mongo-app/samples_snapshot/3.x/SampleApp/Startup.ConfigureServices.AddDbSettings.cs" highlight="3-8"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Program.cs" id="snippet_BookStoreDatabaseSettings" highlight="4-5"::: - In the preceding code: + In the preceding code, the configuration instance to which the *appsettings.json* file's `BookStoreDatabase` section binds is registered in the Dependency Injection (DI) container. For example, the `BookStoreDatabaseSettings` object's `ConnectionString` property is populated with the `BookStoreDatabase:ConnectionString` property in *appsettings.json*. - * The configuration instance to which the *appsettings.json* file's `BookstoreDatabaseSettings` section binds is registered in the Dependency Injection (DI) container. For example, a `BookstoreDatabaseSettings` object's `ConnectionString` property is populated with the `BookstoreDatabaseSettings:ConnectionString` property in *appsettings.json*. - * The `IBookstoreDatabaseSettings` interface is registered in DI with a singleton [service lifetime](xref:fundamentals/dependency-injection#service-lifetimes). When injected, the interface instance resolves to a `BookstoreDatabaseSettings` object. +1. Add the following code to the top of *Program.cs* to resolve the `BookStoreDatabaseSettings` reference: -1. Add the following code to the top of *Startup.cs* to resolve the `BookstoreDatabaseSettings` and `IBookstoreDatabaseSettings` references: - - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Startup.cs" id="snippet_UsingBooksApiModels"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Program.cs" id="snippet_UsingModels"::: ## Add a CRUD operations service 1. Add a *Services* directory to the project root. -1. Add a `BookService` class to the *Services* directory with the following code: +1. Add a `BooksService` class to the *Services* directory with the following code: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Services/BookService.cs" id="snippet_BookServiceClass"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Services/BooksService.cs" id="snippet_File"::: - In the preceding code, an `IBookstoreDatabaseSettings` instance is retrieved from DI via constructor injection. This technique provides access to the *appsettings.json* configuration values that were added in the [Add a configuration model](#add-a-configuration-model) section. + In the preceding code, a `BookStoreDatabaseSettings` instance is retrieved from DI via constructor injection. This technique provides access to the *appsettings.json* configuration values that were added in the [Add a configuration model](#add-a-configuration-model) section. -1. Add the following highlighted code to `Startup.ConfigureServices`: +1. Add the following highlighted code to *Program.cs*: - :::code language="csharp" source="first-mongo-app/samples_snapshot/3.x/SampleApp/Startup.ConfigureServices.AddSingletonService.cs" highlight="9"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Program.cs" id="snippet_BooksService" highlight="7"::: - In the preceding code, the `BookService` class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because `BookService` takes a direct dependency on `MongoClient`. Per the official [Mongo Client reuse guidelines](https://mongodb.github.io/mongo-csharp-driver/2.8/reference/driver/connecting/#re-use), `MongoClient` should be registered in DI with a singleton service lifetime. + In the preceding code, the `BooksService` class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because `BooksService` takes a direct dependency on `MongoClient`. Per the official [Mongo Client reuse guidelines](https://mongodb.github.io/mongo-csharp-driver/2.14/reference/driver/connecting/#re-use), `MongoClient` should be registered in DI with a singleton service lifetime. -1. Add the following code to the top of *Startup.cs* to resolve the `BookService` reference: +1. Add the following code to the top of *Program.cs* to resolve the `BooksService` reference: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Startup.cs" id="snippet_UsingBooksApiServices"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Program.cs" id="snippet_UsingServices"::: -The `BookService` class uses the following `MongoDB.Driver` members to run CRUD operations against the database: +The `BooksService` class uses the following `MongoDB.Driver` members to run CRUD operations against the database: -* [MongoClient](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Driver_MongoClient.htm): Reads the server instance for running database operations. The constructor of this class is provided the MongoDB connection string: +* [MongoClient](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_MongoClient.htm): Reads the server instance for running database operations. The constructor of this class is provided the MongoDB connection string: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Services/BookService.cs" id="snippet_BookServiceConstructor" highlight="3"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Services/BooksService.cs" id="snippet_ctor" highlight="4-5"::: -* [IMongoDatabase](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Driver_IMongoDatabase.htm): Represents the Mongo database for running operations. This tutorial uses the generic [GetCollection\(collection)](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/M_MongoDB_Driver_IMongoDatabase_GetCollection__1.htm) method on the interface to gain access to data in a specific collection. Run CRUD operations against the collection after this method is called. In the `GetCollection(collection)` method call: +* [IMongoDatabase](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_IMongoDatabase.htm): Represents the Mongo database for running operations. This tutorial uses the generic [GetCollection\(collection)](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoDatabase_GetCollection__1.htm) method on the interface to gain access to data in a specific collection. Run CRUD operations against the collection after this method is called. In the `GetCollection(collection)` method call: * `collection` represents the collection name. * `TDocument` represents the CLR object type stored in the collection. -`GetCollection(collection)` returns a [MongoCollection](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/T_MongoDB_Driver_MongoCollection.htm) object representing the collection. In this tutorial, the following methods are invoked on the collection: +`GetCollection(collection)` returns a [MongoCollection](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_MongoCollection.htm) object representing the collection. In this tutorial, the following methods are invoked on the collection: -* [DeleteOne](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_DeleteOne.htm): Deletes a single document matching the provided search criteria. -* [Find\](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/M_MongoDB_Driver_IMongoCollectionExtensions_Find__1_1.htm): Returns all documents in the collection matching the provided search criteria. -* [InsertOne](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_InsertOne.htm): Inserts the provided object as a new document in the collection. -* [ReplaceOne](https://mongodb.github.io/mongo-csharp-driver/2.11/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_ReplaceOne.htm): Replaces the single document matching the provided search criteria with the provided object. +* [DeleteOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_DeleteOneAsync_1.htm): Deletes a single document matching the provided search criteria. +* [Find\](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollectionExtensions_Find__1.htm): Returns all documents in the collection matching the provided search criteria. +* [InsertOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_InsertOneAsync_1.htm): Inserts the provided object as a new document in the collection. +* [ReplaceOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_ReplaceOneAsync.htm): Replaces the single document matching the provided search criteria with the provided object. ## Add a controller Add a `BooksController` class to the *Controllers* directory with the following code: -:::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Controllers/BooksController.cs"::: +:::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Controllers/BooksController.cs"::: The preceding web API controller: -* Uses the `BookService` class to run CRUD operations. +* Uses the `BooksService` class to run CRUD operations. * Contains action methods to support GET, POST, PUT, and DELETE HTTP requests. -* Calls in the `Create` action method to return an [HTTP 201](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) response. Status code 201 is the standard response for an HTTP POST method that creates a new resource on the server. `CreatedAtRoute` also adds a `Location` header to the response. The `Location` header specifies the URI of the newly created book. +* Calls in the `Create` action method to return an [HTTP 201](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) response. Status code 201 is the standard response for an HTTP POST method that creates a new resource on the server. `CreatedAtAction` also adds a `Location` header to the response. The `Location` header specifies the URI of the newly created book. ## Test the web API 1. Build and run the app. -1. Navigate to `https://localhost:/api/books` to test the controller's parameterless `Get` action method. The following JSON response is displayed: +1. Navigate to `https://localhost:/api/books`, where `` is the automatically assigned port number for the app, to test the controller's parameterless `Get` action method. A JSON response similar to the following is displayed: ```json [ { - "id":"5bfd996f7b8e48dc15ff215d", - "bookName":"Design Patterns", - "price":54.93, - "category":"Computers", - "author":"Ralph Johnson" + "id": "61a6058e6c43f32854e51f51", + "bookName": "Design Patterns", + "price": 54.93, + "category": "Computers", + "author": "Ralph Johnson" }, { - "id":"5bfd996f7b8e48dc15ff215e", - "bookName":"Clean Code", - "price":43.15, - "category":"Computers", - "author":"Robert C. Martin" + "id": "61a6058e6c43f32854e51f52", + "bookName": "Clean Code", + "price": 43.15, + "category": "Computers", + "author": "Robert C. Martin" } ] ``` -1. Navigate to `https://localhost:/api/books/{id here}` to test the controller's overloaded `Get` action method. The following JSON response is displayed: +1. Navigate to `https://localhost:/api/books/{id here}` to test the controller's overloaded `Get` action method. A JSON response similar to the following is displayed: ```json { - "id":"{ID}", - "bookName":"Clean Code", - "price":43.15, - "category":"Computers", - "author":"Robert C. Martin" + "id": "61a6058e6c43f32854e51f52", + "bookName": "Clean Code", + "price": 43.15, + "category": "Computers", + "author": "Robert C. Martin" } ``` @@ -341,23 +312,21 @@ There are two details to change about the JSON responses returned in the [Test t To satisfy the preceding requirements, make the following changes: -1. Json.NET has been removed from ASP.NET shared framework. Add a package reference to [`Microsoft.AspNetCore.Mvc.NewtonsoftJson`](https://nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson). +1. In *Program.cs*, chain the following highlighted code on to the `AddControllers` method call: -1. In `Startup.ConfigureServices`, chain the following highlighted code on to the `AddControllers` method call: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Program.cs" id="snippet_AddControllers" highlight="10-11"::: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Startup.cs" id="snippet_ConfigureServices" highlight="12"::: + With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the `Book` class's `Author` property serializes as `Author` instead of `author`. - With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the `Book` class's `Author` property serializes as `Author`. +1. In *Models/Book.cs*, annotate the `BookName` property with the [`[JsonPropertyName]`](xref:System.Text.Json.Serialization.JsonPropertyNameAttribute) attribute: -1. In *Models/Book.cs*, annotate the `BookName` property with the following [`[JsonProperty]`](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm) attribute: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Models/Book.cs" id="snippet_BookName" highlight="2"::: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Models/Book.cs" id="snippet_BookNameProperty" highlight="2"::: - - The `[JsonProperty]` attribute's value of `Name` represents the property name in the web API's serialized JSON response. + The `[JsonPropertyName]` attribute's value of `Name` represents the property name in the web API's serialized JSON response. 1. Add the following code to the top of *Models/Book.cs* to resolve the `[JsonProperty]` attribute reference: - :::code language="csharp" source="first-mongo-app/samples/3.x/SampleApp/Models/Book.cs" id="snippet_NewtonsoftJsonImport"::: + :::code language="csharp" source="first-mongo-app/samples/6.x/BookStoreApi/Models/Book.cs" id="snippet_UsingSystemTextJsonSerialization"::: 1. Repeat the steps defined in the [Test the web API](#test-the-web-api) section. Notice the difference in JSON property names. @@ -365,11 +334,9 @@ To satisfy the preceding requirements, make the following changes: [!INCLUDE[](~/includes/DuendeIdentityServer.md)] -## Next steps +## Additional resources -For more information on building ASP.NET Core web APIs, see the following resources: - -* [YouTube version of this article](https://www.youtube.com/watch?v=7uJt_sOenyo&feature=youtu.be) +* [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/tutorials/first-mongo-app/samples) ([how to download](xref:index#how-to-download-a-sample)) * * * [Microsoft Learn: Create a web API with ASP.NET Core](/learn/modules/build-web-api-aspnet-core/) diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/BookStoreApi.csproj b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/BookStoreApi.csproj new file mode 100644 index 0000000000..760e4e17e0 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/BookStoreApi.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Controllers/BooksController.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Controllers/BooksController.cs new file mode 100644 index 0000000000..76edc65f14 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Controllers/BooksController.cs @@ -0,0 +1,72 @@ +using BookStoreApi.Models; +using BookStoreApi.Services; +using Microsoft.AspNetCore.Mvc; + +namespace BookStoreApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class BooksController : ControllerBase +{ + private readonly BooksService _booksService; + + public BooksController(BooksService booksService) => + _booksService = booksService; + + [HttpGet] + public async Task> Get() => + await _booksService.GetAsync(); + + [HttpGet("{id:length(24)}")] + public async Task> Get(string id) + { + var book = await _booksService.GetAsync(id); + + if (book is null) + { + return NotFound(); + } + + return book; + } + + [HttpPost] + public async Task Post(Book newBook) + { + await _booksService.CreateAsync(newBook); + + return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook); + } + + [HttpPut("{id:length(24)}")] + public async Task Update(string id, Book updatedBook) + { + var book = await _booksService.GetAsync(id); + + if (book is null) + { + return NotFound(); + } + + updatedBook.Id = book.Id; + + await _booksService.UpdateAsync(id, updatedBook); + + return NoContent(); + } + + [HttpDelete("{id:length(24)}")] + public async Task Delete(string id) + { + var book = await _booksService.GetAsync(id); + + if (book is null) + { + return NotFound(); + } + + await _booksService.RemoveAsync(book.Id); + + return NoContent(); + } +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Controllers/WeatherForecastController.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000000..d82833c106 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Controllers/WeatherForecastController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace BookStoreApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Models/Book.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Models/Book.cs new file mode 100644 index 0000000000..0706bcfef6 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Models/Book.cs @@ -0,0 +1,26 @@ +// +using System.Text.Json.Serialization; +// +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace BookStoreApi.Models; + +public class Book +{ + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + public string? Id { get; set; } + + // + [BsonElement("Name")] + [JsonPropertyName("Name")] + public string BookName { get; set; } = null!; + // + + public decimal Price { get; set; } + + public string Category { get; set; } = null!; + + public string Author { get; set; } = null!; +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs new file mode 100644 index 0000000000..2d4bc564f5 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs @@ -0,0 +1,10 @@ +namespace BookStoreApi.Models; + +public class BookStoreDatabaseSettings +{ + public string ConnectionString { get; set; } = null!; + + public string DatabaseName { get; set; } = null!; + + public string BooksCollectionName { get; set; } = null!; +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Program.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Program.cs new file mode 100644 index 0000000000..68c00d3561 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Program.cs @@ -0,0 +1,44 @@ +// +using BookStoreApi.Models; +// +// +using BookStoreApi.Services; +// + +// +// +// +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.Configure( + builder.Configuration.GetSection("BookStoreDatabase")); +// + +builder.Services.AddSingleton(); +// + +builder.Services.AddControllers() + .AddJsonOptions( + options => options.JsonSerializerOptions.PropertyNamingPolicy = null); +// + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Services/BooksService.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Services/BooksService.cs new file mode 100644 index 0000000000..3565c8b436 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/Services/BooksService.cs @@ -0,0 +1,42 @@ +// +using BookStoreApi.Models; +using Microsoft.Extensions.Options; +using MongoDB.Driver; + +namespace BookStoreApi.Services; + +public class BooksService +{ + private readonly IMongoCollection _booksCollection; + + // + public BooksService( + IOptions bookStoreDatabaseSettings) + { + var mongoClient = new MongoClient( + bookStoreDatabaseSettings.Value.ConnectionString); + + var mongoDatabase = mongoClient.GetDatabase( + bookStoreDatabaseSettings.Value.DatabaseName); + + _booksCollection = mongoDatabase.GetCollection( + bookStoreDatabaseSettings.Value.BooksCollectionName); + } + // + + public async Task> GetAsync() => + await _booksCollection.Find(_ => true).ToListAsync(); + + public async Task GetAsync(string id) => + await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync(); + + public async Task CreateAsync(Book newBook) => + await _booksCollection.InsertOneAsync(newBook); + + public async Task UpdateAsync(string id, Book updatedBook) => + await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook); + + public async Task RemoveAsync(string id) => + await _booksCollection.DeleteOneAsync(x => x.Id == id); +} +// diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/WeatherForecast.cs b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/WeatherForecast.cs new file mode 100644 index 0000000000..d5ab0bc26a --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace BookStoreApi; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/appsettings.Development.json b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/appsettings.Development.json new file mode 100644 index 0000000000..a34cd70c53 --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/appsettings.json b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/appsettings.json new file mode 100644 index 0000000000..340116badc --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples/6.x/BookStoreApi/appsettings.json @@ -0,0 +1,14 @@ +{ + "BookStoreDatabase": { + "ConnectionString": "mongodb://localhost:27017", + "DatabaseName": "BookStore", + "BooksCollectionName": "Books" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/aspnetcore/tutorials/first-mongo-app/samples_snapshot/6.x/Book.cs b/aspnetcore/tutorials/first-mongo-app/samples_snapshot/6.x/Book.cs new file mode 100644 index 0000000000..97756d7d5d --- /dev/null +++ b/aspnetcore/tutorials/first-mongo-app/samples_snapshot/6.x/Book.cs @@ -0,0 +1,20 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace BookStoreApi.Models; + +public class Book +{ + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + public string? Id { get; set; } + + [BsonElement("Name")] + public string BookName { get; set; } = null!; + + public decimal Price { get; set; } + + public string Category { get; set; } = null!; + + public string Author { get; set; } = null!; +}