依据C#的MongoDB数据库开发应用(2)–MongoDB数据库的C#开发

在上篇博客《基于C#的MongoDB数据库开发使用(1)–MongoDB数据库的基础知识和选取》里面,小编总括了MongoDB数据库的有的基础信息,并在终极面部分简单介绍了数码库C#使得的开发
,本文继续这些核心,重点介绍MongoDB数据库C#地点的利用和包装处理进度,利用泛型和基类对象针对数据访问层开始展览的卷入处理。

前方介绍到,当前2.2版本的数码库C#使得的API,支持两种不一样的支出接口,二个是依照MongoDatabase的靶子接口,一个是IMongoDatabase的靶子接口,前者中规中矩,和我们选择Shell里面包车型大巴指令名称大约;后者IMongoDatabase的接口是依据异步的,基本上和前者差异十分大,而且接口都提供了异步的拍卖操作。

正文首要介绍基于MongoDatabase的对象接口的包装处理装置。

一 、数据访问层的设计

在重组MongoDB数据库的C#使得的天性,使用泛型和后续关系,把日常的拍卖接口做了抽象性的包裹,以便封装适合越来越多事情的接口,收缩子类代码及统一API的接口名称。

率先我们来看望大致的筹划思路,我们把实体类抽象一个实体基类,方便使用。

 图片 1

大家清楚,在MongoDB数据库的聚众里面,都务求文档有二个_id字段,这一个是强制性的,而且以此字段的囤积类型为ObjectId类型,这几个值考虑了分布式的因素,综合了机器码,进程,时间戳等等地点的剧情,它的结构如下所示。

图片 2

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

  • 4字节:UNIX时间戳
  • 3字节:表示运营MongoDB的机械
  • 2字节:表示生成此_id的进程
  • 3字节:由二个随便数初步的计数器生成的值

图片 3

实业基类BaseEntity蕴含了1个性情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";//对象在数据库的集合名称
        }

        .................

② 、基类各样接口的达成

前边小节我们介绍了实体基类,数据访问层基类接口和基类达成,以及现实集合对象的贯彻类的概念关系,通过泛型和后续关系,我们很好的抽象了各个对象的增加和删除改查、分页等操作,子类继承了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对象后,我们要求依照这些基础上再创制3个目的的MongoCollection对象,这一个正是近乎大家关周全据Curry面的表对象的原型了。

        /// <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对象有1个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;
        }

再有一种艺术是部分更新,也便是创新里面包车型客车内定三个或几个字段,不会影响别的字段,也就不会整整轮换掉其余剧情的操作了。那里运用了3个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最新的接口已经完美合作异步的操作,但也是二个十分大的踊跃,基本上引入了差别的接口命名和处理格局,利用异步能够支撑更好的处理体验,不过也基本上是亟需对具有的接口进行了上上下下的重写了。

下一篇作者会专门介绍一下基于最新的异步接口怎么样落到实处这几个健康增删改查、分页等的基类完结封装处理。

 

相关文章