.NET Core MongoDB資料倉儲和工作單元模式實操

追逐時光者發表於2023-04-11

前言

  上一章節我們主要講解了MongoDB資料倉儲和工作單元模式的封裝,這一章節主要講的是MongoDB使用者管理相關操作實操。如:獲取所有使用者資訊、獲取使用者分頁資料、透過使用者ID獲取對應使用者資訊、新增使用者資訊、事務新增使用者資訊、使用者資訊修改、使用者資訊刪除等實戰教程。

MongoDB從入門到實戰的相關教程

MongoDB從入門到實戰之MongoDB簡介?

MongoDB從入門到實戰之MongoDB快速入門?

MongoDB從入門到實戰之Docker快速安裝MongoDB?

MongoDB從入門到實戰之MongoDB工作常用操作命令?

MongoDB從入門到實戰之.NET Core使用MongoDB開發ToDoList系統(1)-後端專案框架搭建?

MongoDB從入門到實戰之.NET Core使用MongoDB開發ToDoList系統(2)-Swagger框架整合?

MongoDB從入門到實戰之.NET Core使用MongoDB開發ToDoList系統(3)-系統資料集合設計?

MongoDB從入門到實戰之.NET Core使用MongoDB開發ToDoList系統(4)-MongoDB資料倉儲和工作單元模式封裝?

MongoDB從入門到實戰之.NET Core使用MongoDB開發ToDoList系統(5)-MongoDB資料倉儲和工作單元模式實操?

YyFlight.ToDoList專案原始碼地址

歡迎各位看官老爺review,有幫助的別忘了給我個Star哦?!!!

GitHub地址:https://github.com/YSGStudyHards/YyFlight.ToDoList

MongoRepository地址:https://github.com/YSGStudyHards/YyFlight.ToDoList/tree/main/Repository/Repository

MongoDB事務使用前提說明

參閱MongoDB的事務

說明:

MongoDB單機伺服器不支援事務【使用MongoDB事務會報錯:Standalone servers do not support transactions】,只有在叢集情況下才支援事務,因為博主接下來都是在單機環境下操作,所以無法來演示Mongo事務操作,但是方法都已經是封裝好了的,大家可以自己搭建叢集實操。

原因:

MongoDB在使用分散式事務時需要進行多節點之間的協調和通訊,而單機環境下無法實現這樣的分散式協調和通訊機制。但是,在MongoDB部署為一個叢集(cluster)後,將多個計算機連線為一個整體,透過協調和通訊機制實現了分散式事務的正常使用。從資料一致性和可靠性的角度來看,在分散式系統中實現事務處理是至關重要的。而在單機環境下不支援事務,只有在叢集情況下才支援事務的設計方式是為了保證資料一致性和可靠性,並且也符合分散式系統的設計思想。

建立EntityBase公共類

一個公共的具有相同特性和行為的基類。

    public class EntityBase
    {
        /// <summary>
        /// 主鍵Id
        /// </summary>
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string Id { get; set; }

        /// <summary>
        /// 建立時間
        /// </summary>
        public DateTime CreateDate { get; set; }

        /// <summary>
        /// 更新時間
        /// </summary>
        public DateTime UpdateDate { get; set; }
    }

新增UserInfo使用者表實體對映模型

    [Table("yyflight_todolist_user")]
    public class UserInfo : EntityBase
    {
        /// <summary>
        /// 登入賬號
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// 登入密碼
        /// </summary>

        public string Password { get; set; }

        /// <summary>
        /// 使用者暱稱
        /// </summary>
        public string NickName { get; set; }

        /// <summary>
        /// 使用者頭像
        /// </summary>
        public string HeadPortrait { get; set; }

        /// <summary>
        /// 使用者郵箱
        /// </summary>
        public string Email { get; set; }

        /// <summary>
        /// 使用者狀態(0凍結,1正常,2登出)
        /// </summary>
        public int Status { get; set; }
    }

在前面類中,Id屬性中的特性的作用:

  • 需要用於將通用語言執行時(CLR)物件對映到MongoDB集合。
  • [BsonId]進行註釋,使該屬性成為文件的主鍵。
  • [BsonRepresentation(BsonType.ObjectId)]進行註釋,以允許以字串型別而不是ObjectId結構傳遞引數。Mongo處理從字串到ObjectId的轉換。沒有此特性序列化時會有如下異常提示:

System.FormatException: An error occurred while deserializing the Id property of class Repository.Domain.User.UserInfo: Cannot deserialize a 'String' from BsonType 'ObjectId'.

知識擴充MongoDB ObjectId型別概述:

每次插入一條資料系統都會自動插入一個_id鍵,鍵值不可以重複,它可以是任何型別的,也可以手動的插入,預設情況下它的資料型別是ObjectId,由於MongoDB在設計之初就是用作分散式資料庫,所以使用ObjectId可以避免不同資料庫中_id的重複(如果使用自增的方式在分散式系統中就會出現重複的_id的值)。
ObjectId使用12位元組的儲存空間,每個位元組可以儲存兩個十六進位制數字,所以一共可以儲存24個十六進位制數字組成的字串,在這24個字串中,前8位表示時間戳,接下來6位是一個機器碼,接下來4位表示程式id,最後6位表示計數器。

MongoDB 採用 ObjectId 來表示主鍵的型別,資料庫中每個文件都擁有一個_id 欄位表示主鍵,_id 的生成規則如下:

其中包括4-byte Unix 時間戳,3-byte 機器 ID,2-byte 程式 ID,3-byte 計數器(初始化隨機)

601e2b6b  a3203c  c89f   2d31aa
   ↑        ↑       ↑       ↑
 時間戳    機器碼   程式ID   隨機數 

建立使用者Repository

建立使用者IUserRepository介面

    public interface IUserRepository : IMongoRepository<UserInfo>
    {
    }

建立使用者UserRepository類

    public class UserRepository : MongoBaseRepository<UserInfo>, IUserRepository
    {
        public UserRepository(IMongoContext context) : base(context)
        {
        }
    }

建立使用者管理業務程式碼

建立IUserOperationExampleServices介面

    public interface IUserOperationExampleServices
    {
        /// <summary>
        /// 獲取所有使用者資訊
        /// </summary>
        /// <returns></returns>
        Task<IEnumerable<UserInfo>> GetAllUserInfos();

        /// <summary>
        /// 使用者分頁資料獲取
        /// </summary>
        /// <param name="userInfoByPageListReq">userInfoByPageListReq</param>
        /// <returns></returns>
        Task<IEnumerable<UserInfo>> GetUserInfoByPageList(UserInfoByPageListReq userInfoByPageListReq);

        /// <summary>
        /// 透過使用者ID獲取對應使用者資訊
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        Task<UserInfo> GetUserInfoById(string id);

        /// <summary>
        /// 新增使用者資訊
        /// </summary>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        Task<UserInfo> AddUserInfo(UserInfoReq userInfo);

        /// <summary>
        /// 事務新增使用者資訊
        /// </summary>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        Task<UserInfo> AddUserInfoTransactions(UserInfoReq userInfo);

        /// <summary>
        /// 使用者資訊修改
        /// </summary>
        /// <param name="id">id</param>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        Task<UserInfo> UpdateUserInfo(string id, UserInfoReq userInfo);

        /// <summary>
        /// 使用者資訊刪除
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        Task<bool> Delete(string id);
    }

建立UserOperationExampleServices類

    public class UserOperationExampleServices : IUserOperationExampleServices
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IUserRepository _userRepository;

        /// <summary>
        /// 依賴注入
        /// </summary>
        /// <param name="unitOfWork">unitOfWork</param>
        /// <param name="userRepository">userRepository</param>
        public UserOperationExampleServices(IUnitOfWork unitOfWork, IUserRepository userRepository)
        {
            _unitOfWork = unitOfWork;
            _userRepository = userRepository;
        }

        /// <summary>
        /// 獲取所有使用者資訊
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<UserInfo>> GetAllUserInfos()
        {
            var getAllUserInfos = await _userRepository.GetAllAsync();
            return getAllUserInfos;
        }

        /// <summary>
        /// 使用者分頁資料獲取
        /// </summary>
        /// <param name="userInfoByPageListReq">userInfoByPageListReq</param>
        /// <returns></returns>
        public async Task<IEnumerable<UserInfo>> GetUserInfoByPageList(UserInfoByPageListReq request)
        {
            //建立查詢條件構造器
            FilterDefinitionBuilder<UserInfo> buildFilter = Builders<UserInfo>.Filter;
            FilterDefinition<UserInfo> filter = buildFilter.Empty;
            SortDefinition<UserInfo> sort = Builders<UserInfo>.Sort.Ascending(m => m.CreateDate);
            if (!string.IsNullOrEmpty(request.NickName))
            {
                filter = buildFilter.Eq(m => m.NickName, request.NickName);
            }

            if (!string.IsNullOrEmpty(request.Id))
            {
                filter = buildFilter.Eq(m => m.Id, request.Id);
            }

            var list = await _userRepository.FindListByPageAsync(filter, request.PageIndex, request.PageSize, Array.Empty<string>(), sort);
            return list;
        }

        /// <summary>
        /// 透過使用者ID獲取對應使用者資訊
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        public async Task<UserInfo> GetUserInfoById(string id)
        {
            var getUserInfo = await _userRepository.GetByIdAsync(id);
            return getUserInfo;
        }

        /// <summary>
        /// 新增使用者資訊
        /// </summary>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        public async Task<UserInfo> AddUserInfo(UserInfoReq userInfo)
        {
            var addUserInfo = new UserInfo()
            {
                Id = ObjectId.GenerateNewId().ToString(),
                UserName = userInfo.UserName,
                Email = userInfo.Email,
                NickName = userInfo.NickName,
                Password = MD5Helper.MDString(userInfo.Password),
                Status = 1,
                HeadPortrait = userInfo.HeadPortrait,
                CreateDate = DateTime.Now,
                UpdateDate = DateTime.Now,
            };
            await _userRepository.AddAsync(addUserInfo);
            var queryUserInfo = await _userRepository.GetByIdAsync(addUserInfo.Id);
            return queryUserInfo;
        }

        /// <summary>
        /// 事務新增使用者資訊
        /// </summary>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        public async Task<UserInfo> AddUserInfoTransactions(UserInfoReq userInfo)
        {
            using var session = await _unitOfWork.InitTransaction();
            var addUserInfo = new UserInfo()
            {
                Id = ObjectId.GenerateNewId().ToString(),
                UserName = userInfo.UserName,
                Email = userInfo.Email,
                NickName = userInfo.NickName,
                Password = MD5Helper.MDString(userInfo.Password),
                Status = 1,
                HeadPortrait = userInfo.HeadPortrait,
                CreateDate = DateTime.Now,
                UpdateDate = DateTime.Now,
            };
            await _userRepository.AddTransactionsAsync(session, addUserInfo);

            //查不到任何資訊
            var queryUserInfo = await _userRepository.GetByIdAsync(addUserInfo.Id);

            //提交新增使用者資訊操作
            await _unitOfWork.Commit(session);

            //UserInfo只有在提交後才會被新增
            queryUserInfo = await _userRepository.GetByIdAsync(addUserInfo.Id);

            return queryUserInfo;
        }

        /// <summary>
        /// 使用者資訊修改
        /// </summary>
        /// <param name="id">id</param>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        public async Task<UserInfo> UpdateUserInfo(string id, UserInfoReq userInfo)
        {
            #region 指定欄位和條件修改

            //修改條件
            var list = new List<FilterDefinition<UserInfo>>
            {
                Builders<UserInfo>.Filter.Eq("_id", new ObjectId(id))
            };
            var filter = Builders<UserInfo>.Filter.And(list);

            //指定要修改的欄位內容
            //參考文章:https://chsakell.gitbook.io/mongodb-csharp-docs/crud-basics/update-documents
            var updateDefinition = Builders<UserInfo>.Update.
                Set(u => u.HeadPortrait, userInfo.HeadPortrait).
                Set(u => u.NickName, userInfo.NickName).
                Set(u => u.Status, userInfo.Status);

            await _userRepository.UpdateAsync(filter, updateDefinition);

            #endregion

            #region 指定物件非同步修改一條資料

            //var updateUserInfo = new UserInfo
            //{
            //    UserName = userInfo.UserName,
            //    Password = MD5Helper.MDString(userInfo.Password),
            //    Status = 1,
            //    HeadPortrait = userInfo.HeadPortrait,
            //    Email = userInfo.Email,
            //    NickName = userInfo.NickName,
            //    UpdateDate = DateTime.Now,
            //};
            //await _userRepository.UpdateAsync(updateUserInfo, id);

            #endregion

            #region 資料批次修改示例

            ////1.批次修改的條件(把建立時間CreateDate為近五日的使用者狀態更改為0)
            //var time = DateTime.Now;
            //var list = new List<FilterDefinition<UserInfo>>();
            //list.Add(Builders<UserInfo>.Filter.Gt("CreateDate", time));//大於當前時間
            //list.Add(Builders<UserInfo>.Filter.Lt("CreateDate", time.AddDays(5)));//小於當前時間+5day
            //var filter = Builders<UserInfo>.Filter.And(list);

            ////2.要修改的欄位內容
            //var dic = new Dictionary<string, string>
            //{
            //    { "Status", "0" }
            //};

            ////3.批次修改
            //await _userRepository.UpdateManayAsync(dic, filter);

            #endregion

            return await _userRepository.GetByIdAsync(id);
        }

        /// <summary>
        /// 使用者資訊刪除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<bool> Delete(string id)
        {
            await _userRepository.DeleteAsync(id);
            var testUserInfo = await _userRepository.GetByIdAsync(id);
            return testUserInfo == null;
        }
    }

UserOperationExample控制建立

    /// <summary>
    /// MongoDB使用者管理操作示例
    /// </summary>
    [ApiController]
    [Produces("application/json")]
    [Route("api/[controller]/[action]")]
    public class UserOperationExampleController : ControllerBase
    {
        private readonly IUserOperationExampleServices _userOperationExampleServices;

        /// <summary>
        /// 依賴注入
        /// </summary>
        /// <param name="userOperationExampleServices">userOperationExampleServices</param>
        public UserOperationExampleController(IUserOperationExampleServices userOperationExampleServices)
        {
            _userOperationExampleServices = userOperationExampleServices;
        }

        /// <summary>
        /// 獲取所有使用者資訊
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<ActionResult<IEnumerable<UserInfo>>> GetAllUserInfos()
        {
            var userInfos = await _userOperationExampleServices.GetAllUserInfos();
            return Ok(userInfos);
        }

        /// <summary>
        /// 獲取使用者分頁資料
        /// </summary>
        /// <param name="userInfoByPageListReq">userInfoByPageListReq</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult<IEnumerable<UserInfo>>> GetUserInfoByPageList([FromBody] UserInfoByPageListReq userInfoByPageListReq)
        {
            var getUserInfoByPageList = await _userOperationExampleServices.GetUserInfoByPageList(userInfoByPageListReq);
            return Ok(getUserInfoByPageList);
        }

        /// <summary>
        /// 透過使用者ID獲取對應使用者資訊
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        [HttpGet("{id}")]
        public async Task<ActionResult<UserInfo>> GetUserInfoById(string id)
        {
            var userInfo = await _userOperationExampleServices.GetUserInfoById(id);
            return Ok(userInfo);
        }

        /// <summary>
        /// 新增使用者資訊
        /// </summary>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult<UserInfo>> AddUserInfo([FromBody] UserInfoReq userInfo)
        {
            var addUserInfo = await _userOperationExampleServices.AddUserInfo(userInfo);
            return Ok(addUserInfo);
        }

        /// <summary>
        /// 事務新增使用者資訊
        /// </summary>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult<UserInfo>> AddUserInfoTransactions([FromBody] UserInfoReq userInfo)
        {
            //TODO:單機伺服器不支援事務使用【使用MongoDB事務會報錯:Standalone servers do not support transactions】,只有在叢集情況下才能用
            var addUserInfo = await _userOperationExampleServices.AddUserInfoTransactions(userInfo);
            return Ok(addUserInfo);
        }

        /// <summary>
        /// 使用者資訊修改
        /// </summary>
        /// <param name="id">id</param>
        /// <param name="userInfo">userInfo</param>
        /// <returns></returns>
        [HttpPut("{id}")]
        public async Task<ActionResult<UserInfo>> UpdateUserInfo(string id, [FromBody] UserInfoReq userInfo)
        {
            var updateUserInfo = await _userOperationExampleServices.UpdateUserInfo(id, userInfo);
            return Ok(updateUserInfo);
        }

        /// <summary>
        /// 使用者資訊刪除
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        [HttpDelete("{id}")]
        public async Task<ActionResult> Delete(string id)
        {
            var deleteUser = await _userOperationExampleServices.Delete(id);
            return Ok(deleteUser);
        }
    }

註冊資料庫基礎操作和工作單元

//註冊資料庫基礎操作和工作單元
builder.Services.AddScoped<IMongoContext, MongoContext>();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddScoped<IUserRepository, UserRepository>();

註冊相關應用服務

builder.Services.AddScoped<IUserOperationExampleServices, UserOperationExampleServices>();

Swagger使用者管理操作示例展示

新增使用者資訊

 新增成功,返回新增成功的使用者資訊:

透過使用者ID獲取對應使用者資訊

拿剛才新增成功的使用者ID,查詢使用者資訊:

獲取所有使用者資訊

使用者分頁資料獲取

查詢第1頁,顯示10條資料:

查詢第1頁,顯示2條資料:

 

 使用者資訊修改

指定要修改的欄位內容,修改HeadPortrait、NickName、Status
參考文章:https://chsakell.gitbook.io/mongodb-csharp-docs/crud-basics/update-documents

 

 修改成功:

使用者資訊刪除

輸入需要刪除的使用者ID,點選Execute刪除:

 

相關文章