NoSQL 是泛指非关系型的数据库, 现今在我们的项目中也多有使用, 其独特的优点为我们的项目架构带来了不少亮点, 而我们这里的主角 (MongoDB) 则是 NoSQL 数据库家族中的一种. 事实上, NoSQL 数据库的应用场景有很多, 其最主要的目的就是为了能解决大规模数据集合多重数据种类带来的困难, 及大数据应用的难题.
MongoDB
MongoDB 是一个开源的文档型数据库, 由 C++ 语言编写, 采用分布式的文件存储方案, 而文件的存储格式为 BSON.MongoDB 支持的数据类型有很多种, 如: String,Int,Float,Timestamp,Binary,Object,Date,Arrays 等. 而 MongoDB 的特点也有很多, 如: 强大的查询语言, 支持索引, 支持自动处理碎片, 支持 JAVA/C#/Python 等多种开发语言, 支持面向集合存储等. 关于 MongoDB 的具体描述也可自行到其官网查看, 我这里就不再过多啰嗦了, 下面贴出与其几个相关的网址吧:
MongoDB 官网: https://www.MongoDB.com/
MongoDB 官方的. NET API 文档:
MongoDB 官方的 GitHub 网址: https://GitHub.com/MongoDB/mongo
MongoDB 可视化管理工具 NoSQL Manager for MongoDB 的官网: https://www.mongodbmanager.com/
背景
在大部分的项目中都会遇到要将短消息类型的数据 (比如企业系统内的 "操作日记", 聊天通讯系统内的 "对话记录" 等数据) 做持久化, 而这种数据的特征就是关系简单, 数据量庞大, 大部分只有读写操作等. 基于这些业务需要与应用场景, 那么我们就可以考率使用 MongoDB 来做数据的持久化存储与管理了.
在这里, 我们将模拟一个基于 Saas 平台下的企业系统内 "操作日记" 的业务场景(项目将以 Saas 服务提供给各个企业使用), 并使用 MongoDB 来做数据的持久化存储与管理(包含对数据的增加与查询操作). 这里我们为了简便则将代码的结构层次分为两层(实际项目中各位按需分层级), 那么项目结构将是这个样子的:
1,Lezhima.web: 接受来自客户端的请求, 及服务端响应的出入口. 由一个基于 ASP.NET Core 的 MVC 项目组成.
2,Lezhima.Data: 直接跟 MongoDB 进行通讯交互, 实现对 DB 的增, 查等操作. 由一个基于. NET Core 的类库组成.
业务规则:
基于上述的应用场景所悉, 我们将面向的是企业客户, 且是以 Saas 服务运行在云上的, 则我们操作管理 MongoDB 时将面临如下几个规则(基于海量数据读写考率):
1, 希望数据能按企业 ID 分库, 即各个企业的数据归档到其对应的独立库中.
2, 能根据日期每天分表存储数据.
3, 分库能按企业 ID 自动完成, 分表能按日期自动完成.
与 MongoDB 通讯的 API 类库:
我们将使用由 MongoDB 官方提供的 API 类库来与 MongoDB 进行通讯与交互, 在 Lezhima.Data 项目中可通过 NuGet 管理器引用如下两个类库:
- ,MongoDB.Bson
- ,MongoDB.Driver
实现代码
通过上面的介绍, 我们清楚了两个分层之间的功能与关系, 那么接下来我们就分别来看看它们具体的代码, 及操作 MongoDB 的简便酷爽吧.
1, 我们先看看 Lezhima.Data 层的代码, 首先定义一个名为 "MongoDbContext" 类, 用于管理 MongoDB 的上下文, 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Security.Authentication;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using MongoDB.Bson;
- using MongoDB.Driver;
- namespace Lezhima.Data.Context
- {
- /// <summary>
- /// MongoDB 对象的上下文
- /// </summary>
- public class MongoDbContext
- {
- /// <summary>
- /// 缓存指定库中表对象(集合)
- /// </summary>
- public static Dictionary<string, object> _collectionsMongoDb;
- /// <summary>
- /// Mongo 上下文
- /// </summary>
- public IMongoDatabase DbContext { get; }
- /// <summary>
- /// 初始化 MongoDB 数据上下文
- /// 将数据库名传递进来
- /// </summary>
- public MongoDbContext(string dbName)
- {
- // 连接字符串, 如:"MongoDB://username:password@host:port/[DatabaseName]?ssl=true"
- // 建议放在配置文件中
- var connectionString = "MongoDB://root:a123@192.168.1.6:27017";
- try
- {
- var mongoClient = new MongoClient(connectionString);
- // 数据库如果不存在, 会自动创建
- DbContext = mongoClient.GetDatabase(dbName);
- }
- catch (Exception e)
- {
- Log.WriteLogByError("构建 MongoDbContext 出错", e);
- throw;
- }
- }
- /// <summary>
- /// 异步获取表(集合)
- /// </summary>
- /// <typeparam name="TEntity"></typeparam>
- /// <param name="datetime"></param>
- /// <returns></returns>
- public async Task<IMongoCollection<TEntity>> GetCollectionAsync<TEntity>(string tableName="") where TEntity : class
- {
- // 集合缓存如果为空, 那么创建一个
- if (_collectionsMongoDb == null)
- {
- _collectionsMongoDb = new Dictionary<string, object>();
- }
- var dt = DateTime.Now.ToString("yyyy -MM-dd");
- if (!string.IsNullOrEmpty(tableName))
- {
- dt = tableName;
- }
- // 获取集合名称, 使用的标准是在实体类型名后添加日期
- var collectionName = dt;
- // 如果集合不存在, 那么创建集合
- if (false == await IsCollectionExistsAsync<TEntity>(collectionName))
- {
- await DbContext.CreateCollectionAsync(collectionName);
- }
- // 如果缓存中没有该集合, 那么加入缓存
- if (!_collectionsMongoDb.ContainsKey(collectionName))
- {
- _collectionsMongoDb[collectionName] = DbContext.GetCollection<TEntity>(collectionName);
- }
- // 从缓存中取出集合返回
- return (IMongoCollection<TEntity>)_collectionsMongoDb[collectionName];
- }
- /// <summary>
- /// 集合是否存在
- /// </summary>
- /// <typeparam name="TEntity"></typeparam>
- /// <returns></returns>
- public async Task<bool> IsCollectionExistsAsync<TEntity>(string name)
- {
- var filter = new BsonDocument("name", name);
- // 通过集合名称过滤
- var collections = await DbContext.ListCollectionsAsync(new ListCollectionsOptions { Filter = filter });
- // 检查是否存在
- return await collections.AnyAsync();
- }
- }
- }
2, 在 Lezhima.Data 层增加一个名为 "IMongoRepository" 接口, 用于封装向业务层提供操作 MongoDB 的操作方法, 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Text;
- using System.Threading.Tasks;
- using MongoDB.Bson;
- using MongoDB.Driver;
- namespace Lezhima.Data.Interface
- {
- public interface IMongoRepository<T> where T : class
- {
- /// <summary>
- /// 从指定的库与表中获取指定条件的数据
- /// </summary>
- /// <returns></returns>
- Task<List<T>> GetListAsync(Expression<Func<T, bool>> predicate, string dbName, string tableName = "");
- /// <summary>
- /// 对指定的库与表中新增数据
- /// </summary>
- /// <returns></returns>
- Task<bool> Add(List<T> list, string dbName, string tableName = "");
- }
- }
3, 在 Lezhima.Data 层再增加一个名为 "MongoRepository" 类, 实现 "IMongoRepository" 接口, 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Threading.Tasks;
- using System.Web;
- using Lezhima.Data.Context;
- using Lezhima.Data.Interface;
- using MongoDB.Bson;
- using MongoDB.Driver;
- namespace Lezhima.Data
- {
- /// <summary>
- /// 封装向业务层提供操作 MongoDB 的操作方法
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class MongoRepository<T> : IMongoRepository<T> where T : class
- {
- /// <summary>
- /// 从指定的库与表中获取指定条件的数据
- /// </summary>
- /// <returns></returns>
- public async Task<List<T>> GetListAsync(Expression<Func<T, bool>> predicate, string dbName, string tableName)
- {
- var dbContext = new MongoDbContext(dbName);
- var collection = await dbContext.GetCollectionAsync<T>(tableName);
- return collection.AsQueryable<T>().Where(predicate).ToList();
- }
- /// <summary>
- /// 对指定的库与表中新增数据
- /// </summary>
- /// <returns></returns>
- public async Task<bool> Add(List<T> list, string dbName, string tableName = "")
- {
- var dbContext = new MongoDbContext(dbName);
- var collection = await dbContext.GetCollectionAsync<T>(tableName);
- await collection.InsertManyAsync(list);
- return true;
- }
- }
- }
4, 在 Lezhima.Web 层再增加一个名为 "TestController" 的控制器, 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Lezhima.Core;
- using Lezhima.Data.Interface;
- using Microsoft.AspNetCore.Mvc;
- namespace Lezhima.Web.Controllers
- {
- [Route("API/[controller]")]
- public class TestController : Controller
- {
- private readonly IMongoRepository<ActionLog> _IMongoRepository;
- public TestController(IMongoRepository<ActionLog> __IMongoRepository)
- {
- _IMongoRepository = __IMongoRepository;
- }
- /// <summary>
- /// 测试新增数据方法
- /// </summary>
- /// <returns></returns>
- [HttpGet]
- public async Task<string> Add()
- {
- // 创建两个不同企业 ID 的实体数据
- var model1 = new ActionLog();
- model1.CompanyId = Guid.Parse("B29BC831-A974-4114-90E2-0001E03FBCAF");
- model1.ActionLogId = Guid.NewGuid();
- model1.Context = "测试企业 1";
- model1.CreateTime = DateTime.Now;
- model1.UpdateTime = DateTime.Now;
- var model2 = new ActionLog();
- model2.CompanyId = Guid.Parse("651bbe49-a4c8-4514-babb-897dad7065e3");
- model2.ActionLogId = Guid.NewGuid();
- model2.Context = "测试企业 2";
- model2.CreateTime = DateTime.Now;
- model2.UpdateTime = DateTime.Now;
- var list = new List<ActionLog>();
- list.Add(model1);
- list.Add(model2);
- var group_list = list.GroupBy(p => p.CompanyId);
- var tableName = "ActionLog_" + DateTime.Now.ToString("yyyy-MM-dd");
- foreach (var group in group_list)
- {
- var dbName = "ActionLog_" + group.FirstOrDefault().CompanyId.ToString();
- await _IMongoRepository.Add(group.ToList(), dbName, tableName);
- }
- return "value1";
- }
- /// <summary>
- /// 测试查询方法
- /// </summary>
- /// <param name="companyId"></param>
- /// <returns></returns>
- [HttpGet("{companyId}")]
- public async Task<List<ActionLog>> Get(Guid companyId)
- {
- var dbName = "ActionLog_" + companyId.ToString();
- var tableName = "ActionLog_" + DateTime.Now.ToString("yyyy-MM-dd");
- var list = await _IMongoRepository.GetListAsync(p => p.Context.IndexOf("测试企业")> -1, dbName, tableName);
- return list;
- }
- }
- }
总结
1,MongoDB 是开源的文档型非关系型数据库, 支持 JAVA/C#/Python 等多种开发语言.
2, 通过由 MongoDB 官方提供的两个 API 类库实现跟 MongoDB 通讯交互.
3,MongoDB 不需要提前创建数据库与表结构, 其会通过传递进去的数据结构自动判断是否需要维护库与表的结构, 这个机制对我们项目的大部分场景都很实用.
4, 最后再通过对 Lezhima.Data 层简单的封装后, 使得面向业务层的调用非常简便.
声明
来源: https://www.cnblogs.com/Andre/p/9712245.html