.net平台的MongoDB使用

网址:http://www.cnblogs.com/skychen1218/p/6595759.html

前言

  最近花了点时间玩了下MongoDB.Driver,进行封装了工具库,平常也会经常用到MongoDB,因此写一篇文章梳理知识同时把自己的成果分享给大家。

  本篇会设计到Lambda表达式的解析,有兴趣的同学也看看我之前写的《表达式树的解析》。

  文章最后会给出源码下载地址。

MongoDB简介

  MongoDB是一个基于分布式文件存储的非关系型数据库,相比于其他NoSql它支持复杂的查询。

  文本是类似JSON的BSON格式,BSON是在JSON的基础上进化:更快的遍历、操作更简易、更多的数据类型。因此MongoDB可以存储比较复杂的数据类型,同样也支持建立索引。

  MongoDB的概念有:

  • DataBase(库)
  • Collections(集合),类似于关系型数据库的表
  • Document(文档),类似于关系型数据库的一条数据

  

MongoDB优缺点

  • 优点

  1. 高效性,内置GridFS,从而达到海量数据存储,并且满足大数据集的快速范围查询。
  2. 高扩展性,分片使MongoDB的有更高的吞吐量,复制使MongoDB更高的可用性。
  3. BSON文档,易于理解、查看,
  4. 免费
  • 缺点

  1. 不支持事务
  2. 不支持表关联
  3. 不耗CPU却耗内存
  4. 没有成熟的管理工具

MongoDB使用场景

  拥有高效的存储的特点,让MongoDB用在操作日志记录是非常流行的做法。

  随着版本的升级提供更加强大的功能,产品逐渐成熟用在主业务也很多,例如电商行业的订单系统与包裹跟踪模块,海量的主订单与订单明细,包裹的状态变更信息。

  然而因为BSON文档的存储方式,使平常的开发的思维模式有所变更。举个栗子,传统用关系型数据库,订单模块就会分主订单表和订单明细表,创建订单就会用事务同时添加两表的数据,查找订单也会通过两表关联查询出来。但是使用MongoDB,主订单表与其明细,将会以一个完整的对象保存为文档。

  也因为不支持事务、表关联的原因,它更加适合用作于一个完整的业务模块。

  部分朋友会带着一个问题,非关系型数据库和关系型数据库哪个更好。我认为,谁都无法代替谁,一般情况下,非关系型数据库更多的作为关系型数据库扩展,用好了效果甚佳,滥用了只会寸步难行。

  

MongoDB安装

  本来想写的,相应的文章在园子太多了,借用一位仁兄的博文,传送门

  MongoDB下载地址:https://www.mongodb.com/download-center#community

  管理工具:Robomongo,传送门

MongoDB.Driver的使用

  

  创建一个控制台,到Nuget下载MongoDB.Driver。写入以下代码:

 

using System;
using FrameWork.MongoDB.MongoDbConfig;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;

namespace FrameWork.MongoDb.Demo
{
class Program
{
static void Main(string[] args)
{
var database = "testdatabase";
var collection = "TestMongo";
var db = new MongoClient("您的地址").GetDatabase(database);
var coll = db.GetCollection<TestMongo>(collection);

var entity = new TestMongo
{
Name = "SkyChen",
Amount = 100,
CreateDateTime = DateTime.Now
};

coll.InsertOneAsync(entity).ConfigureAwait(false);

}
}

public class TestMongo : MongoEntity
{

[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreateDateTime { get; set; }

public decimal Amount { get; set; }

public string Name { get; set; }

}
}

  第一个demo:添加数据就完成了。F12可以看到IMongoCollection这个接口,增删改查都有,注意分One和Many。基础的使用就不扯过多,在文章尾部的代码已经提供增删改查的封装。

  增删查的封装相对简单,但是MongoDB.Driver提供的update的稍微比较特殊。通过Builders<T>.Update.Set(_fieldname, value)更新指定字段名,有多个字段名需要修改,就要通过new UpdateDefinitionBuilder<T>().Combine(updateDefinitionList)去完成

  然而,这种方式并不适用于我们实际开发,因此需要对Update方法进行 实体更新封装Lambda更新封装

实体更新封装

  通过ID作为过滤条件更新整个实体在实际工作中是常有的。既然通过ID作为条件,那么只能通过UpdateOneAsync进行约束更新一条数据。更新的字段可以通过反射实体对象进行遍历属性。下边是实现代码:

/// <summary>
/// mongodb扩展方法
/// </summary>
internal static class MongoDbExtension
{
/// <summary>
/// 获取更新信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
internal static UpdateDefinition<T> GetUpdateDefinition<T>(this T entity)
{
var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);

var updateDefinitionList = GetUpdateDefinitionList<T>(properties, entity);

var updateDefinitionBuilder = new UpdateDefinitionBuilder<T>().Combine(updateDefinitionList);

return updateDefinitionBuilder;
}

/// <summary>
/// 获取更新信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="propertyInfos"></param>
/// <param name="entity"></param>
/// <returns></returns>
internal static List<UpdateDefinition<T>> GetUpdateDefinitionList<T>(PropertyInfo[] propertyInfos, object entity)
{
var updateDefinitionList = new List<UpdateDefinition<T>>();

propertyInfos = propertyInfos.Where(a => a.Name != "_id").ToArray();

foreach (var propertyInfo in propertyInfos)
{
if (propertyInfo.PropertyType.IsArray || typeof(IList).IsAssignableFrom(propertyInfo.PropertyType))
{
var value = propertyInfo.GetValue(entity) as IList;

var filedName = propertyInfo.Name;

updateDefinitionList.Add(Builders<T>.Update.Set(filedName, value));
}
else
{
var value = propertyInfo.GetValue(entity);
if (propertyInfo.PropertyType == typeof(decimal))
value = value.ToString();

var filedName = propertyInfo.Name;

updateDefinitionList.Add(Builders<T>.Update.Set(filedName, value));
}
}

return updateDefinitionList;
}
}

 

Lambda表达式更新封装

  曾经用过其他ORM都清楚Lambda表达式使用是非常频繁的,MongoDB.Driver已经支持Lambda表达式的过滤条件,但没支持部分字段更新,因此由我们自己来写解析。下边是现实代码:

 

#region Mongo更新字段表达式解析
/// <summary>
/// Mongo更新字段表达式解析
/// </summary>
/// <typeparam name="T"></typeparam>
public class MongoDbExpression<T> : ExpressionVisitor
{
#region 成员变量
/// <summary>
/// 更新列表
/// </summary>
internal List<UpdateDefinition<T>> UpdateDefinitionList = new List<UpdateDefinition<T>>();
private string _fieldname;

#endregion

#region 获取更新列表
/// <summary>
/// 获取更新列表
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public static List<UpdateDefinition<T>> GetUpdateDefinition(Expression<Func<T, T>> expression)
{
var mongoDb = new MongoDbExpression<T>();

mongoDb.Resolve(expression);
return mongoDb.UpdateDefinitionList;
}
#endregion

#region 解析表达式
/// <summary>
/// 解析表达式
/// </summary>
/// <param name="expression"></param>
private void Resolve(Expression<Func<T, T>> expression)
{
Visit(expression);
}
#endregion

#region 访问对象初始化表达式

/// <summary>
/// 访问对象初始化表达式
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected override Expression VisitMemberInit(MemberInitExpression node)
{
var bingdings = node.Bindings;

foreach (var item in bingdings)
{
var memberAssignment = (MemberAssignment)item;
_fieldname = item.Member.Name;

if (memberAssignment.Expression.NodeType == ExpressionType.MemberInit)
{
var lambda = Expression.Lambda<Func<object>>(Expression.Convert(memberAssignment.Expression, typeof(object)));
var value = lambda.Compile().Invoke();
UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
}
else
{
Visit(memberAssignment.Expression);
}
}
return node;
}

#endregion

#region 访问二元表达式

/// <summary>
/// 访问二元表达式
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected override Expression VisitBinary(BinaryExpression node)
{
UpdateDefinition<T> updateDefinition;

var value = ((ConstantExpression)node.Right).Value;
if (node.Type == typeof(int))
{
var realValue = (int)value;
if (node.NodeType == ExpressionType.Decrement)
realValue = -realValue;

updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
}
else if (node.Type == typeof(long))
{
var realValue = (long)value;
if (node.NodeType == ExpressionType.Decrement)
realValue = -realValue;

updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
}
else if (node.Type == typeof(double))
{
var realValue = (double)value;
if (node.NodeType == ExpressionType.Decrement)
realValue = -realValue;

updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
}
else if (node.Type == typeof(decimal))
{
var realValue = (decimal)value;
if (node.NodeType == ExpressionType.Decrement)
realValue = -realValue;

updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
}
else if (node.Type == typeof(float))
{
var realValue = (float)value;
if (node.NodeType == ExpressionType.Decrement)
realValue = -realValue;

updateDefinition = Builders<T>.Update.Inc(_fieldname, realValue);
}
else
{
throw new Exception(_fieldname + "不支持该类型操作");
}

UpdateDefinitionList.Add(updateDefinition);

return node;
}
#endregion

#region 访问数组表达式

/// <summary>
/// 访问数组表达式
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected override Expression VisitNewArray(NewArrayExpression node)
{
var listLambda = Expression.Lambda<Func<IList>>(node);
var list = listLambda.Compile().Invoke();
UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, list));

return node;
}

/// <summary>
/// 访问集合表达式
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected override Expression VisitListInit(ListInitExpression node)
{
var listLambda = Expression.Lambda<Func<IList>>(node);
var list = listLambda.Compile().Invoke();
UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, list));

return node;
}
#endregion

#region 访问常量表达式

/// <summary>
/// 访问常量表达式
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected override Expression VisitConstant(ConstantExpression node)
{
var value = node.Type.IsEnum ? (int)node.Value : node.Value;

UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));

return node;
}
#endregion

#region 访问成员表达式

/// <summary>
/// 访问成员表达式
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected override Expression VisitMember(MemberExpression node)
{
if (node.Type.GetInterfaces().Any(a => a.Name == "IList"))
{
var lambda = Expression.Lambda<Func<IList>>(node);
var value = lambda.Compile().Invoke();

UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
}
else
{
var lambda = Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object)));
var value = lambda.Compile().Invoke();

if (node.Type.IsEnum)
value = (int)value;

UpdateDefinitionList.Add(Builders<T>.Update.Set(_fieldname, value));
}

return node;
}
#endregion
}
#endregion

 

表达式树的解析

  对于Lambda表达式的封装,我侧重讲一下。假如有一段这样的更新代码:  

复制代码
new MongoDbService().Update<User>(a => a._id == "d99ce40d7a0b49768b74735b91f2aa75", a => new User{AddressList = new List<string>{"number1","number2"},Age = 10,BirthDateTime = DateTime.Now,Name = "skychen",NumList = new List<int>{1211,23344},Sex = Sex.Woman,Son = new User{Name = "xiaochenpi",Age = 1}});
复制代码

  那么,我们可以调试监视看看(下图),我们可以得出两个重要信息:

  1.Expression<Func<T, T>>解析出来Body的NodeType是MemberInit

  2.Bindings里有需要修改的字段信息。

  再调试进去看看Bindings的第一项,我们又可以了解了几个重要信息。

  1.Bindings里的元素是MemberAssignment类型。

  2.Member能取到Name属性,也就是字段名

  3.Expression属性,使用 Expression.Lambda,进行Compile().Invoke()就能得到我们需要的值。

  fileName和Value都能取到了,那么更新自然能解决了。

  上图是源码的部分核心代码,奇怪的是,我并没有在VisitMemberInit里进行遍历Bindings后进行Update.Set,而是将item的Expression属性再一次访问。那是因为我需要针对不同的数据类型进行处理。例如:

  常量,我可以定义一个object value进行去接收,如果遇到枚举我需要强转成整型。

  集合与数组,假如草率的使用object类型,object value = Expression.Lambda<Func<object>>(node).Compile().Invoke(),那么更新到MongoDB里就会有bug,奇怪的_t,_v就会出现。以此我需要定义为IList才能解决这个问题。

  此外,工作中还会遇到金额或者数量自增的情况。Amount = a.Amount+9.9M,Count =a.Count-1。 MongoDB.Driver提供了Builders<T>.Update.Inc方法,因此重写二元表达式进行封装。

附加

  经过测试,官方驱动2.4.3和2.4.4版本对类型IList支持有问题,如下图,所以现在封装版本最高支持到2.4.2。

  

结束

  不知道有多少朋友直接拖到文章尾部直接下载源码的。。。。。。

  如果对您有用,麻烦您推荐一下。

  此外还要感谢非非大哥哥,率先做了我的小白鼠给我提出了可贵的BUG,不然我还真不敢放出源码。

  如果有什么问题和建议,可以在下方评论,我会及时回复。

  双手奉上源码:https://github.com/SkyChenSky/Framework.MongoDB.git

转载于:https://www.cnblogs.com/zxtceq/p/7692189.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/399569.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2018程序员最佳ssh免费登陆工具

https://www.jianshu.com/p/b29b894aa60f Linux 终端 Screenshot from 2018-09-15 00-12-41.png PAC Screenshot from 2018-09-15 00-12-00.png 参考资料 讨论qq群144081101 591302926 567351477本文涉及的python测试开发库 谢谢点赞&#xff01;本文相关海量书籍下载 Wind…

学习flex布局(弹性布局)

Flex是Flexible Box的缩写&#xff0c;意为弹性布局。是W3C早期提出的一个新的布局方案。可以便捷的实现页面布局&#xff0c;目前较高版本的主流浏览器都能兼容&#xff0c;兼容情况如下&#xff1a; Flex在移动端开发上已是主流&#xff0c;比如在h5页面&#xff0c;微信小程…

php创建无限级树型菜单以及三级联动菜单

http://www.php.cn/php-weizijiaocheng-373500.html 这篇文章主要介绍了php创建无限级树型菜单 &#xff0c;主要使用的是递归函数&#xff0c;感兴趣的小伙伴们可以参考一下 写递归函数&#xff0c;可考虑缓存&#xff0c;定义一些静态变量来存上一次运行的结果&#xff0c;多…

使用Docker镜像和仓库

为什么80%的码农都做不了架构师&#xff1f;>>> Docker镜像 由文件系统叠加而成最底端第一层是引导文件系统bootfs&#xff0c;类似grub镜像第二层是root文件系统rootfs列出镜像 huangyiHP ~ % sudo docker images REPOSITORY TAG IMAGE …

wordpress发布文章时右侧边栏选择作者的功能代码

因为本网络营销博客现在有了两个作者&#xff0c;在后台发布文章时&#xff0c;希望可以选择作者&#xff08;以前仅是一个管理员&#xff09;。通过在网上查找资料&#xff0c;并进行实践成功。特分享如下。 一 在当前使用主题目录下的functions.php中添加以下php代码&#x…

c# webbrowser  获取用户选中文字

c# webbrowser 获取用户选中文字 原文:c# webbrowser 获取用户选中文字最近一直被一个问题困扰&#xff0c;有一个文本框&#xff0c;一个webbrowser控件&#xff0c;一个上下文菜单&#xff0c; 用户用鼠标左键选中文字&#xff0c;右键点击搜索&#xff0c;就把选中的文字赋…

cannot be deleted directly via the port API: has device owner network:floatingip

cannot be deleted directly via the port API: has device owner network:floatingip posted on 2015-10-13 19:26 秦瑞It行程实录 阅读(...) 评论(...) 编辑 收藏 转载于:https://www.cnblogs.com/ruiy/p/4875605.html

c# params 工作原理。

2019独角兽企业重金招聘Python工程师标准>>> 高级语言之所以成为高级语言&#xff0c;编译器付出了很多。params在给我们带来方便的同时&#xff0c;编译器也是贡献不少。 实例代码&#xff1a; using System; public class Test { public static void Main(){ C…

这7个在线工具,帮助您快速开发WordPress主题

由于WordPress遵从GPL协议&#xff0c;这个协议规定WordPress可以免费用于商业用途。这样做一个网站的成本就比较低。相对应的开发WordPress主题的需求就多起来了。结合笔者多年开发WordPress主题的经验&#xff0c;熟练使用下面一些免费的工具&#xff0c;在开发WordPress主题…

不用第三方插件如何统计自己wordpress的访问量

很多wordpress博主都很在乎自己的访问量&#xff0c;使用第三方统计插件又会拖慢自己博客的响应速度&#xff0c;下面就交给大家如何自己写代码统计自己博客的访问量。 工具/原料 wordpress站点 自己站点的各种权限 方法/步骤 登录自己博客的后台&#xff0c;输入正确的用户…

WordPress导航菜单函数register_nav_menus() 和 wp_nav_menu()

导航菜单是每一个WordPress主题必须的元素&#xff0c;如果你要制作一个WordPress主题&#xff0c;那就必须熟悉WordPress导航菜单注册函数 register_nav_menus() 和 导航菜单调用函数wp_nav_menu() &#xff0c;这两个参数一般都是配合使用的。今天我们就一起来解释一下这两个…

基因重组

1s / 32M 【问题描述】目前,科学家们正致力于对生物基因的重组进行深入研究。基因的物质载体是脱氧核糖核酸(DNA)。DNA 是一种仅由 A、T、G、C 四种基元构成的双螺旋结构的有机分子。DNA 的两条单链上,同一位置的两个基元是互相对应的。A 对 T,G 对 C,因此,我们只需用任意一条链…

day6笔记

一、上节回顾 list&#xff1a;li [1,2,3,5,a]增加&#xff1a;append&#xff1a;末尾加入追加 insert&#xff1a;插入&#xff0c;在任意位置&#xff0c;insert&#xff08;index,内容&#xff09; extend:迭代着加入&#xff0c;asc ----> ‘a’,‘s’,‘c’ [1,2,3] …

Android手游《》斗地主完整的源代码(支持单机和网络对战)

Android手游《斗地主》完整的源代码&#xff08;支持单机和网络对战&#xff09;下载。一个很不错的源代码。斗地主掌游是一个独特的国内社会斗地主棋牌游戏&#xff0c;之后玩家可以下载网上斗地主和全世界。掌游斗地主特点&#xff1a;1、只有一个主要的社会斗地主棋牌游戏。…

page对象

page对象指的是页面本身 查看当前page对象的字符串描述 转载于:https://www.cnblogs.com/liuliuyiming/p/7731704.html

Memcached总结三:Memcached常用命令及使用说明

一、存储命令 存储命令的格式&#xff1a; 12<command name> <key> <flags> <exptime> <bytes><data block>参数说明如下&#xff1a; <command name>set/add/replace<key>查找关键字<flags>客户机使用它存储关于键值对…

解决SQL命令行回退的问题

场景 在linux或者aix上安装后Oracle后&#xff0c;在SQL命令行下无法通过键盘的退格键回退&#xff0c;如下 解决方法 安装软件 # rpm -ivh rlwrap-0.41-1.el6.x86_64.rpm warning: rlwrap-0.41-1.el6.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 0608b895: NOKEYPrep…

ASP.NET AJAX Timer Trouble? Location is key.

If you’ve made much use of the ASP.NET AJAX Timer control, you may have noticed that it can behave somewhat unexpectedly. In this post, I’m going to take a closer look at how the Timer works and the most significant factor that influences it: Location.…

在屏幕上打印杨辉三角

这就是杨辉三角&#xff0c;也叫贾宪三角。这于我们现在的学习联系最紧密的是2项式乘方展开式的系数规律。如图&#xff0c;在贾宪三角中&#xff0c;第3行的第三个数恰好对应着两数和的平方公式依次下去。 杨辉三角是一个由数字排列成的三角形数表&#xff0c;一般形式如下&am…

对SPA(单页面应用)的总结

目录 1、单页面应用&#xff08;SPA&#xff09;的概念&#xff1a;2、作用&#xff08;好处&#xff09;3、缺点4、实现SPA1、单页面应用&#xff08;SPA&#xff09;的概念&#xff1a; 1、single-page application是一种特殊的Web应用。它将所有的活动局限于一个Web页面中&a…