NoSQL | MongoDB入門實戰(zhàn)教程(5)

前面我們學(xué)習(xí)了MongoDB的基本查詢命令操作,作為后端開發(fā)的我們大部分場景都是在應(yīng)用程序中和MongoDB進行交互,因此本篇我們來學(xué)習(xí)一下如何在ASP.NET Core中集成MongoDB。
首先,請參照之前的文章安裝部署好一個MongoDB節(jié)點或集群。
然后,手動創(chuàng)建一個數(shù)據(jù)庫BookStoreDB 和 一個集合 Books。
use BookStoreDBdb.CreateCollection('Books')
最后,預(yù)先插入兩條測試數(shù)據(jù):
db.Books.insertMany([{'Name':'Design Patterns','Price':54.93,'Category':'Computers','Author':'Ralph Johnson','CreatedDate':ISODate("2012-10-02T07:58:51Z"),'UpdatedDate':ISODate("2012-10-02T07:58:51Z")},{'Name':'Clean Code','Price':43.15,'Category':'Computers','Author':'Robert C. Martin','CreatedDate':ISODate("2012-10-02T07:58:51Z"),'UpdatedDate':ISODate("2012-10-02T07:58:51Z")}])
創(chuàng)建WebAPI項目
建立一個ASP.NET Core 或 ASP.NET 5的WebAPI項目。
通過NuGet安裝MongoDB.Driver:
PM>Install-Package MongoDB.Driver
目前MongoDB.Driver最新版本為2.12.4(2021年6月5日發(fā)布)。一般來說,我們操作MongoDB都會選擇這個官方的Driver。
添加實體模型
在WebAPI項目中添加Models目錄,并增加 Book 實體類:
public class Book : MongoDocBase{[]public string BookName { get; set; }public decimal Price { get; set; }public string Category { get; set; }public string Author { get; set; }}public class MongoDocBase{[][]public string Id { get; set; }[]public DateTime? CreatedDate { get; set; }[]public DateTime? UpdatedDate { get; set; }}
需要注意的是:MongoDB存儲時間類型數(shù)據(jù)時,都是先轉(zhuǎn)換為UTC時間,然后存儲到數(shù)據(jù)庫中。當(dāng)我們?nèi)〕龃鎯Φ臅r間時,就會出現(xiàn)時差的問題。因此,一般我們會給文檔中的日期類型加上如下所示的注解,將它轉(zhuǎn)換為本地時間傳輸:
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
在實際應(yīng)用中,我們會給實體類創(chuàng)建一些DTO,然后在應(yīng)用層進行DTO向DO的轉(zhuǎn)換。因此,這里我們假設(shè)會進行 創(chuàng)建Book 和 修改Book 的操作,創(chuàng)建兩個DTO類:
(1)CreateBookDto
public class CreateBookDto{public string BookName { get; set; }public decimal Price { get; set; }public string Category { get; set; }public string Author { get; set; }[]public DateTime CreatedDate { get; set; } = DateTime.Now;}
(2)UpdateBookDto
public class UpdateBookDto : CreateBookDto{public string Id { get; set; }[]public DateTime UpdatedDate { get; set; } = DateTime.Now;}
在本示例中,使用AutoMapper進行DTO和DO之間的互轉(zhuǎn),因此,安裝 AutoMapper 及其 擴展:
PM>Install-Package AutoMapperPM>Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
創(chuàng)建映射規(guī)則配置:
public class MappingConfigs : Profile{public MappingConfigs(){CreateMap<CreateBookDto, Book>().ReverseMap();CreateMap<UpdateBookDto, Book>().ForMember(dest => dest.CreatedDate, opt => opt.Ignore()).ReverseMap();}}
添加配置模型
首先,在appSettings.json中添加以下數(shù)據(jù)庫配置:
{......"BookstoreDatabaseSettings": {"BooksCollectionName": "Books","ConnectionString": "mongodb://mongo-master:27017","DatabaseName": "BookStoreDB"}}
需要注意的是:這里的連接字符串指向的是一個沒有設(shè)置用戶名密碼的MongoDB節(jié)點。如果你的MongoDB節(jié)點設(shè)置了用戶名密碼 或者 復(fù)制集分片集 之類的,請修改為匹配的連接字符串。
然后,創(chuàng)建一個配置項類,也放到Models目錄中:
namespace EDT.BookStore.API.Models{public class BookStoreDatabaseSettings : IBookStoreDatabaseSettings{public string BooksCollectionName { get; set; }public string ConnectionString { get; set; }public string DatabaseName { get; set; }}public interface IBookStoreDatabaseSettings{string BooksCollectionName { get; set; }string ConnectionString { get; set; }string DatabaseName { get; set; }}}
最后,將其加入到IoC容器中控制:
public void ConfigureServices(IServiceCollection services){.....// AutoMapper Settingsservices.AddAutoMapper(typeof(MappingConfigs));// MongoDB Settingsservices.Configure<BookStoreDatabaseSettings>(Configuration.GetSection(nameof(BookStoreDatabaseSettings)));services.AddSingleton<IBookStoreDatabaseSettings>(sp =>sp.GetRequiredService<IOptions<BookStoreDatabaseSettings>>().Value);}
這里,IBookstoreDatabaseSettings 接口使用單一實例服務(wù)生存期在 DI 中注冊。在注入時,接口實例時將解析為 BookStoreDatabaseSettings 對象。
添加BookService提供CRUD服務(wù)
在Services目錄下,創(chuàng)建IBookService接口:
namespace EDT.BookStore.API.Services{public interface IBookService{IList<Book> Get();Task<IList<Book>> GetAsync();Book Get(string id);Task<Book> GetAsync(string id);Book Create(Book book);Task<Book> CreateAsync(Book book);void Update(string id, Book bookIn);Task UpdateAsync(string id, Book bookIn);void Remove(string id);Task RemoveAsync(string id);}}
然后,創(chuàng)建BookService 實現(xiàn) IBookService 接口。
namespace EDT.BookStore.API.Services{public class BookService : IBookService{private readonly IMongoCollection<Book> _books;public BookService(IBookStoreDatabaseSettings settings){var mongoClient = new MongoClient(settings.ConnectionString);var mongoDatabase = mongoClient.GetDatabase(settings.DatabaseName);_books = mongoDatabase.GetCollection<Book>(settings.BooksCollectionName);}public Book Create(Book book){_books.InsertOne(book);return book;}public async Task<Book> CreateAsync(Book book){await _books.InsertOneAsync(book);return book;}public IList<Book> Get(){return _books.Find(book => true).ToList();}public async Task<IList<Book>> GetAsync(){return await _books.Find(book => true).ToListAsync();}public Book Get(string id){return _books.Find(book => book.Id == id).FirstOrDefault();}public async Task<Book> GetAsync(string id){return await _books.Find(book => book.Id == id).FirstOrDefaultAsync();}public void Remove(string id){_books.DeleteOne(book => book.Id == id);}public async Task RemoveAsync(string id){await _books.DeleteOneAsync(book => book.Id == id);}public void Update(string id, Book bookIn){_books.ReplaceOne(book => book.Id == id, bookIn);}public async Task UpdateAsync(string id, Book bookIn){await _books.ReplaceOneAsync(book => book.Id == id, bookIn);}}}
在上面的代碼中,會通過構(gòu)造函數(shù)從DI檢索IBookStoreDatabaseSettings實例獲取MongoDB連接字符串、數(shù)據(jù)庫名 和 集合名。
當(dāng)然,我們也可以使用 約定大于配置 的方式,統(tǒng)一采用實體類的名字 作為默認(rèn)的 集合名,示例如下:
_books = mongoDatabase.GetCollection<Book>(typeof(Book).Name);
最后,將BookService也加入到IoC容器中:
services.AddSingleton<IBookService, BookService>();
這里,將BookService作為單一實例注入,這是因為 BookService 直接依賴于 MongoClient,而根據(jù)官方Mongo Client重用準(zhǔn)則,我們應(yīng)該使用單一實例服務(wù)在IoC容器中注入MongoClient。
添加Controller提供應(yīng)用層接口
在Controllers目錄下,新增 BookController 控制器:
namespace EDT.BookStore.API.Controllers{[][]public class BookController : ControllerBase{private readonly IMapper _mapper;private readonly IBookService _bookService;public BookController(IMapper mapper, IBookService bookService){_mapper = mapper;_bookService = bookService;}[][][]public async Task<ActionResult<IList<Book>>> Get(){var books = await _bookService.GetAsync();if (books == null){return NoContent();}return Ok(books);}[][][]public async Task<ActionResult<Book>> Get(string id){var book = await _bookService.GetAsync(id);if (book == null){return NotFound();}return Ok(book);}[][]public async Task<ActionResult<Book>> Create(CreateBookDto bookDto){var book = _mapper.Map<Book>(bookDto);await _bookService.CreateAsync(book);return CreatedAtRoute("GetBook", new { id = book.Id.ToString() }, bookDto);}[][][]public async Task<ActionResult> Update(string id, UpdateBookDto bookDto){var book = await _bookService.GetAsync(id);if (book == null){return NotFound();}_mapper.Map(bookDto, book);await _bookService.UpdateAsync(id, book);return Ok();}[][][]public async Task<ActionResult> Update(string id){var book = await _bookService.GetAsync(id);if (book == null){return NotFound();}await _bookService.RemoveAsync(id);return Ok();}}}
生成該ASP.NET Core WebAPI應(yīng)用,啟動之后在Swagger頁面進行測試

點擊GET /Book接口,測試結(jié)果如下:

其他接口測試結(jié)果不再贅述,有興趣的童鞋可以自行跑起來測試一下。
示例github地址:https://github.com/EdisonChou/EDT.Mongo.Sample
本文總結(jié)了如何在ASP.NET Core/ASP.NET 5應(yīng)用程序中操作MongoDB,展示了一個麻雀雖小但五臟俱全的示例,希望能對你有所幫助。
參考資料
Microsoft Doc,使用ASP.NET Core和MongoDB創(chuàng)建WebAPI,傳送門:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/first-mongo-app?view=aspnetcore-5.0&tabs=visual-studio
唐建法,《MongoDB高手課》(極客時間)
郭遠(yuǎn)威,《MongoDB實戰(zhàn)指南》(圖書)
