因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的接口名称。

首先我们来探大概的统筹思路,我们将实体类华而不实一个实体基类,方便使用。

 图片 1

咱理解,在MongoDB数据库的集里面,都求文档有一个_id字段,这个是强制性的,而且这字段的储存类型为ObjectId类型,这个价考虑了分布式的元素,综合了机器码,进程,时间戳等等地方的始末,它的布局如下所示。

图片 2

ObjectId是一个12字节的  BSON 类型字符串。按照字节顺序,依次代表:

  • 4字节:UNIX时间戳
  • 3字节:表示运行MongoDB的机器
  • 2字节:表示生成此_id的进程
  • 3字节:由一个擅自数起之计数器生成的价值

图片 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; }
    }

下一场以泛型的艺术,把数量访问层的接口提出来,并引入了多少访问层的基类进行落实与选定接口,如下所示。

图片 4

其中,上面几乎只类似的概念如下所示。

图片 5

多少访问层基类BaseDAL的类定义如下所示,主要就是是针对性地方的IBaseDAL<T>接口进行落实。

图片 6

产生了这些基类的实现,我们对实体类的处理便杀有益、统一了,基本上不欲以复制大量底代码来兑现基础的增删改查分页实现了。

譬如说地方的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 < 1) ? 1 : info.CurrenetPageIndex;
            int pageSize = (info.PageSize <= 0) ? 20 : info.PageSize;

            int excludedRows = (pageindex - 1) * 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 < 1) ? 1 : info.CurrenetPageIndex;
            int pageSize = (info.PageSize <= 0) ? 20 : info.PageSize;

            int excludedRows = (pageindex - 1) * 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 > 0;
        }

倘是批量插,可以使用她的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 > 0); //部分成功也返回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 > 0;
            }

            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 > 0;
        }

部分更新,可以组合使用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 > 0;
        }

        /// <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 > 0;
        }

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 > 0;
        }

里面地方注释的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 > 0;
        }

还是依据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 > 0;
        } 

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最新的接口就完善配合异步的操作,但也是一个充分怪的腾,基本上引入了不同之接口命名和处理方式,利用异步可以支持再次好之处理体验,但是也多是内需针对富有的接口进行了上上下下的还写了。

下一篇我会专门介绍一下根据最新的异步接口如何贯彻这些健康增删改查、分页等的基类实现封装处理。

 

相关文章