asp.net core类似于spring boot,内置了一个kerstral web服务器,所以可以直接通过dotnet启动,然后访问。
本文着重于.net操作mongodb的过程
框架添加mongoHelper
因为EFcore是不支持mongoHelper,所以只能通过MongoClient来访问数据库
首先框架中添加mongoHelper类,mongoHelper通过泛型来实现增删改查,数据库配置在appSetting.json中,然后把mongoHelper注入容器
public static class MongoServiceCollectionExtension { public static void AddMongoDb(this IServiceCollection services, IConfiguration configuration) { services.Configure<MongoOption>(configuration.GetSection("MongoOption")); services.AddSingleton<MongoClientHelper>(); services.AddSingleton<MongoHelper>(); } }
public class MongoHelper { private IMongoDatabase _dataBase; private MongoClient _client; public MongoHelper(MongoClientHelper clienthelper, IOptions<MongoOption> option) { _client = clienthelper.GetMongoClient(); _dataBase = _client.GetDatabase(option.Value.DefaultDataBase); } public void SwitchDataBase(string dataBaseName) { _dataBase = _client.GetDatabase(dataBaseName); } private IMongoCollection<T> GetCollection<T>() { return _dataBase.GetCollection<T>(typeof(T).Name); } private IMongoCollection<BsonDocument> GetAggregrationCollection<T>() { return _dataBase.GetCollection<BsonDocument>(typeof(T).Name); } }
下面来详细说说遇到的坑
1. 获取不到配置的database
一开始的配置如下,然后发现最后获取的database是默认的admin,并不是配置的mydb
// mongoDb配置 "MongoOption": { "MongoHost": "mongodb://me:123456@127.0.0.1:27017", "DefaultDataBase": "mydb" },
网上查到一句话
最终把db名字加到了后面
// mongoDb配置 "MongoOption": { "MongoHost": "mongodb://me:123456@127.0.0.1:27017/mydb", },
2. 读取表数据报错,不能将string转换成DateTime
由于mongo的特性,不管我插入什么数据都行,只要把对应的model改成你想要的就行。但是读取的时候我们必须指定一个model,那么假如这个model以前插入过数据,然后被修改了又插入过数据,当你读取的时候就会报错。我这边有个字段原来是string类型的,用了一段时间后,被改成了DateTime类型。当我使用mongo的find方法时,他就报错了string不能转换成datetime。
然后我尝试着增加构造函数,或者修改setter方法,但是依然没有用
于是我在mongo里面增加了一个方法,可以看到上图中有2个GetCollection方法,第二个是给聚合函数用的,
/// <summary> /// 根据条件查询数据 /// </summary> public List<T> Get<T>(FilterDefinition<T> filter) { return GetCollection<T>().Find(filter).ToList(); } /// <summary> /// 聚合查询表T /// </summary> /// <typeparam name="T"></typeparam> /// <param name="pipeline"></param> /// <returns></returns> public async Task<List<BsonDocument>> AggregateAsync<T>(PipelineDefinition<BsonDocument, BsonDocument> pipeline) { return (await GetAggregrationCollection<T>().AggregateAsync(pipeline)).ToList(); }
第二个查找出来的是BsonDocument类型的list,需要转换成对应的model list
protected List<TaskCats> ConvertAggregateResultToTaskCats(List<BsonDocument> queryResults) { if (!queryResults.Any()) return null; return queryResults.Select(result => { // 历史遗留问题,reportingTime可能为空,为string,为datetime //var reportingTime = result["reportingTime"]; var reportingTime = result.GetValue("reportingTime"); return new TaskCats( result["adAccount"].ToString(), Convert.ToDateTime(result["reportedDate"]), reportingTime.ToString() == "BsonNull" ? Convert.ToDateTime("1990-11-11") : Convert.ToDateTime(reportingTime), Convert.ToDateTime(result["startDate"]), result["taskId"].ToString(), result.GetValue("description", null)?.ToString(), result.GetValue("weekday", null)?.ToString(), Convert.ToDouble(result["workingHours"]), result.GetValue("BUName", null)?.ToString(), result.GetValue("priority", null)?.ToString(), result.GetValue("projectName", null)?.ToString(), result.GetValue("otherProject", null)?.ToString(), result["_id"].ToString()); }).ToList(); }
框架到此为止(最后那个转换是在业务层实现,因为会涉及到具体的entity)
业务类实现
controller实现交互,service负责具体的操作,联动model和entity以及dao层的mongoHelper。
这里有一个前期数据导入的类,比如说导入初始管理员以及menu列表,这一块是跟业务相关的,我把它放在了业务层。
编写IHost的extension类,通过IServiceScope获取注入的类对象,实现数据库插入
/// <summary> /// Initialize the mongodb data, insert menu info and admin user. /// </summary> /// <param name="host">The host</param> public static void InitialData(this IHost host) { using var serviceScope = host.Services.CreateScope(); var mongoHelper = serviceScope.ServiceProvider.GetRequiredService<MongoHelper>(); AddMenuInfo(mongoHelper); if (!IsDefaultUserExsit(mongoHelper)) { AddDefaultUser(mongoHelper); } }
配置validation
使用FluentValidation:https://www.cnblogs.com/lwqlun/p/10311945.html
返回badRequest跟controller格式一致
这个Extension是属于框架类,这边只是展示用
一些问题
1. 因为spring boot默认会给表增加_class字段用于映射,但是.net使用mongoClient并不需要这个字段,假如不定义就会报错。暂时没有找到很好的解决办法,定义了一个_class属性。
2. 对于Nlog,路径一定要用/,不能用. 因为linux下会找不到路径,导致所有的log都记录在根目录下面
3. MongoDb时区问题
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
很多攻略说添加这个特性,其实这个特性的意思是跟jvm设置一样的,插入的时候,mongo驱动会自动把时间转换成0时区的时间,存入数据库,然后你会发现数据库中的数据跟实际的比对确实是少了8个小时。查询的时候,查出来的数据,mongo驱动会自动再加上8小时,所以你使用的时候就感觉不出有问题。假如你需要数据库里面存储的时间跟实际的一致,那么不管你插入还是查询都必须加8小时(对于mongo的insert和find是这样,但是aggregate方法不需要加)。