为什么要在.net 项目中使用NoSQL?其实这是个历史遗留问题. 最近一年用express+mongodb架构做了不少工程, 不亦乐乎, 但当项目上线时, 悲哀的发现这些脚本语言的一大特点就是难以部署. 这个话题可以展开讲: 脚本/弱类型语言不适合做大工程.
所以对其中的一个工程, 就有了重写的念头. 但是数据已经在mongo中, 迁移到关系型数据库想想就头疼. 最终决定还是mongodb吧.
看了很多各种教程, 都是helloworld, 大型项目不可能不分层, 吐槽一下MongoDB的官网文档真是很屎. 没有多少例子.今天下午, 琢磨了一下.net core和mongodb的操作实践, 算是有点心得,记下来吧.
0. 环境
.net core v2.2
mongodb v4.0
visual studio community 2017
mongodb.driver v2.7.2
1. 起项目
新建.net core api 工程 , nuget 搜索
Mongodb
, 下载安装MongoDB.Driver
, 注意其中已经包含了MongoDB.Core, 不需要重复下载:image.png
2. 规划
POCO模型类:
Domain
文件夹, 所有模型类都继承Entity
接口领域类:
Repository
文件夹, 包含数据上下文定义接口IContext
和抽象操作接口IRepository
不愿意弄太多文件夹, 这两个就够了image.png
3. 开码
写一个空的Entity接口,方便模型类继承:
public interface Entity { }
写一个User POCO类测试, 继承Entity:
using MongoDB.Bson;using MongoDB.Bson.Serialization.Attributes;using System;using System.Collections.Generic; [BsonIgnoreExtraElements] /// <summary> /// User POCO /// </summary> public class User:Entity { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } [BsonElement("username")] public string Username { get; set; } [BsonElement("password")] public string Password { get; set; } [BsonElement("realName")] public string RealName { get; set; } }
上面的一些标签有必要说一下:BsonIgnoreExtraElements
是忽略mongodb内部自动产生的一些字段, 打开mongodb就知道:比如:
image.png
类似这样的, 你的POCO类中没有这个字段就会报错.BsonId
告诉说这个字段是主键, 默认是ObjectId
类型.BsonRepresentation(BsonType.ObjectId)
这个标签很有用, 在后面Update, findbyid的时候, 由于前台传过来的是字符串, 没法和数据库中ObjectId
类型的主键进行比较. 加上这个就可以解决这个问题.BsonElement("username")
是在数据库中的字段名.
Repository文件夹下建立
IContext
接口:
using BioVR.Backend.Domain;using MongoDB.Driver; public interface IContext<T> where T:Entity { IMongoCollection<T> Entities { get;} }
这个接口主要是规定所有的Contex实现类中都要有IMongoCollection
类型字段, 俗话说得好,接口即规范.
UserContext实现类
public class UserContext : IContext<User> { // IOC private readonly IMongoDatabase _db; public UserContext(IOptions<Settings> options) { var client = new MongoClient(options.Value.ConnectionString); _db = client.GetDatabase(options.Value.Database); } public IMongoCollection<User> Entities => _db.GetCollection<User>("users"); }
这里, 我们使用.net 的依赖注入(构造函数方式)从Settings里拿到了数据库连接字符串, 并连接到了数据库, 如是, 我们必须注册settings:
打开项目目录下的appsettings.json
, 添加数据库连接地址
{ "MongoDB": { "ConnectionString": "mongodb://localhost:27017", "Database": "yourdbname" }, "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*"}
然后在Startup.cs中加入Configuration
:
public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.Configure<Settings>( options => { options.ConnectionString = Configuration.GetSection("MongoDb:ConnectionString").Value; options.Database = Configuration.GetSection("MongoDb:Database").Value; }); }
这样上面的UserContext
就能拿到数据了
UserRepository类:
public class UserRepository : IRepository<User> { private readonly IContext<User> _context; /// <summary> /// IOC /// </summary> /// <param name="context"></param> public UserRepository(IContext<User> context) { _context = context; } public async Task<string> Create(User t) { await _context.Entities.InsertOneAsync(t); return t.Id; } public async Task<bool> Delete(string id) { var deleteResult = await _context.Entities.DeleteOneAsync(x => x.Id == id); return deleteResult.DeletedCount != 0; } public async Task<List<User>> GetList(int skip = 0, int count = 0) { try { var result = await _context.Entities.Find(x => true) .Skip(skip) .Limit(count) .ToListAsync(); return result; } catch (Exception ex) { throw; } } public async Task<List<User>> GetListByField(string fieldName, string fieldValue) { var filter = Builders<User>.Filter.Eq(fieldName, fieldValue); var result = await _context.Entities.Find(filter).ToListAsync(); return result; } public async Task<User> GetOneById(string id) { return await _context.Entities.Find(x => x.Id.ToString() == id).FirstOrDefaultAsync(); } public async Task<bool> Update(User t) { var filter = Builders<User>.Filter.Eq(x=>x.Id,t.Id); var replaceOneResult = await _context.Entities.ReplaceOneAsync(doc => doc.Id == t.Id,t); return replaceOneResult.ModifiedCount != 0; } }
这里写CURD的各个方法, 注意,除了lambda表达式,我们还可以使用Linq语法做查询.
UserController:
[Route("api/[controller]")] [ApiController] public class UsersController : ControllerBase { private readonly IRepository<User> _userRepository; public UsersController(IRepository<User> userRepository) { _userRepository = userRepository; } [HttpGet] public async Task<List<User>> List() { return await _userRepository.GetList(); } [HttpPost] public async Task<string> Add([FromBody] User user) { var id = await _userRepository.Create(user); return id; } [HttpDelete("{id}")] public async Task<bool> Delete(string id) { return await _userRepository.Delete(id); } }
注意UserController和UserRepository类中都有依赖注入,我们必须注册这些服务, 在Startup文件中加入:
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.Configure<Settings>( options => { options.ConnectionString = Configuration.GetSection("MongoDb:ConnectionString").Value; options.Database = Configuration.GetSection("MongoDb:Database").Value; }); InjectRepositories(services); } private void InjectRepositories(IServiceCollection services) { services.AddTransient<IContext<User>, UserContext>(); services.AddTransient<IRepository<User>, UserRepository>(); }
单独写一个InjectRepositories
方法, 因为可能将来需要注册的服务会很多.
这样就可以了.
本文没什么特别之处, 就是觉得这样的架构比较适合项目开发, 自己记录一下, 也供大家参考,抛砖引玉.
作者:Angeladaddy
链接:https://www.jianshu.com/p/ce6fe18c2b9f