MongoDB在实际项目中的使用
MongoDB简介
MongoDB是近些年来流行起来的NoSql的代表,和传统数据库最大的区别是支持文档型数据库。
当然,现在的一些数据库通过自定义复合类型,可变长数组等手段也可以模拟文档型数据库。
例如在PostgreSQL中,以下是一个复合类型的例子
CREATE TYPE complex AS (
r double precision,
i double precision
);
CREATE TYPE inventory_item AS (
name text,
supplier_id integer,
price numeric
);
数组的定义如下
array[1,2] --一维数组
array[[1,2],[3,5]] --二维数组
当然,MongoDB生来就是文档型数据库,自然在应用层面对数据操作非常友好。
- 使用了一套聚合框架来进行专业的聚合操作,和SQL语言相比,可以支持更加细致的操作
- 可以使用JavaScript编写MapReduce函数进行数据统计操作,在分布式框架下适合处理大数据
当然,你也可以将MongoDB当做普通的关系型数据库那样使用。但是这样就无法定义View(如果要使用View这样的功能,还是老老实实将MongoDB当做文档型数据库来使用吧)
在 http://codesnippet.info/ 建站过程中,有些基础数据是简单的关系型数据,有些是缓存用文档型数据。
MongoDB的管理工具
这里我就推荐自己开发的工具MongoCola了。
MongoCola项目Github地址
这个项目从2011年到现在已经断断续续维持了5年了,从MongoDB1.0到MongoDB3.2,这个工具和MongoDB一起成长起来的。将近200个Star,最近又有两个两个朋友贡献了代码(现在使用C#开发Winform的人真的不多了),让我感到很欣慰。
期间进行了一次比较大的重构(由于自己对于软件设计的理解的提高,以及从盲目的追求快速添加功能到追求整个项目代码的合理,才下决心进行了一次伤筋动骨的重构,当然现在再看,这次重构很重要,但是代码仍然还是有问题的,绝非完美。)
在开发 www.codesnippet.info 的过程中,整个MONGODB数据库的查看都使用了这个工具,同时在使用中发现了一些BUG,也进行了一些改善。当然我现在也不敢保证BUG全部都清除干净了。如果发现BUG,请和我联系。
原本打算使用Mono进行跨平台的,但是Mono对于Winform的支持并不好,所以虽然这个工具可以在Mac的OSX上运行,但是最好还是老老实实在Windows下运行比较好。发一张工具的界面图片,在OSX上截WIndows远程桌面的图。
C#驱动程序的再包装
虽然官方的C#已经和不错了,虽然MongoDB原生支持ORM的。文档型对象就是OOP对象了。
但是资深码农都会自己再写一些操作数据库的Helper方法。为自己定制的一套从EntityBase到ORM的方法。
(关于时区的设定,其实可以在系列化设定中完成)
using System;
using MongoDB.Bson.Serialization.Attributes;
namespace InfraStructure.DataBase
{
/// <summary>
/// 实体
/// 没有物理删除,所以自增SN是安全的
/// </summary>
public abstract class EntityBase
{
/// <summary>
/// 创建时间
/// </summary>
[BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreateDateTime;
/// <summary>
/// 创建者
/// [这里可以是用户名,亦可是账号]
/// </summary>
public string CreateUser;
/// <summary>
/// 删除标记
/// </summary>
public bool IsDel;
/// <summary>
/// 序列号
/// </summary>
[BsonId] public string Sn;
/// <summary>
/// 更新时间
/// </summary>
[BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime UpdateDateTime;
/// <summary>
/// 更新者
/// </summary>
public string UpdateUser;
/// <summary>
/// 获得表名称
/// </summary>
/// <returns></returns>
public abstract string GetCollectionName();
/// <summary>
/// 获得主键前缀
/// </summary>
/// <returns></returns>
public abstract string GetPrefix();
/// <summary>
/// 序列号格式
/// </summary>
public const string SnFormat = "D8";
}
}
ORM的增删改也无非就是将驱动的数据库操作重新定制了一下而已。
具体代码可以在本网站的开源项目中找到
数据库操作辅助类
MongoDB的序列化设定 (干货)
- 场景一:由于类定义变更,某个字段不需要了,但是如果不进行设定的话,发序列化会出错,某个数据库字段没有配置的实体字段(IgnoreExtraElementsConvention)
- 场景二:在序列化的时候,为了节省空间,希望字段为空的时候,不进行序列化。(IgnoreIfNullConvention)
-
场景三:日期型的数据,序列化的时候,希望可以指定时区(RegisterSerializer)
//http://mongodb.github.io/mongo-csharp-driver/1.10/serialization/ var pack = new ConventionPack(); pack.Add(new IgnoreExtraElementsConvention(true)); pack.Add(new IgnoreIfNullConvention(true)); ConventionRegistry.Register("CustomElementsConvention", pack, t => { return true; }); //DateTime Localize BsonSerializer.RegisterSerializer(typeof(DateTime), new DateTimeSerializer(DateTimeKind.Local)); return true;
当然,你也可以对某个日期型字段单独指定时区,或者将某个字段定义为主键。详细请参考上文提到的 [BsonId] 和 [BsonDateTimeOptions(Kind = DateTimeKind.Local)] 特性。
FileStorage
MongoDB虽然可以使用FileSystem,但是由于是内存型数据库,可能会大量消耗内存资源。
这里贴一下FileSystemHelper,但是不建议大型项目在没有足够资源的情况下使用!!
using System;
using System.Collections.Generic;
using System.Web;
using MongoDB.Driver.GridFS;
using System.IO;
using MongoDB.Driver;
using System.Drawing.Imaging;
using System.Drawing;
namespace InfraStructure.Storage
{
public static class MongoStorage
{
/// <summary>
/// 服务器
/// </summary>
private static MongoServer _innerServer;
/// <summary>
/// 链接字符串
/// </summary>
private static readonly string Connectionstring = @"mongodb://localhost:";
/// <summary>
/// 初始化MongoDB
/// </summary>
/// <param name="dbList">除去Logger以外</param>
/// <param name="defaultDbName"></param>
/// <param name="port"></param>
/// <returns></returns>
public static bool Init(string port = "28030")
{
try
{
_innerServer = new MongoClient(Connectionstring + port).GetServer();
_innerServer.Connect();
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 保存文件
/// </summary>
/// <param name="file"></param>
/// <param name="ownerId"></param>
/// <param name="databaseType"></param>
/// <returns></returns>
public static string InsertFile(HttpPostedFileBase file, string ownerId, string databaseType)
{
var mongofilename = ownerId + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + file.FileName;
var innerFileServer = _innerServer.GetDatabase(databaseType);
var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
gfs.Upload(file.InputStream, mongofilename);
return mongofilename;
}
/// <summary>
/// 保存文件
/// </summary>
/// <param name="file"></param>
/// <param name="ownerId"></param>
/// <param name="databaseType"></param>
/// <returns></returns>
public static string InsertFile(HttpPostedFile file, string ownerId, string databaseType)
{
return InsertFile(new HttpPostedFileWrapper(file) as HttpPostedFileBase, ownerId, databaseType);
}
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <param name="fileName"></param>
/// <param name="ownerId"></param>
/// <param name="databaseType"></param>
/// <returns></returns>
public static string InsertFile(Stream file, string fileName, string ownerId, string databaseType)
{
var mongofilename = ownerId + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + fileName;
var innerFileServer = _innerServer.GetDatabase(databaseType);
var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
gfs.Upload(file, mongofilename);
return mongofilename;
}
/// <summary>
/// 保存流为固定文件名
/// </summary>
/// <param name="file"></param>
/// <param name="fixedFilename"></param>
/// <param name="databaseType"></param>
public static void InsertStreamWithFixFileName(Stream file, string fixedFilename, string databaseType)
{
var innerFileServer = _innerServer.GetDatabase(databaseType);
var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
gfs.Upload(file, fixedFilename);
}
/// <summary>
/// 获得文件
/// </summary>
/// <param name="stream"></param>
/// <param name="filename"></param>