.net core操作MongoDB
前言
现实中认识的一个搞java(百万富婆)的大佬,已经转行做抖音主播了,搞技术的只能赶在年前再水一篇博客,不足之处欢迎拍砖,以免误人子弟,呔,真是太难受了
环境准备
- .net core 3.1
- MongoDB
- Navicat Premium 16
创建项目
1.选择ASP.NET Core Web API 项目模板

2.配置项目信息 项目名称,项目路径

3.配置项目其它信息,这里选择使用.net core 3.1 不配置https

添加依赖
1.获取MongoDB.Driver包 版本 2.18.0
方案A. install-package MongoDB.Driver
方案B. nuget包管理工具搜索 MongoDB.Driver
方案C. 选中项目,双击,添加
<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.18.0" />
</ItemGroup>
注册中间件
1.添加配置文件
"MongoDB": {
"ConnName": "mongodb://root:123456@127.0.0.1:27017/",
"DatabaseName": "test"
}
2.添加配置类
public class MongoDBConfig
{
/// <summary>
/// 连接字符串
/// </summary>
public string ConnName { get; set; }
/// <summary>
/// 数据库名称
/// </summary>
public string DatabaseName { get; set; }
}
3.注册配置文件与中间件
//读取配置文件
var mongoDBOptions = Configuration.GetSection("MongoDB").Get<MongoDBConfig>();
//注册配置
services.Configure<MongoDBConfig>(this.Configuration.GetSection("MongoDB"));
//注册中间件
services.AddSingleton(new MongoClient(mongoDBOptions.ConnName));
其实就是注册了一个mongodb的客户端实例,然后用这个客户端实例来实现crud,如果你想优雅点(强迫症),也可以自定义一个拓展,来注册这个实例,这是一个通用技巧,后面使用到的类请自行注册
public static class MongoDBExtensions
{
public static void AddMongoDB(this IServiceCollection services, Action<MongoDBConfig> setupAction)
{
if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}
var options = new MongoDBConfig();
setupAction(options);
services.Configure(setupAction);
services.AddSingleton(services.AddSingleton(new MongoClient(options.ConnName)));
}
}
使用拓展,来注册中间件
var mongoDBOptions = Configuration.GetSection("MongoDB").Get<MongoDBConfig>();
services.AddMongoDB(m =>
{
m.ConnName = mongoDBOptions.ConnName;
m.DatabaseName = mongoDBOptions.DatabaseName;
});
封装基础操作
最常用的办法,就是弄个DBHelper类,这种简单粗暴的方式,在.net core里使用并不优雅(网上教程太多了,不想重复的水),这里模拟常规的仓储模式,进行演示
1.创建 Service,Repertoty,Model 文件夹
2.创建 BaseDbContext 类
public class BaseDbContext
{
private readonly IMongoDatabase _database;
public BaseDbContext(MongoClient client, IOptions<MongoDBConfig> options)
{
//决定使用哪个库
_database = client.GetDatabase(options.Value.DatabaseName);
}
/// <summary>
/// 数据库对象
/// </summary>
public IMongoDatabase MongoDatabase { get{ return _database; } }
}
如果项目只操作一个数据库,直接在基类里指定操作的数据库即可,如果需要操作多个数据库,可以再新建一个子类(TestDbContext),在子类里指定要操作的数据库
前面我已经注册了mongodb的客户端跟配置文件,所以在Startup类下,可以直接注册这个类,容器会自动管理这些依赖
services.AddSingleton(typeof(BaseDbContext));
如果你想更优雅点(强迫症),可以创建一个IBaseDbContext 接口,在接口里定义相关的规范
services.AddSingleton<IBaseDbContext>();
3.创建 BaseRepertoty 类
public class BaseRepertoty<T> where T : class, new()
{
private readonly IMongoDatabase _database;
private readonly string _collName;
public BaseRepertoty(BaseDbContext baseDbContext)
{
_database = baseDbContext.MongoDatabase;
_collName = typeof(T).GetAttributeValue((TableAttribute m) => m.Name);
_collName = _collName ?? typeof(T).Name;
}
}
前面我们已经注册了BaseDbContext,它的主要作用是确定操作的库,那BaseRepertoty就是对具体集合(表)的操作,这里使用泛型,避免重复的CRUD操作
我新建了一个拓展,用于获取实体类上真实集合(表)的名称,如果用户没有指定,则取实体类的类名,做为集合(表)的名称
public static class AttributeExtensions
{
public static TValue GetAttributeValue<TAttribute, TValue>(
this Type type,
Func<TAttribute, TValue> valueSelector)
where TAttribute : Attribute
{
var att = type.GetCustomAttributes(
typeof(TAttribute), true
).FirstOrDefault() as TAttribute;
if (att != null)
{
return valueSelector(att);
}
return default(TValue);
}
}
4.创建 UserInfoRepertoty 类
public class UserInfoRepertoty : BaseRepertoty<UserInfo>
{
public UserInfoRepertoty(BaseDbContext baseDbContext) : base(baseDbContext)
{
}
}
它的作用是指定操作具体的集合(表),这个类是非必须的,它只是指定了泛型的具体类型,也可以在server层调用的时候指定
5.创建 UserInfo 实体类
[Table("UserInfo")]
public class UserInfo
{
/// <summary>
/// 主键
/// </summary>
[BsonId]
public ObjectId Id { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreateTime { get; set; }
/// <summary>
/// 合作伙伴
/// </summary>
public List<Partner> PartnerList { get; set; }
/// <summary>
/// 其它信息
/// </summary>
public Info Info { get; set; }
}
public class Partner
{
/// <summary>
/// 伙伴名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 合作状态
/// </summary>
public int? Status { get; set; }
}
public class Info
{
/// <summary>
/// 身份编码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 图片
/// </summary>
public string Pic { get; set; }
}
mongodb里默认的主键是_id,它会自动生成,类型为ObjectId,强烈推荐使用自动生成,速度快,且能提供获取对应的时间戳的方法
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
前4个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
接下来的3个字节是机器标识码
紧接的两个字节由进程 id 组成 PID
最后三个字节是随机数
时间类型要做对应的处理,标记为当前时间
其它常用注解说明
BsonId修饰的字段对应BsonDocument中的_id;
BsonDefaultValue(value)用于指定默认值;
BsonIgnore表示不映射,即使BsonDocument中包含该字段也不会赋值给属性;
BsonExtraElements修饰的字段用于存储没有映射到类中的其他属性;
BsonElement可以指定修饰的属性映射到BsonDocument中的哪个字段;
使用mongodb的一个主要原因,就是它灵活的存储方式,类json,我们就来体验一番,基于这个模型来进行CRUD,我定义的类型如下,基本上能覆盖99%的使用场景
ObjectId (主键)
string (字符串)
int (数字)
object (对象)
array (数组)
我们在BaseRepertoty类里,实现CRUD,完整代码如下
public class BaseRepertoty<T> where T : class, new()
{
private readonly IMongoDatabase _database;
private readonly string _collName;
public BaseRepertoty(BaseDbContext baseDbContext)
{
_database = baseDbContext.MongoDatabase;
_collName = typeof(T).GetAttributeValue((TableAttribute m) => m.Name);
_collName = _collName ?? typeof(T).Name;
}
#region Add 添加一条数据
/// <summary>
/// 添加一条数据
/// </summary>
/// <param name="t">添加的实体</param>
/// <returns></returns>
public bool Add(T t)
{
try
{
var client = _database.GetCollection<T>(_collName);
client.InsertOne(t);
return true;
}
catch (Exception ex)
{
return false;
}
}
#endregion
#region AddAsync 异步添加一条数据
/// <summary>
/// 异步添加一条数据
/// </summary>
/// <param name="t">添加的实体</param>
/// <returns></returns>
public async Task<bool> AddAsync(T t)
{
try
{
var client = _database.GetCollection<T>(_collName);
await client.InsertOneAsync(t);
return true;
}
catch
{
return false;
}
}
#endregion
#region InsertMany 批量插入
/// <summary>
/// 批量插入
/// </summary>
/// <param name="t">实体集合</param>
/// <returns></returns>
public bool InsertMany(List<T> t)
{
try
{
var client = _database.GetCollection<T>(_collName);
client.InsertMany(t);
return true;
}
catch (Exception ex)
{
return false;
}
}
#endregion
#region InsertManyAsync 异步批量插入
/// <summary>
/// 异步批量插入
/// </summary>
/// <param name="t">实体集合</param>
/// <returns></returns>
public async Task<bool> InsertManyAsync(List<T> t)
{
try
{
var client = _database.GetCollection<T>(_collName);
await client.InsertManyAsync(t);
return true;
}
catch
{
return false;
}
}
#endregion
#region UpdateOne 修改数据(单条)
/// <summary>
/// 批量修改数据
/// </summary>
/// <param name="update">要修改的字段</param>
/// <param name="filter">修改条件</param>
/// <returns></returns>
public UpdateResult UpdateOne(UpdateDefinition<T> update, FilterDefinition<T> filter)
{
try
{
var client = _database.GetCollection<T>(_collName);
return client.UpdateOne(filter, update);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
#region UpdateOneAsync 异步批量修改数据
/// <summary>
/// 异步批量修改数据
/// </summary>
/// <param name="update">要修改的字段</param>
/// <param name="filter">修改条件</param>
/// <returns></returns>
public async Task<UpdateResult> UpdateOneAsync(UpdateDefinition<T> update, FilterDefinition<T> filter)
{
try
{
var client = _database.GetCollection<T>(_collName);
return await client.UpdateOneAsync(filter, update);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
#region UpdateManay 批量修改数据
/// <summary>
/// 批量修改数据
/// </summary>
/// <param name="update">要修改的字段</param>
/// <param name="filter">修改条件</param>
/// <returns></returns>
public UpdateResult UpdateManay(UpdateDefinition<T> update, FilterDefinition<T> filter)
{
try
{
var client = _database.GetCollection<T>(_collName);
return client.UpdateMany(filter, update);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
#region UpdateManayAsync 异步批量修改数据
/// <summary>
/// 异步批量修改数据
/// </summary>
/// <param name="update">要修改的字段</param>
/// <param name="filter">修改条件</param>
/// <returns></returns>
public async Task<UpdateResult> UpdateManayAsync(UpdateDefinition<T> update, FilterDefinition<T> filter)
{
try
{
var client = _database.GetCollection<T>(_collName);
return await client.UpdateManyAsync(filter, update);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
#region DeleteOne 删除一条数据
/// <summary>
/// 删除多条数据
/// </summary>
/// <param name="filter">删除的条件</param>
/// <returns></returns>
public DeleteResult DeleteOne(FilterDefinition<T> filter)
{
try
{
var client = _database.GetCollection<T>(_collName);
return client.DeleteOne(filter);
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
#region DeleteOneAsync 异步删除一条数据
/// <summary>
/// 异步删除多条数据
/// </summary>
/// <param name="filter">删除的条件</param>