基于C#的MongoDB数据库开发应用(2)--MongoDB数据库的C#开发
在上篇博客《基于C#的MongoDB数据库开发应用(1)--MongoDB数据库的基础知识和使用》里面,我总结了MongoDB数据库的一些基础信息,并在最后面部分简单介绍了数据库C#驱动的开发 ,本文继续这个主题,重点介绍MongoDB数据库C#方面的使用和封装处理过程,利用泛型和基类对象针对数据访问层进行的封装处理。
前面介绍到,当前2.2版本的数据库C#驱动的API,支持两种不同的开发接口,一个是基于MongoDatabase的对象接口,一个是IMongoDatabase的对象接口,前者中规中矩,和我们使用Shell里面的命令名称差不多;后者IMongoDatabase的接口是基于异步的,基本上和前者差别很大,而且接口都提供了异步的处理操作。
本文主要介绍基于MongoDatabase的对象接口的封装处理设置。
1、数据访问层的设计
在结合MongoDB数据库的C#驱动的特点,使用泛型和继承关系,把常规的处理接口做了抽象性的封装,以便封装适合更多业务的接口,减少子类代码及统一API的接口名称。
首先我们来看看大概的设计思路,我们把实体类抽象一个实体基类,方便使用。
我们知道,在MongoDB数据库的集合里面,都要求文档有一个_id字段,这个是强制性的,而且这个字段的存储类型为ObjectId类型,这个值考虑了分布式的因素,综合了机器码,进程,时间戳等等方面的内容,它的构造如下所示。
aaarticlea/png;base64,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" alt="" />
ObjectId是一个12字节的 BSON 类型字符串。按照字节顺序,依次代表:
- 4字节:UNIX时间戳
- 3字节:表示运行MongoDB的机器
- 2字节:表示生成此_id的进程
- 3字节:由一个随机数开始的计数器生成的值
实体基类BaseEntity包含了一个属性Id,这个是一个字符串型的对象(也可以使用ObjectId类型,但是为了方便,我们使用字符型,并声明为ObjectId类型即可),由于我们声明了该属性对象为ObjectId类型,那么我们就可以在C#代码里面使用字符串的ID类型了,代码如下所示。
/// <summary>
/// MongoDB实体类的基类
/// </summary>
public class BaseEntity
{
/// <summary>
/// 基类对象的ID,MongoDB要求每个实体类必须有的主键
/// </summary>
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
}
然后利用泛型的方式,把数据访问层的接口提出来,并引入了数据访问层的基类进行实现和重用接口,如下所示。
其中,上面几个类的定义如下所示。
数据访问层基类BaseDAL的类定义如下所示,主要就是针对上面的IBaseDAL<T>接口进行实现。
有了这些基类的实现,我们对于实体类的处理就非常方便、统一了,基本上不需要在复制大量的代码来实现基础的增删改查分页实现了。
例如上面的User集合(表对象)的数据访问类定义如下所示,在对象的定义的时候,指定对应的实体类,并在构造函数里面指定对应的集合名称就可以实例化一个数据访问类了。
/// <summary>
/// 数据表User对应的具体数据访问类
/// </summary>
public class User : BaseDAL<UserInfo>, IBaseDAL<UserInfo>
{
/// <summary>
/// 默认构造函数
/// </summary>
public User()
{
this.entitysName = "users";//对象在数据库的集合名称
} .................
2、基类各种接口的实现
前面小节我们介绍了实体基类,数据访问层基类接口和基类实现,以及具体集合对象的实现类的定义关系,通过泛型和继承关系,我们很好的抽象了各种对象的增删改查、分页等操作,子类继承了BaseDAL基类后,就自然而然的具有了非常强大的接口处理功能了。下面我们来继续详细介绍基于C#驱动的MongoDB数据库是如何进行各种增删改查等封装的。
1)构造MongoDatabase对象
首先我们需要利用连接字符串来构建MongoDatabase对象,因为所有的接口都是基于这个对象进行处理的,代码如下所示。
/// <summary>
/// 根据数据库配置信息创建MongoDatabase对象,如果不指定配置信息,则从默认信息创建
/// </summary>
/// <param name="databaseName">数据库名称,默认空为local</param>
/// <returns></returns>
protected virtual MongoDatabase CreateDatabase()
{
string connectionString = null; if (!string.IsNullOrEmpty(dbConfigName))
{
//从配置文件中获取对应的连接信息
connectionString = ConfigurationManager.ConnectionStrings[dbConfigName].ConnectionString;
}
else
{
connectionString = defaultConnectionString;
} var client = new MongoClient(connectionString);
var database = client.GetServer().GetDatabase(new MongoUrl(connectionString).DatabaseName); return database;
}
2)构建MongoCollection对象
上面构建了MongoDatabase对象后,我们需要基于这个基础上再创建一个对象的MongoCollection对象,这个就是类似我们关系数据库里面的表对象的原型了。
/// <summary>
/// 获取操作对象的MongoCollection集合,强类型对象集合
/// </summary>
/// <returns></returns>
protected virtual MongoCollection<T> GetCollection()
{
MongoDatabase database = CreateDatabase();
return database.GetCollection<T>(this.entitysName);
}
3)查询单个对象
利用MongoCollection对象,我们可以通过API接口获取对应的对象,单个对象的接口为FindOneById(也可以用FindOne接口,如注释部分的代码),我们具体的处理代码如下所示
/// <summary>
/// 查询数据库,检查是否存在指定ID的对象
/// </summary>
/// <param name="key">对象的ID值</param>
/// <returns>存在则返回指定的对象,否则返回Null</returns>
public virtual T FindByID(string id)
{
ArgumentValidation.CheckForEmptyString(id, "传入的对象id为空"); MongoCollection<T> collection = GetCollection();
return collection.FindOneById(new ObjectId(id)); //FindOne(Query.EQ("_id", new ObjectId(id)));
}
如果基于条件的单个记录查询,我们可以使用Expression<Func<T, bool>>和IMongoQuery的参数进行处理,如下代码所示。
/// <summary>
/// 根据条件查询数据库,如果存在返回第一个对象
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>存在则返回指定的第一个对象,否则返回默认值</returns>
public virtual T FindSingle(Expression<Func<T, bool>> match)
{
MongoCollection<T> collection = GetCollection();
return collection.AsQueryable().Where(match).FirstOrDefault();
} /// <summary>
/// 根据条件查询数据库,如果存在返回第一个对象
/// </summary>
/// <param name="query">条件表达式</param>
/// <returns>存在则返回指定的第一个对象,否则返回默认值</returns>
public virtual T FindSingle(IMongoQuery query)
{
MongoCollection<T> collection = GetCollection();
return collection.FindOne(query);
}
4)IQueryable的接口利用
使用过EF的实体框架的话,我们对其中的IQueryable<T>印象很深刻,它可以给我提供很好的LINQ语法获取对应的信息,它可以通过使用Expression<Func<T, bool>>和IMongoQuery的参数来进行条件的查询操作,MongoCollection对象有一个AsQueryable()的API进行转换,如下所示。
/// <summary>
/// 返回可查询的记录源
/// </summary>
/// <returns></returns>
public virtual IQueryable<T> GetQueryable()
{
MongoCollection<T> collection = GetCollection();
IQueryable<T> query = collection.AsQueryable(); return query.OrderBy(this.SortPropertyName, this.IsDescending);
}
如果是通过使用Expression<Func<T, bool>>和IMongoQuery的参数,那么处理的接口代码如下所示。
/// <summary>
/// 根据条件表达式返回可查询的记录源
/// </summary>
/// <param name="match">查询条件</param>
/// <param name="sortPropertyName">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns></returns>
public virtual IQueryable<T> GetQueryable(Expression<Func<T, bool>> match, string sortPropertyName, bool isDescending = true)
{
MongoCollection<T> collection = GetCollection();
IQueryable<T> query = collection.AsQueryable();
if (match != null)
{
query = query.Where(match);
}
return query.OrderBy(sortPropertyName, isDescending);
}
/// <summary>
/// 根据条件表达式返回可查询的记录源
/// </summary>
/// <param name="query">查询条件</param>
/// <param name="sortPropertyName">排序表达式</param>
/// <param name="isDescending">如果为true则为降序,否则为升序</param>
/// <returns></returns>
public virtual IQueryable<T> GetQueryable(IMongoQuery query, string sortPropertyName, bool isDescending = true)
{
MongoCollection<T> collection = GetCollection();
IQueryable<T> queryable = collection.Find(query).AsQueryable(); return queryable.OrderBy(sortPropertyName, isDescending);
}
5)集合的查询处理
通过利用上面的IQueryable<T>对象,以及使用Expression<Func<T, bool>>和IMongoQuery的参数,我们很好的进行集合的查询处理操作的了,具体代码如下所示
/// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>指定对象的集合</returns>
public virtual IList<T> Find(Expression<Func<T, bool>> match)
{
return GetQueryable(match).ToList();
} /// <summary>
/// 根据条件查询数据库,并返回对象集合
/// </summary>
/// <param name="query">条件表达式</param>
/// <returns>指定对象的集合</returns>
public virtual IList<T> Find(IMongoQuery query)
{
MongoCollection<T> collection = GetCollection();
return collection.Find(query).ToList();
}
对于分页,我们是非常需要的,首先在大数据的集合里面,我们不可能一股脑的把所有的数据全部返回,因此根据分页参数返回有限数量的集合处理就是我们应该做的,分页的操作代码和上面很类似,只是利用了Skip和Take的接口,返回我们需要的记录数量就可以了。
/// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="match">条件表达式</param>
/// <param name="info">分页实体</param>
/// <returns>指定对象的集合</returns>
public virtual IList<T> FindWithPager(Expression<Func<T, bool>> match, PagerInfo info)
{
int pageindex = (info.CurrenetPageIndex < ) ? : info.CurrenetPageIndex;
int pageSize = (info.PageSize <= ) ? : info.PageSize; int excludedRows = (pageindex - ) * pageSize; IQueryable<T> query = GetQueryable(match);
info.RecordCount = query.Count(); return query.Skip(excludedRows).Take(pageSize).ToList();
}
或者是下面的代码
/// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="query">条件表达式</param>
/// <param name="info">分页实体</param>
/// <returns>指定对象的集合</returns>
public virtual IList<T> FindWithPager(IMongoQuery query, PagerInfo info)
{
int pageindex = (info.CurrenetPageIndex < ) ? : info.CurrenetPageIndex;
int pageSize = (info.PageSize <= ) ? : info.PageSize; int excludedRows = (pageindex - ) * pageSize; IQueryable<T> queryable = GetQueryable(query);
info.RecordCount = queryable.Count(); return queryable.Skip(excludedRows).Take(pageSize).ToList();
}
6)对象的写入操作
对象的写入可以使用save,它是根据_id的来决定插入还是更新的,如下代码所示。
/// <summary>
/// 保存指定对象到数据库中,根据Id的值,决定是插入还是更新
/// </summary>
/// <param name="t">指定的对象</param>
/// <returns>执行成功指定对象信息</returns>
public virtual T Save(T t)
{
ArgumentValidation.CheckForNullReference(t, "传入的对象t为空"); MongoCollection<T> collection = GetCollection();
var result = collection.Save(t);
return t;
}
插入记录就可以利用insert方法进行处理的,代码如下所示。
/// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="t">指定的对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool Insert(T t)
{
ArgumentValidation.CheckForNullReference(t, "传入的对象t为空"); MongoCollection<T> collection = GetCollection();
var result = collection.Insert(t);
return result != null && result.DocumentsAffected > ;
}
如果是批量插入,可以利用它的insertBatch的方法进行处理,具体代码如下所示。
/// <summary>
/// 插入指定对象集合到数据库中
/// </summary>
/// <param name="list">指定的对象集合</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool InsertBatch(IEnumerable<T> list)
{
ArgumentValidation.CheckForNullReference(list, "传入的对象list为空"); MongoCollection<T> collection = GetCollection();
var result = collection.InsertBatch(list);
return result.Any(s => s != null && s.DocumentsAffected > ); //部分成功也返回true
}
7)对象的更新操作
更新操作分为了两个不同的部分,一个是全部的记录更新,也就是整个JSON的替换操作了,一般我们是在原来的基础上进行更新的,如下代码所示。
/// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="t">指定的对象</param>
/// <param name="id">主键的值</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool Update(T t, string id)
{
ArgumentValidation.CheckForNullReference(t, "传入的对象t为空");
ArgumentValidation.CheckForEmptyString(id, "传入的对象id为空"); bool result = false;
MongoCollection<T> collection = GetCollection();
var existing = FindByID(id);
if (existing != null)
{
var resultTmp = collection.Save(t);
result = resultTmp != null && resultTmp.DocumentsAffected > ;
} return result;
}
还有一种方式是部分更新,也就是更新里面的指定一个或几个字段,不会影响其他字段,也就不会全部替换掉其他内容的操作了。这里利用了一个UpdateBuilder<T>的对象,用来指定那些字段需要更新,以及这些字段的值内容的,具体的更新代码如下所示。
/// <summary>
/// 封装处理更新的操作
/// </summary>
/// <param name="id">主键的值</param>
/// <param name="update">更新对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
public virtual bool Update(string id, UpdateBuilder<T> update)
{
ArgumentValidation.CheckForNullReference(update, "传入的对象update为空");
ArgumentValidation.CheckForEmptyString(id, "传入的对象id为空"); var query = Query.EQ("_id", new ObjectId(id));
MongoCollection<T> collection = GetCollection();
var result = collection.Update(query, update);
return result != null && result.DocumentsAffected > ;
}
部分更新,可以结合使用Inc和Set方法来进行处理,如下是我在子类里面利用到上面的Update部分更新的API进行处理个别字段的更新操作。
/// <summary>
/// 为用户增加岁数
/// </summary>
/// <param name="id">记录ID</param>
/// <param name="addAge">待增加的岁数</param>
/// <returns></returns>
public bool IncreaseAge(string id, int addAge)
{
//增加指定的岁数
var query = Query<UserInfo>.EQ(s => s.Id, id);
var update = Update<UserInfo>.Inc(s => s.Age, addAge); var collection = GetCollection();
var result = collection.Update(query, update);
return result != null && result.DocumentsAffected > ;
} /// <summary>
/// 单独修改用户的名称
/// </summary>
/// <param name="id">记录ID</param>
/// <param name="newName">用户新名称</param>
/// <returns></returns>
public bool UpdateName(string id, string newName)
{
//增加指定的岁数
var query = Query<UserInfo>.EQ(s => s.Id, id);
var update = Update<UserInfo>.Set(s => s.Name, newName); var collection = GetCollection();
var result = collection.Update(query, update);
return result != null && result.DocumentsAffected > ;
}
8)对象的删除操作
对象的删除,一般可以利用条件进行删除,如单个删除可以使用_id属性进行处理,也可以利用批量删除的接口进行删除操作,代码如下所示。
/// <summary>
/// 根据指定对象的ID,从数据库中删除指定对象
/// </summary>
/// <param name="id">对象的ID</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool Delete(string id)
{
ArgumentValidation.CheckForEmptyString(id, "传入的对象id为空"); MongoCollection<T> collection = GetCollection();
//var result = collection.Remove(Query<T>.EQ(s => s.Id, id));
var result = collection.Remove(Query.EQ("_id", new ObjectId(id)));
return result != null && result.DocumentsAffected > ;
}
其中上面注释的var result = collection.Remove(Query<T>.EQ(s => s.Id, id));代码,就是利用了强类型的对象属性和值进行移除,一样可以的。
对于批量删除,可以利用Query的不同进行处理。
/// <summary>
/// 根据指定对象的ID,从数据库中删除指定指定的对象
/// </summary>
/// <param name="idList">对象的ID集合</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool DeleteBatch(List<string> idList)
{
ArgumentValidation.CheckForNullReference(idList, "传入的对象idList为空"); MongoCollection<T> collection = GetCollection();
var query = Query.In("_id", new BsonArray(idList));
var result = collection.Remove(query);
return result != null && result.DocumentsAffected > ;
}
或者基于IMongoQuery的条件进行处理。
/// <summary>
/// 根据指定条件,从数据库中删除指定对象
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool DeleteByQuery(IMongoQuery query)
{
MongoCollection<T> collection = GetCollection();
var result = collection.Remove(query);
return result != null && result.DocumentsAffected > ;
}
9)其他相关接口
一般除了上面的接口,还有一些其他的接口,如获取记录的总数、判断条件的记录是否存在等也是很常见的,他们的代码封装如下所示。
/// <summary>
/// 获取表的所有记录数量
/// </summary>
/// <returns></returns>
public virtual int GetRecordCount()
{
return GetQueryable().Count();
}
/// <summary>
/// 根据查询条件,判断是否存在记录
/// </summary>
/// <param name="match">条件表达式</param>
/// <returns></returns>
public virtual bool IsExistRecord(Expression<Func<T, bool>> match)
{
return GetQueryable(match).Any();//.Count() > 0
} /// <summary>
/// 根据查询条件,判断是否存在记录
/// </summary>
/// <param name="query">条件表达式</param>
/// <returns></returns>
public virtual bool IsExistRecord(IMongoQuery query)
{
return GetQueryable(query).Any();//.Count() > 0
}
非常感谢您的详细阅读,以上基本上就是我对整个MongoDB数据库的各个接口的基类封装处理了,其中已经覆盖到了基础的增删改查、分页等操作接口,以及一些特殊的条件处理接口的扩展,我们利用这些封装好的基类很好的简化了子类的代码,而且可以更方便的在基类的基础上进行特殊功能的扩展处理。
当然,以上介绍的都不是最新的接口,是2.0(或2.2)版本之前的接口实现,虽然在2.2里面也还可以利用上面的MongoDatabase对象接口,但是IMongoDatabase最新的接口已经全面兼容异步的操作,但也是一个很大的跳跃,基本上引入了不同的接口命名和处理方式,利用异步可以支持更好的处理体验,但是也基本上是需要对所有的接口进行了全部的重写了。
下一篇我会专门介绍一下基于最新的异步接口如何实现这些常规增删改查、分页等的基类实现封装处理。
基于C#的MongoDB数据库开发应用(2)--MongoDB数据库的C#开发的更多相关文章
- 基于C#的MongoDB数据库开发应用(1)--MongoDB数据库的基础知识和使用
在花了不少时间研究学习了MongoDB数据库的相关知识,以及利用C#对MongoDB数据库的封装.测试应用后,决定花一些时间来总结一下最近的研究心得,把这个数据库的应用单独作为一个系列来介绍,希望从各 ...
- MongoDB基础之 用户和数据库基于角色的访问控制
mongod 关键字参数:--auth 默认值是不需要验证,即 --noauth,该参数启用用户访问权限控制:当mongod 使用该参数启动时,MongoDB会验证客户端连接的账户和密码,以确定其是否 ...
- MongoDB小型文档型数据库使用
MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中 ...
- 【MongoDB数据库】Java MongoDB CRUD Example
上一页告诉我们MongoDB 命令入门初探,本篇blog将基于上一篇blog所建立的数据库和表完毕一个简单的Java MongoDB CRUD Example.利用Java连接MongoDB数据库,并 ...
- MongoDb 学习笔记(一) --- MongoDb 数据库介绍、安装、使用
1.数据库和文件的主要区别 . 数据库有数据库表.行和列的概念,让我们存储操作数据更方便 . 数据库提供了非常方便的接口,可以让 nodejs.php java .net 很方便的实现增加修改删除功能 ...
- 数据库基础 非关系型数据库 MongoDB 和 redis
数据库基础 非关系型数据库 MongoDB 和 redis 1 NoSQL简介 访问量增加,频繁的读写 直接访问(硬盘)物理级别的数据,会很慢 ,关系型数据库的压力会很大 所以,需要内存级的读写操作, ...
- NoSql非关系型数据库之MongoDB应用(三):MongoDB在项目中的初步应用
业精于勤,荒于嬉:行成于思,毁于随. 我们可以结合相关的IDE做一个简单的增删改查了,实现MongoDB在项目中的初步应用. 前提是安装了MongoDB服务和MongoDB可视化工具,没有安装的可以点 ...
- Linux下安装配置MongoDB 3.0.x 版本数据库
说明: 操作系统:CentOS 5.X 64位 IP地址:192.168.21.128 实现目的: 安装配置MongoDB数据库 具体操作: 一.关闭SElinux.配置防火墙 1.vi /etc/s ...
- MongoDB(一):关系型数据库和非关系型数据库
一.关系型数据库 1.概念 关系型数据库:是指采用了关系模型来组织数据的数据库,是目前各类数据库中使用最为广泛的数据库系统.简单的说,关系模型指的就是二维表格模型,一个关系型数据库就是由二维表及其之间 ...
随机推荐
- Spring-Context之六:基于Setter方法进行依赖注入
上文讲了基于构造器进行依赖注入,这里讲解基于Setter方法进行注入.在Java世界中有个约定(Convention),那就是属性的设置和获取的方法名一般是:set+属性名(参数)及get+属性名() ...
- linux奇技淫巧
用着用着就发现,linux的每个命令都是那么的深奥而富有技巧,实用而淫荡..真可谓奇技淫巧.... 初学的真不易掌握... http://www.cnblogs.com/include/archive ...
- 谨慎DateTime.Now在EF的query中的使用
执行如下代码: var query = from tr in _carrierRepository select new BaseCarrier { CarrierCode = tr.CarrierC ...
- DDD~领域层
回到目录 再论Domain与Infrastructure 在面向领域的设计中,领域层(Domain)实现上是位于最底层的,其它层有对它的引用,包括基础设施层(Infrastructure)也是去引用领 ...
- Masonry -- 使用纯代码进行iOS应用的autolayout自适应布局
简介 简化iOS应用使用纯代码机型自适应布局的工作,使用一种简洁高效的语法替代NSLayoutConstraints. 项目主页: Masonry 最新示例: 点击下载 项目简议: 如果再看到关于纯代 ...
- phpstudy80端口被占用时的解决方案
1.适合人群? 之前笔记本单独安装过Apache.php.mysql环境,但是后期想用集成开发环境phpstudy的,安装完phpstudy后(之前的单独环境依然存在),发现启动时,总是显示80端口被 ...
- leancloud 手机注册用户(调用API) 教程
// 从storybord 连线过来的button方法(注册按钮) - (IBAction)regist:(UIButton *)sender { AFHTTPSessionManager *mana ...
- 咱们来聊聊JS中的异步,以及如何异步,菜鸟版
为什么需要异步?why?来看一段代码. 问题1: for(var i=0;i<100000;i++){ } alert('hello world!!!'); 这段代码的意思是执行100...次后 ...
- Nodejs中的this
以下内容都是关于在nodejs中的this而非javascript中的this,nodejs中的this和在浏览器中javascript中的this是不一样的. 在全局中的this console.l ...
- wangEditor——轻量级web富文本框
提示:最新版wangEditor请参见 http://www.wangeditor.com/ 和 https://github.com/wangfupeng1988/wangEditor 交流 ...