Asp.mvc(一)~使用MongoDB来存储数据 #
MongoDB是一种文档型非关系型数据库(NoSql),拥有无模式的灵活的数据结构。 MongoDB 内置 Sharding, 可将数据碎片化, 以提高数据的查询效率。 Sharding的概念大家自行google。
创建数据库以及集合
使用终端进入 Mongo/bin 目录下
mongo 127.0.0.1:27017
use MonogoSample
db.createCollection('User')
项目结构
创建以下结构的工程项目:
Core:
Data:
Services:
Web:
Core: 创建领域模型 ###
在Core项目中,我们需要引用 MongoDB.Bson以及 MongoDB.Driver ,因为我们需要使用到 ObjectId结构,它存在于MongoDB.Bson空间下,还需要使用到 IMongoCollection ,这个泛型集合接口存在于MongoDB.Driver空间下。 获取 MongoDB.Driver 可以去官网下载 dll 或者源文件, 也可以直接使用 Nuget 包控制台键入以下命令来获取驱动:
Install-Package MonogoDB.Driver
下面创建一个MongoEntity抽象类, 声明一个公共属性 Id, 由于存在于MongoDB 的每条数据都有一个字段即: _id, 此属性类型为 ObjectId, 所以我们可以在这里设置一个类型为ObjectId的属性 Id
using MongoDB.Bson;
namespace Core
{
public abstract class MongoEntity
{
public ObjectId Id { get; set; }
}
}
创建名为 User 的POCO类来与数据库中的 User集合对应(此处并不要求此POCO类类名与集合名称相同,因为可以通过 BsonClassMap来完成POCO模型与集合的映射), 理所当然,它继承自 MongoEntity。
using System;
namespace Core.Domain
{
public partial class User : MongoEntity
{
public string LoginName { get; set; }
public string LoginPwd { get; set; }
public string NickName { get; set; }
public string PhoneNo { get; set; }
public Gender Gender { get; set; }
public DateTime Birthday { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public DateTime RegisterTime { get; set; }
}
public enum Gender
{
Male, Female
}
}
创建 IRepository 泛型接口, 定义基本 CURD操作。
using MongoDB.Driver;
using System.Threading.Tasks;
namespace Core.Data
{
public interface IRepository<T> where T : MongoEntity
{
void Create(T entity);
void Delete(string id);
void Update(T entity);
Task<T> FindOne(string id);
IMongoCollection<T> Collection { get; }
}
}
Data: 配置领域对象 ###
创建 UserMap 完成对 Core.Domain.User 与数据库中 User 集合的映射。
using Core.Domain;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace Data.Mapping
{
public partial class UserMap : BsonClassMap
{
public UserMap()
: base(typeof(User))
{
this.Reset();
this.AutoMap();
//映射的 Collection 名称
//默认为类名 即 typeof(Tclass).Name
this.SetDiscriminator("User");
this.MapProperty("Gender").SetSerializer(new EnumSerializer<Gender>(BsonType.String));
//这里不仅仅可以映射枚举,还可以映射字段名,
//主要应用在数据库中已经存在数据的情况下,或者想改名
//例如:字段 RegisterTime 映射为数据库中的 AddTime
this.MapProperty("RegisterTime").SetElementName("AddTime");
}
}
}
下面问题来了,我们在此处是设置映射,但是我们并没有去执行这段 Code, 在这里我们需要使用到 WebActivatorEx 来执行这段 Code, 可以直接在 Nuget 包控制台中键入:
Install-Package WebActivatorEx
来获取 WebActivatorEx 以完成对POCO类的映射, 创建 MongoMapRegister
using Data;
using MongoDB.Bson.Serialization;
using System;
using System.Linq;
using System.Reflection;
using WebActivatorEx;
[assembly: PreApplicationStartMethod(typeof(MongoMapRegister), "Register")]
namespace Data
{
public partial class MongoMapRegister
{
/// <summary>
/// 注册所有对 Mongo 模型的映射
/// </summary>
public static void Register()
{
var registerClasses = Assembly.GetExecutingAssembly().GetTypes()
.Where(x => x.BaseType != null && x.IsSubclassOf(typeof(BsonClassMap)));
foreach (var type in registerClasses)
{
dynamic classInstance = Activator.CreateInstance(type);
BsonClassMap.RegisterClassMap(classInstance);
}
}
}
}
创建 MongoDbHandler 泛型来获取与POCO类相对应的集合。 connectionString 的形式为: mongodb://[username:password@]host1[:port1], 若 mongodb 没有设置了用户名与口令,那么可以直接省略 [username:password@],通过 MongoClient(string connectionString)来获取 MongoClient 实例, 通过其 GetDataBase(string dbName)来获取相关数据库的连接。
在这里由于我们需要得到与 T 相对应的集合名称来获取相对应集合,所以我们需要通过 BsonClassMap.GetRegisteredClassMaps()来获取所有映射的 POCO类型, 根据其 ClassType 来获取 T 的映射类(继承自 BsonClassMap ),然后通过 Discriminator 来获取集合名称。 通过 IMongoDataBase.GetCollection(string collectionName)来获取相对应集合实例。
using Core;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using System;
using System.Configuration;
using System.Linq;
namespace Data
{
public class MongoDbHandler<T> where T : MongoEntity
{
private string connectionString = ConfigurationManager.ConnectionStrings["mongo"].ConnectionString;
private string databaseName = ConfigurationManager.AppSettings["mongodb"];
public IMongoCollection<T> Collection { get; private set; }
public MongoDbHandler()
{
var mongoClient = new MongoClient(connectionString);
var mongoDb = mongoClient.GetDatabase(databaseName);
//获取当前模型的映射类
//获取当前类的 Discriminator 即集合名称
var registerClass = BsonClassMap.GetRegisteredClassMaps()
.SingleOrDefault(x => x.ClassType == typeof(T));
if (registerClass != null)
this.Collection = mongoDb.GetCollection<T>(registerClass.Discriminator);
else
throw new ArgumentNullException();
}
}
}
创建 MongoRepository 实现 IRepository 接口, 实现 CURD 操作,这里使用 IRepository 的目的是因为我们也许会使用到其他的数据持久层框架来完成对数据的操作, 在Core 中我们只定义了 POCO 类与 IRepository , 这样可以实现控制反转,以解耦和。 下面这段代码较为简单。
using Core;
using Core.Data;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data
{
public partial class MongoRepository<T> : IRepository<T> where T : MongoEntity
{
private MongoDbHandler<T> MongoDbHandler;
public MongoRepository()
{
this.MongoDbHandler = new MongoDbHandler<T>();
}
public virtual void Create(T entity)
{
this.MongoDbHandler.Collection.InsertOneAsync(entity);
}
public virtual void Delete(string id)
{
this.MongoDbHandler.Collection.DeleteOneAsync(x => x.Id == new ObjectId(id));
}
public virtual void Update(T entity)
{
}
public async virtual Task<T> FindOne(string id)
{
var result = await this.MongoDbHandler.Collection.Find(x => x.Id == new ObjectId(id)).ToListAsync();
return result.FirstOrDefault();
}
public IMongoCollection<T> Collection
{
get
{
return this.MongoDbHandler.Collection;
}
}
}
}
Services: 实现业务逻辑 ###
创建 IUserService 接口, 定义关于 Core.Domain.User 的相关业务操作。
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
using M = Core.Domain;
namespace Services.User
{
public interface IUserService
{
Task<List<M.User>> FindManyAsync(Expression<Func<M.User, bool>> exp, int limit, int skip);
Task<M.User> FindOneAsync(string id);
void CreateUser(M.User model);
}
}
创建 UserService 实现 IUserService 接口,这里并没有用到依赖注入框架。 一般我常用的是 Autofac, 可以使用 Nuget 获取。 由于只是个 Demo, 所以这里是直接将依赖暴露在外, 后续在做改善。可以很清晰的看到,这里主要是完成对 IUserService 业务的实现。
using Core.Data;
using Data;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
using M = Core.Domain;
namespace Services.User
{
public partial class UserService : IUserService
{
private IRepository<M.User> _userRepository;
public UserService()
{
this._userRepository = new MongoRepository<M.User>();
}
public async Task<List<M.User>> FindManyAsync(Expression<Func<M.User, bool>> exp, int limit, int skip)
{
var result = await this._userRepository.Collection.Find(exp)
.Limit(limit).Skip(skip).ToListAsync();
//result = await this._userRepository.Collection.Find(x => true).ToListAsync();
return result;
}
public async Task<M.User> FindOneAsync(string id)
{
var result = await this._userRepository.FindOne(id);
return result;
}
public void CreateUser(M.User model)
{
this._userRepository.Create(model);
}
}
}