如何从40亿整数中找到不存在的一个
前言
给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数。(在文件中至少确实一个这样的数-为什么?)。在具有足够内存的情况下,如何解决该问题?如果有几个外部的“临时”文件可用,但是仅有几百字节的内存,又该如何解决该问题?
分析
这仍然是《编程珠玑》中的一个问题。前面我们曾经提到过《位图法》,我们使用位图法解决了这个问题。32位整型最多有4294967296个整数,而很显然40亿个数中必然会至少缺一个。我们同样也可以尝试使用位图法解决该问题,使用536 870 912个字节,约512M内存存储这40亿整数,存在该整数的位置1,最后遍历比特位,输出第一个比特位为0的位置即可。那如果仅借助几个“临时”文件,使用几百字节的内存的情况下该如何处理呢?
能否使用二分搜索呢?这40亿个整数是随机排列的,因此普通的二分搜索不能找到那个不存在的数。但是我们可以基于二分搜索的思想。
一个整数有32位,我们按照每个比特位是0还是1,将要查找的数据范围一分为二。从最高比特位开始:
- 将最高比特位为0的放在一堆,为1的放在另外一堆
- 如果一样多,则随意选择一堆,例如选0,则该位为0
- 如果不一样多,选择少的一堆继续,如1更少,则该位为1
这里需要做一些解释:
- 由于2^32个整数中,每一个比特位是1还是0的个数是相同的。如果在这40亿个整数中,某比特位为1和0的个数是相同的,则说明两边都有不存在的数。因此选择任意一堆即可。
- 如果比特位1的整数比0的整数多,则说明,比特位为0的一堆数中,肯定缺少了一些数。而比特位为1的一堆数中,可能缺少一些数。因此,我们选择少的,也就是比特位为0的那一堆数。
- 每一次选择,都记录选择的是0还是1,最多32次选择后,便可以至少找到一个整数,不存在这40亿数中。
实例说明
由于32位的整型数据量太多,不便说明,我们用一个4比特的数据对上面的思路再做一个说明。4比特最多有16个数。
假设有以下源数据:
3 5 2 6 7 -1 -4 -6 -3 1 -5
对应二进制形式如下(负数在内存中以补码形式存储):
0011 0101 0010 0110 0111 1111 1100 1010 1101 0001 1011
1.处理第1比特位被分为两部分数据,分别为:
- 比特位为0的
0011 0101 0010 0110 0111 0001
- 比特位为1的
1111 1100 1010 1101 1011
可以看到,第一比特位为1的数为5个,比比特位为0的数要少,因此选择比特位为1的数,继续处理。且第一比特位,获得1.
3.处理第2比特位仍然分为两部分数据,分别为:
- 比特位为0的
1010 1011
- 比特位为1的
1111 1100 1101
可以看到,第一比特位为1的数为3个,比比特位为0的数要多,因此选择比特位为0的数,继续处理。且第二比特位,获得0。
2.处理第3比特位仍然被分为两部分数据,分别为:
- 比特位为0的
无
- 比特位为1的
1010 1011
明显看到第三比特位为0的数没有,因此选择比特位0,获得0。至此,已经没有必要继续查找了。
我们最终得到了前三个比特位100,因此不存在于这些数中至少有1000,1001,即-8,-7。
代码实现
C语言实现:
//binarySearch.c#include <stdio.h>#include <stdlib.h>
#define MAX_STR 10#define SOURCE_FILE "source.txt" //最原始文件,需要保留#define SRC_FILE "src.txt" //需要分类的文件#define BIT_1_FILE "bit1.txt"#define BIT_0_FILE "bit0.txt"#define INT_BIT_NUM 32/*FILE *src 源数据文件指针FILE *fpBit1 存储要处理的比特位为1的数据FILE *fpBit0 存储要处理的比特位为0的数据int bit 要处理的比特位返回值0:选择比特位为0的数据继续处理1:选择比特位为1的数据继续处理-1:出错*/int splitByBit(FILE *src,FILE *fpBit1,FILE *fpBit0,int bit,int *nums){ /*入参检查*/ if(NULL == src || NULL == fpBit1 || NULL == fpBit0 || NULL == nums) { printf("input para is NULL"); return -1; } /*bit位检查*/ if(bit < 0 || bit > INT_BIT_NUM ) { printf("the bit is wrong"); return -1; } char string[MAX_STR] = {0}; int mask = 1<< bit; int bit0num = 0; int bit1num = 0; int num = 0; //printf("mask is %x\n",mask); /*循环读取源数据*/ while(fgets(string, MAX_STR, src ) != NULL) { num = atoi(string); //printf("%d&%d %d\n",num,mask, num&mask); /*根据比特位的值,将数据写到不同的位置,注意优先级问题*/ if(0 == (num&mask)) { //printf("bit 0 %d\n",num); fprintf(fpBit0, "%d\n", num); bit0num++; } else { //printf("bit 1 %d\n",num); fprintf(fpBit1, "%d\n", num); bit1num++; } } //printf("bit0num:%d,bit1num:%d\n",bit0num,bit1num); if(bit0num > bit1num) { /*说明比特位为1的数少*/ *nums = bit1num; return 1; } else { *nums = bit0num; return 0; }}/*** *关闭所有文件描述符 * * **/void closeAllFile(FILE **src,FILE **bit0,FILE **bit1){ if(NULL != src && NULL != *src) { fclose(*src); *src = NULL; } if(NULL != bit1 && NULL != *bit1) { fclose(*bit1); *bit1 = NULL; } if(NULL != bit0 && NULL != *bit0) { fclose(*bit0); *bit0 = NULL; } }int findNum(int *findNum){ int loop = 0; /*打开最原始文件*/ FILE *src = fopen(SOURCE_FILE,"r"); if(NULL == src) { printf("failed to open %s",SOURCE_FILE); return -1; } FILE *bit1 = NULL; FILE *bit0 = NULL; int num = 0; int bitNums = 0; //得到比特位的数字数量 int findBit = 0; //当前得到的比特位 for(loop = 0; loop < INT_BIT_NUM;loop++) { /*第一次循环不会打开,保留源文件*/ if(NULL == src) { src = fopen(SRC_FILE,"r"); } if(NULL == src) { return -1; }
/**打开失败时,注意关闭所有打开的文件描述符**/ bit1 = fopen(BIT_1_FILE,"w+"); if(NULL == bit1) { closeAllFile(&src,&bit1,&bit0); printf("failed to open %s",BIT_1_FILE); return -1; } bit0 = fopen(BIT_0_FILE,"w+"); if(NULL == bit0) { closeAllFile(&src,&bit1,&bit0); printf("failed to open %s",BIT_0_FILE); return -1; } findBit = splitByBit(src,bit1,bit0,loop,&bitNums); if(-1 == findBit) { printf("process error\n"); closeAllFile(&src,&bit1,&bit0); return -1; } closeAllFile(&src,&bit1,&bit0); //printf("find bit %d\n",findBit); /*将某比特位数量少的文件重命名为新的src.txt,以便进行下一次处理*/ if(1 == findBit) { rename(BIT_1_FILE,SRC_FILE); num |= (1 << loop); printf("mv bit1 file to src file\n"); } else { printf("mv bit0 file to src file\n"); rename(BIT_0_FILE,SRC_FILE); }
/*如果某个文件数量为0,则没有必要继续寻找下去*/ if(0 == bitNums) { printf("no need to continue\n"); break; } } *findNum = num; return 0;}int main(){ int num = 0; findNum(&num); printf("final num is %d or 0x%x\n",num,num); return 0;}
代码说明:
- 这里的splitByBit函数根据比特位将数据分为两部分
- closeAllFile用于关闭文件描述符
- findNum函数循环32个比特位,每处理一次得到一个比特位,最终可以得到不存在其中的整数。
利用脚本产生了约2000万个整数:
wc -l source.txt 20000001 source.txt
编译运行:
$ gcc -o binarySearch binarySearch.c$ time ./binarySearchfinal num is 18950401 or 0x1212901
real 0m8.001suser 0m6.466ssys 0m0.445s
程序的主要时间花在了读写文件,且占用内存极小。
总结
本文从一个特别的角度用最常见的二分搜索解决了该问题,最多拆分32次,便可从中找到不存在的整数。你有什么更好的思路或优化点,欢迎留言。
webservice
之前都是看别人写博客,自己没有写博客的习惯.在工作的过程中,总是会碰到许多的技术问题.有很多时候想记录下来,后面一直有许多的问题等着解决.总想着等系统完成了,再回头总结下.往往结果就把这事抛到脑后了.
总觉得不能一直这样哈.今天简单记一下吧.有表达不清楚的地方,多多包涵.
最近在研究.net orm框架.想开发一套更好用的Orm框架.别嫌*多.碰到一个Expression合并的问题.
一.需求情况描述
需要更新部分数据的时候,可能前端传回的只有部分字段的数据.而更新的时候,需要设置更新人,更新日期等.
举个栗子来说:
现在有一个预约信息表
前端需要修改数据内容如下,我们暂且叫表达A
var exp = ExpressionHelper.CreateExpression<AppointmentDto>(a => new { a.Id, a.PatientName, a.PatientNamePy, a.IdentityCardNumber, a.Birthday, a.PatientAge, a.PatientSex, a.PatientPhone, a.Address });
而写入数据库的时候需要添加更新人,更新时间.LastUpdateUserId和UpdateTime.
于是我们便又多了一个lambda表达式,我们叫它表达式B
var exp = ExpressionHelper.CreateExpression<AppointmentDto>(a => new { a.Id, a.PatientName, a.PatientNamePy, a.IdentityCardNumber, a.Birthday, a.PatientAge, a.PatientSex, a.PatientPhone, a.Address, a.LastUpdateUserId, a.UpdateTime });
这里说下ExpressionHelper.CreateExpression<T>方法,只是一个为了缩减代码长度而写的方法.输入的lambda表达式原样返回了.
外面不用写好长的类型了.Expression这个类型平时不用.写外面看着眼晕. Expression<Func<AppointmentDto, object>> exp1 = a => new {a.Id,a.PatientName};
/// <summary> /// 转换Expr /// 在外面调用时可以使用var以减少代码长度 /// </summary> /// <param name="expr"></param> /// <returns></returns> public static Expression<Func<T, object>> CreateExpression<T>(Expression<Func<T, object>> expr) { return expr; }
所以此处,使用var可以看起来更整洁.但并不推荐在正常情况下使用var.
个人觉得使用var让代码可维护性降低.读起来真的是头疼.之前在维护一个比较大的系统的时候,公司的主要项目,缺少项目文档,代码里面也基本上没啥注释.而且又清一色的var,每个方法返回的是啥类型?你得去方法那边看去.看着真是恼火,又不得不去一点一点的改.都改成相应的类型后,看着就清爽多了.看一眼,流程就基本上能明白大概.所以,var在C#这种强类型语言里,能不用就别用了.
上面就当是发牢骚了.我们回到正题.
我们看到表达式B比表达式A只多了两个字段.大多数代码都是重复的.而且,两个lambda表达式严重的加长了代码行数.几个这样的表达式下来,这个类就到了几百行了.
对于喜欢简洁,简单的我来说,类一大了我就头疼.那咋整?要是有办法将这两个表达式简化处理一下就好了.将表达式A加上一个短的表达式,来实现表达式B呢.
比如实现 var exprB = exprA.Add(a => new { a.PatientPhone });
So,开始捯饬...
二.解决方法
因为这个合并表达式的方法是在个人系统内部使用满足我定制的Orm的类名称需求
所以定义了一个新的Expression表达式类型NewObjectExpression来处理
1 /// <summary> 2 /// New Object Expression 3 /// 合并NewExpression使用. 4 /// </summary> 5 public class NewObjectExpression : Expression, IArgumentProvider 6 { 7 private IList<Expression> arguments; 8 9 /// <summary> 10 /// 构造方法 11 /// </summary> 12 /// <param name="constructor"></param> 13 /// <param name="arguments"></param> 14 /// <param name="members"></param> 15 internal NewObjectExpression(ConstructorInfo constructor, IList<Expression> arguments, List<MemberInfo> members) 16 { 17 this.Constructor = constructor; 18 this.arguments = arguments; 19 this.Members = members; 20 21 if (members != null) 22 { 23 List<string> nameList = members.Select(member => member.Name).ToList(); 24 for (int i = 0; i < nameList.Count; i++) 25 { 26 if (!string.IsNullOrEmpty(ExpressionString)) 27 { 28 ExpressionString += "," + nameList[i]; 29 } 30 else 31 { 32 ExpressionString = nameList[i]; 33 } 34 } 35 } 36 } 37 38 /// <summary> 39 /// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.) 40 /// </summary> 41 /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns> 42 public override Type Type 43 { 44 get { return Constructor.DeclaringType; } 45 } 46 47 /// <summary> 48 /// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.) 49 /// </summary> 50 /// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns> 51 public sealed override ExpressionType NodeType 52 { 53 get { return ExpressionType.New; } 54 } 55 56 /// <summary> 57 /// Gets the called constructor. 58 /// </summary> 59 public ConstructorInfo Constructor { get; } 60 61 /// <summary> 62 /// Gets the arguments to the constructor. 63 /// </summary> 64 public ReadOnlyCollection<Expression> Arguments 65 { 66 get { return (ReadOnlyCollection<Expression>)arguments; } 67 } 68 69 Expression IArgumentProvider.GetArgument(int index) 70 { 71 return arguments[index]; 72 } 73 74 int IArgumentProvider.ArgumentCount 75 { 76 get 77 { 78 return arguments.Count; 79 } 80 } 81 82 /// <summary> 83 /// ExpressionString 84 /// </summary> 85 public string ExpressionString { get; private set; } = ""; 86 87 public ConstructorInfo Constructor1 => Constructor; 88 89 public List<MemberInfo> Members { get; set; } 90 91 /// <summary> 92 /// 更新members 93 /// </summary> 94 /// <param name="arguments"></param> 95 /// <param name="members"></param> 96 /// <returns></returns> 97 public NewObjectExpression Update(IList<Expression> arguments, List<MemberInfo> members) 98 { 99 if (arguments != null) 100 { 101 this.arguments = arguments; 102 } 103 if (Members != null) 104 { 105 this.Members = members; 106 ExpressionString = ""; 107 List<string> nameList = members.Select(member => member.Name).ToList(); 108 for (int i = 0; i < nameList.Count; i++) 109 { 110 if (!string.IsNullOrEmpty(ExpressionString)) 111 { 112 ExpressionString += "," + nameList[i]; 113 } 114 else 115 { 116 ExpressionString = nameList[i]; 117 } 118 } 119 } 120 return this; 121 } 122 }
待处理的属性都放到了Members里面.后面解析使用的也是Members.其它方法Copy自NewExpression的源码,可以删了不用.
下面我们来扩展Expression<Func<T, object>>,让Expression<Func<T, object>>拥有Add和Remove属性的方法.
直接上代码,看前两个方法.后面两个方法是扩展Expression<Func<T, bool>>表达式的And和Or.等有回头有空再介绍.
1 /// <summary> 2 /// Expression 扩展 3 /// </summary> 4 public static class ExpressionExpand 5 { 6 /// <summary> 7 /// Expression And 8 /// NewExpression 合并 9 /// </summary> 10 /// <param name="expr"></param> 11 /// <returns></returns> 12 public static Expression<Func<T, object>> Add<T>(this Expression<Func<T, object>> expr, Expression<Func<T, object>> expandExpr) 13 { 14 Expression<Func<T, object>> result = null; 15 ParameterExpression parameter = Expression.Parameter(typeof(T), "p"); 16 List<MemberInfo> memberInfoList = new List<MemberInfo>(); 17 #region 处理原expr 18 if (expr.Body is NewExpression) 19 { // t=>new{t.Id,t.Name} 20 NewExpression newExp = expr.Body as NewExpression; 21 if (newExp.Members != null) 22 { 23 memberInfoList = newExp.Members.ToList(); 24 } 25 } 26 else if (expr.Body is NewObjectExpression) 27 { 28 NewObjectExpression newExp = expr.Body as NewObjectExpression; 29 if (newExp.Members != null) 30 { 31 memberInfoList = newExp.Members.ToList(); 32 } 33 } 34 else if (expr.Body is UnaryExpression) 35 { //t=>t.Id 36 UnaryExpression unaryExpression = expr.Body as UnaryExpression; 37 MemberExpression memberExp = unaryExpression.Operand as MemberExpression; 38 memberInfoList.Add(memberExp.Member); 39 } 40 #endregion 41 42 #region 处理扩展expr 43 if (expandExpr.Body is NewExpression) 44 { // t=>new{t.Id,t.Name} 45 NewExpression newExp = expandExpr.Body as NewExpression; 46 for (int i = 0; i < newExp.Members.Count; i++) 47 { 48 MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name); 49 if (!memberInfoList.Any(member => member.Name == newExp.Members[i].Name)) 50 { 51 memberInfoList.Add(newExp.Members[i]); 52 } 53 } 54 } 55 else if (expr.Body is NewObjectExpression) 56 { 57 NewObjectExpression newExp = expr.Body as NewObjectExpression; 58 if (newExp.Members != null && newExp.Members.Count > 0) 59 { 60 for (int i = 0; i < newExp.Members.Count; i++) 61 { 62 MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name); 63 if (!memberInfoList.Any(member => member.Name == newExp.Members[i].Name)) 64 { 65 memberInfoList.Add(newExp.Members[i]); 66 } 67 } 68 } 69 } 70 else if (expandExpr.Body is UnaryExpression) 71 { //t=>t.Id 72 UnaryExpression unaryExpression = expandExpr.Body as UnaryExpression; 73 MemberExpression memberExp = unaryExpression.Operand as MemberExpression; 74 if (!memberInfoList.Any(exp => exp.Name == memberExp.Member.Name)) 75 { 76 memberInfoList.Add(memberExp.Member); 77 } 78 } 79 #endregion 80 NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[0], null, memberInfoList); 81 result = Expression.Lambda<Func<T, object>>(newObjExpression, parameter); 82 return result; 83 } 84 85 /// <summary> 86 /// Expression Remove 87 /// NewExpression 合并 88 /// </summary> 89 /// <param name="expr"></param> 90 /// <returns></returns> 91 public static Expression<Func<T, object>> Remove<T>(this Expression<Func<T, object>> expr, Expression<Func<T, object>> expandExpr) 92 { 93 Expression<Func<T, object>> result = null; 94 ParameterExpression parameter = Expression.Parameter(typeof(T), "p"); 95 List<MemberInfo> memberInfoList = new List<MemberInfo>(); 96 List<MemberInfo> removeMemberInfoList = new List<MemberInfo>(); 97 #region 处理原expr 98 if (expr.Body is NewExpression) 99 { // t=>new{t.Id,t.Name} 100 NewExpression newExp = expr.Body as NewExpression; 101 if (newExp.Members != null) 102 { 103 memberInfoList = newExp.Members.ToList(); 104 } 105 } 106 else if (expr.Body is NewObjectExpression) 107 { 108 NewObjectExpression newExp = expr.Body as NewObjectExpression; 109 if (newExp.Members != null) 110 { 111 memberInfoList = newExp.Members.ToList(); 112 } 113 } 114 else if (expr.Body is UnaryExpression) 115 { //t=>t.Id 116 UnaryExpression unaryExpression = expr.Body as UnaryExpression; 117 MemberExpression memberExp = unaryExpression.Operand as MemberExpression; 118 memberInfoList.Add(memberExp.Member); 119 } 120 #endregion 121 122 #region 处理扩展expr 123 if (expandExpr.Body is NewExpression) 124 { // t=>new{t.Id,t.Name} 125 NewExpression newExp = expandExpr.Body as NewExpression; 126 for (int i = 0; i < newExp.Members.Count; i++) 127 { 128 MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name); 129 if (!removeMemberInfoList.Any(member => member.Name == newExp.Members[i].Name)) 130 { 131 removeMemberInfoList.Add(newExp.Members[i]); 132 } 133 } 134 } 135 else if (expr.Body is NewObjectExpression) 136 { 137 NewObjectExpression newExp = expr.Body as NewObjectExpression; 138 if (newExp.Members != null && newExp.Members.Count > 0) 139 { 140 for (int i = 0; i < newExp.Members.Count; i++) 141 { 142 MemberExpression memberExp = Expression.Property(parameter, newExp.Members[i].Name); 143 if (!removeMemberInfoList.Any(member => member.Name == newExp.Members[i].Name)) 144 { 145 removeMemberInfoList.Add(newExp.Members[i]); 146 } 147 } 148 } 149 } 150 else if (expandExpr.Body is UnaryExpression) 151 { //t=>t.Id 152 UnaryExpression unaryExpression = expandExpr.Body as UnaryExpression; 153 MemberExpression memberExp = unaryExpression.Operand as MemberExpression; 154 if (!memberInfoList.Any(exp => exp.Name == memberExp.Member.Name)) 155 { 156 removeMemberInfoList.Add(memberExp.Member); 157 } 158 } 159 #endregion 160 161 for (int i = memberInfoList.Count - 1; i >= 0; i--) 162 { 163 if (removeMemberInfoList.Any(member => member.Name == memberInfoList[i].Name)) 164 { 165 memberInfoList.Remove(memberInfoList[i]); 166 } 167 } 168 if (memberInfoList.Count <= 0) 169 { 170 throw new System.Exception("Expression Remove Error.All Properties are removed."); 171 } 172 NewObjectExpression newObjExpression = new NewObjectExpression(typeof(object).GetConstructors()[0], null, memberInfoList); 173 result = Expression.Lambda<Func<T, object>>(newObjExpression, parameter); 174 return result; 175 } 176 177 /// <summary> 178 /// Expression And 179 /// </summary> 180 /// <param name="expr"></param> 181 /// <returns></returns> 182 public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> expandExpr) 183 { 184 Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(Expression.And(expandExpr.Body, expr.Body), expr.Parameters); 185 return result; 186 } 187 188 /// <summary> 189 /// Expression And 190 /// </summary> 191 /// <param name="expr"></param> 192 /// <returns></returns> 193 public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> expandExpr) 194 { 195 Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(Expression.Or(expandExpr.Body, expr.Body), expr.Parameters); 196 return result; 197 } 198 }
Add方法可处理 NewExpression 类似 t=>new{t.Id,t.Name} , UnaryExpression 类似t=>t.Id,以及我们自定义的NewObjectExpression类型
所以我们在更新数据的时候就可以这么写了:
Dbc.Db.Update(dto, exp.Add(a => a.LastUpdateUserId)); Dbc.Db.Update(dto, exp.Add(a => new { a.LastUpdateUserId, a.UpdateTime }));
在Orm框架内部,解析NewObjectExpression时,解析方法如下
1 /// <summary> 2 /// 通过Lambed Expression获取属性名称 3 /// </summary> 4 /// <param name="expr">查询表达式</param> 5 /// <returns></returns> 6 public static List<string> GetPiList<T>(Expression<Func<T, object>> expr) 7 { 8 List<string> result = new List<string>(); 9 if (expr.Body is NewExpression) 10 { // t=>new{t.Id,t.Name} 11 NewExpression nexp = expr.Body as NewExpression; 12 if (nexp.Members != null) 13 { 14 result = nexp.Members.Select(member => member.Name).ToList(); 15 } 16 } 17 else if (expr.Body is NewObjectExpression) 18 { // t=>new{t.Id,t.Name} 19 NewObjectExpression nexp = expr.Body as NewObjectExpression; 20 if (nexp.Members != null) 21 { 22 result = nexp.Members.Select(member => member.Name).ToList(); 23 } 24 } 25 else if (expr.Body is UnaryExpression) 26 { //t=>t.Id 27 UnaryExpression uexp = expr.Body as UnaryExpression; 28 MemberExpression mexp = uexp.Operand as MemberExpression; 29 result.Add(mexp.Member.Name); 30 } 31 else 32 { 33 throw new System.Exception("不支持的Select lambda写法"); 34 } 35 return result; 36 }
至此,就完成了Expression<Func<T, object>>Add和Remove属性的扩展,Orm可以让代码更简洁.
三.后记
其实在使用新的类NewObjectExpression来解决之前,尝试过其它的许多方式,因为使用.net的类型可以在其它的框架程序中借鉴引用.不必局限在个人框架内部.
NewExpression内部有一些校验,本身Expression<Func<T, object>>是一个匿名类.试过处理NewExpression,以及新建类继承自NewExpression等方式.都没成功.
要是大家有更好的方法欢迎留言告知.希望本文能对大家有所帮助.
Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库
正文
前言
在 .Net Core 2.2中 Microsoft.AspNetCore.App 默认内置了EntityFramework Core 包,所以在使用过程中,我们无需再从 NuGet 仓库单独应用 EFCore 包;本文并不打算深入的介绍 EFCore 的各种使用方式、原理解析,本文重点在于解决让初学者在10分钟内快速使用上 EFCore 的问题。
1. Code First 方式
EFCore 支持 Code First 方式,这个特性允许开发人员基于业务实体模型创建数据库
1.1 首先创建一个 Asp.Net Core WebApi 项目 Ron.MSSQL,如下
1.2 创建业务实体模型文件夹 Models,添加两个业务实体 Topic,Post
public class Topic
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreateTime { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public int TopicId { get; set; }
public string Content { get; set; }
public DateTime CreateTime { get; set; }
public virtual Topic Topic { get; set; }
}
上面定义的两个实体对象之间通过 Topic.Posts 和 Post.Topic 属性建立了主外键关系,这两个表的主键为 Id,且类型为 int ,这表示在下面的创建数据库过程中,EFCore 会自动的为这两个实体对象建立关系和主键,并会自动设置 Id 字段为主键标识
1.3 编写数据库上下文对象,该对象必须继承自 DbContext
DbContext 内置了很多个构造函数,这里使用配置选项的方式,实现方式也非常简单,最终,在 ForumContext 类中定义上面的实体业务模型集合即可
public class ForumContext : DbContext
{
public ForumContext(DbContextOptions<ForumContext> options) : base(options)
{
}
public DbSet<Topic> Topics { get; set; }
public DbSet<Post> Posts { get; set; }
}
1.4 在 appsettings.json 文件中配置数据库连接字符串,这里使用的是本机安装的 SQLExpress,指定数据库名称为:Forum
"ConnectionStrings": {
"Forum": "server=.\\SQLEXPRESS;uid=sa;pwd=123456;database=Forum;"
}
1.5 在 Startup.cs 中配置连接字符串,注入上下文对象
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ForumContext>(options =>
{
var connectionString = this.Configuration["ConnectionStrings:Forum"];
options.UseSqlServer(connectionString);
});
...
}
上面的代码使用 AddDbContext 方法,并配置了数据库连接字符串为配置文件中的 "ConnectionStrings:Forum" 节点的值
1.5 在项目的包管理器控制台中使用命令根据实体业务模型创建数据库
现在,打开项目中的包管理器控制台
- 在控制台中输入以下两组命令
Add-Migration Forum_v1
Update-Database
在输入命令 Add-Migration Forum_v1 后,回车,控制台输出 To undo this action, use Remove-Migration. 表示命令执行成功;同时可以看到,在项目中多了一个文件夹 Migrations;
注意:此时,数据库 Forum 并没有被创建
在 Migrations 文件夹中;当执行 Update-Database 命令后,EFCore 设计工具将根据 Migrations 中的定义去创建数据库,最终,控制台输出 Done 表示创建完成
查看数据库
从上图中可以看到,数据库创建成功,同时,Forum 数据库中还多了一个表 __EFMigrationsHistory ,该表存储的正是我们项目中的 Migrations 的内容,只有两个字段,对应 20190109031435_Forum_v1.cs 和 当前使用的 EFCore 版本号
如果后续有增加实体,只需要再次执行 Add-Migration 命令即可
如果希望获得帮助,还可在包管理器控制台执行命令 get-help Add-Migration
1.6 在项目中执行 CURD 操作
至此,数据库创建完成,为了在控制器中使用 ForumContext 对象,我们在 HomeController 中使用依赖注入的方式获得 FormContext 对象,以备后续使用
private ForumContext context;
public HomeController(ForumContext context)
{
this.context = context;
}
现在,尝试着在项目中执行一些增删改查的工作,插入一条 Topic 记录,在 HomeController 中编写以下代码
[Route("api/[controller]"), ApiController]
public class HomeController : ControllerBase
{
private ForumContext context;
public HomeController(ForumContext context)
{
this.context = context;
}
[HttpGet]
public ActionResult<IEnumerable<Topic>> Get()
{
var topics = context.Topics.ToList();
return topics;
}
[HttpPost]
public void Post([FromBody] TopicViewModel model)
{
context.Topics.Add(new Topic()
{
Content = model.Content,
CreateTime = DateTime.Now,
Title = model.Title
});
context.SaveChanges();
}
[HttpPut]
public void Put([FromBody] TopicViewModel model)
{
var topic = context.Topics.Where(f => f.Id == model.Id).FirstOrDefault();
topic.Title = model.Title;
topic.Content = model.Content;
context.Topics.Update(topic);
context.SaveChanges();
}
[HttpDelete("{id}")]
public void Delete(int id)
{
var topic = context.Topics.Where(f => f.Id == id).FirstOrDefault();
context.Topics.Remove(topic);
context.SaveChanges();
}
}
上面的代码定义了 Get/Post/Put/Delete 接口,这是一个标准的 Resetful API ,通过依次调用模拟对数据库的 CURD 操作
2. DB First 的使用方式
在很多时候,我们的开发方式是先设计好数据库模型,然后再生成实体对象,这种方式对于从其它语言迁移到 .Net Core 上非常友好,从现有数据库中生成实体对象非常简单,只需要一个命令即可,还是以上面创建好的数据库 Forum 为例子
2.1 基于现有数据库生成实体对象,在项目中的包管理器控制台输入命令,指定使用的是 Microsoft.EntityFrameworkCore.SqlServer 驱动,生成的实体模型输出到目录 DbModels 中
`Scaffold-DbContext "server=.\SQLEXPRESS;uid=sa;pwd=123456;database=Forum" Microsoft.EntityFrameworkCore.SqlServer -OutPutDir DbModels
2.2 执行结果
如果仅需要生成部分数据表,还可以通过将 -Tables 参数添加到上述命令来指定要为哪些表生成实体。 例如 -Tables Blog,Post。多个数据表以逗号分隔
2.3 项目中生成的实体对象文件夹
通过查看生成的代码比较,和 Code First 方式基本相同,使用方式完全一致
3. 导航属性
不管是 Code First 还是 DB First ,在实体对象中,我们都可以看到有个一个导航属性,比如 Topic.Posts 和 Post.Topic ,该导航属性定义了前缀 virtual 表示延迟加载此关联对象,在 Code First 中,导航属性还起到主外键关系定义的作用
结束语
- 本文介绍两种使用 EF Core 的方式
- 通过一个简单的 Forum 示例来一步一步的了解了 EFCore 的使用过程
示例代码下载
https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.MSSQL
WPF实战案例-打印
在前段时间做了一下打印,因为需要支持的格式比较多,所以wpf能打印的有限分享一下几种格式的打印(.xls .xlsx .doc .docx .png .jpg .bmp .pdf)
首先为了保证excel和word的格式问题,excel和word是调用的office进行打印的。
获取所有打印机的方法:
LocalPrintServer print = new LocalPrintServer(); var printers = print.GetPrintQueues(); foreach (var item in printers) { //打印机名称 item.Name; }
Excel打印。三个参数 :1.打印机名称 2要打印的文件路径+名称 3要打印的sheet页名称(可以不写就打印全部sheet)
static Excel.Application Application { get; set; } public static void PrintExcel(string printName, string fileName, List<string> sheetNames = null) { if (Application == null) Application = new Excel.Application(); Application.Visible = false; //Application.Calculation = Excel.XlCalculation.xlCalculationManual; var book = Application.Workbooks.Open(fileName); if (sheetNames == null) { sheetNames = new List<string>(); Excel.Sheets sheets = book.Sheets; for (int i = 1; i <= sheets.Count; i++) { Excel.Worksheet workSheet = sheets.Item[i]; if (workSheet.Visible != Excel.XlSheetVisibility.xlSheetHidden) sheetNames.Add(workSheet.Name); } } foreach (var item in sheetNames) { Excel.Worksheet workSheet = (Excel.Worksheet)book.Worksheets[item]; //------------------------打印页面相关设置-------------------------------- workSheet.PageSetup.PaperSize = Excel.XlPaperSize.xlPaperA4;//纸张大小 //workSheet.PageSetup.Orientation = Excel.XlPageOrientation.xlLandscape;//页面横向 workSheet.PageSetup.Zoom = 75; //打印时页面设置,缩放比例百分之几 workSheet.PageSetup.Zoom = false; //打印时页面设置,必须设置为false,页高,页宽才有效 workSheet.PageSetup.FitToPagesWide = 1; //设置页面缩放的页宽为1页宽 workSheet.PageSetup.FitToPagesTall = false; //设置页面缩放的页高自动 //workSheet.PageSetup.LeftHeader = "Nigel";//页面左上边的标志 //workSheet.PageSetup.CenterFooter = "第 &P 页,共 &N 页";//页面下标 workSheet.PageSetup.FirstPageNumber = (int)Excel.Constants.xlAutomatic; workSheet.PageSetup.Order = Excel.XlOrder.xlDownThenOver; workSheet.PageSetup.PrintGridlines = true; //打印单元格网线 workSheet.PageSetup.TopMargin = 1.5 / 0.035; //上边距为2cm(转换为in) workSheet.PageSetup.BottomMargin = 1.5 / 0.035; //下边距为1.5cm workSheet.PageSetup.LeftMargin = 2 / 0.035; //左边距为2cm workSheet.PageSetup.RightMargin = 2 / 0.035; //右边距为2cm workSheet.PageSetup.CenterHorizontally = true; //文字水平居中 //------------------------打印页面设置结束-------------------------------- workSheet.PrintOutEx(Missing.Value, Missing.Value, Missing.Value, Missing.Value, printName); } //book.PrintOutEx(Missing.Value, Missing.Value, Missing.Value, Missing.Value, printName); //直接打印 book.Close(false); //关闭工作空间 }
excel有时候关不干净,贡献一个强制关闭的方法,可以在excel操作完成后调用:
[DllImport("User32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int Processid); public static void ExcelClose() { if (Application != null) { Application.Quit(); int iId = 0; IntPtr intptr = new IntPtr(Application.Hwnd); System.Diagnostics.Process p = null; try { GetWindowThreadProcessId(intptr, out iId); p = System.Diagnostics.Process.GetProcessById(iId); if (p != null) { p.Kill(); p.Dispose(); } Application = null; } catch (Exception e) { throw e; } } System.GC.Collect(); }
Word打印(打印机名称,要打印的文件路径+名称)
public static void PrintWord(string printName, string fileName) { Word.Application appword = new Word.Application(); appword.Visible = false; appword.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone; Word.Document doc = appword.Documents.Open(fileName); appword.ActivePrinter = printName; doc.PrintOut(true); doc.Close(false); appword.Quit(); }
图片打印WPF(支持格式 png jpg bmp)
public static string PrintImage(string printName, string fileName) { System.Windows.Controls.PrintDialog dialog = new System.Windows.Controls.PrintDialog();//开始打印 var printers = new LocalPrintServer().GetPrintQueues(); var selectedPrinter = printers.FirstOrDefault(d => d.Name == printName); if (selectedPrinter == null) { return "没有找到打印机"; } dialog.PrintQueue = selectedPrinter; dialog.PrintDocument(new TestDocumentPaginator(new BitmapImage(new Uri(fileName))), "Image"); return ""; }
public class TestDocumentPaginator : DocumentPaginator { #region 字段 private Size _pageSize; private ImageSource image; #endregion #region 构造 public TestDocumentPaginator(BitmapImage img) { image = img; //我们使用A3纸张大小 var pageMediaSize = LocalPrintServer.GetDefaultPrintQueue() .GetPrintCapabilities() .PageMediaSizeCapability .FirstOrDefault(x => x.PageMediaSizeName == PageMediaSizeName.ISOA4); if (pageMediaSize != null) { _pageSize = new Size((double)pageMediaSize.Width, (double)pageMediaSize.Height); } } #endregion #region 重写 /// <summary> /// /// </summary> /// <param name="pageNumber">打印页是从0开始的</param> /// <returns></returns> public override DocumentPage GetPage(int pageNumber) { var visual = new DrawingVisual(); using (DrawingContext dc = visual.RenderOpen()) { double imgWidth = 0; double imgHeight = 0; if (image.Height > _pageSize.Height) { double h = _pageSize.Height / image.Height; imgWidth = image.Width * h; imgHeight = image.Height * h; } if (image.Width > _pageSize.Width) { double w = _pageSize.Width / image.Width; imgWidth = image.Width * w; imgHeight = image.Height * w; } if (image.Width < _pageSize.Width && image.Height < _pageSize.Height) { double h = _pageSize.Height / image.Height; double w = _pageSize.Width / image.Width; if (h > w) { imgWidth = image.Width * w; imgHeight = image.Height * w; } else { imgWidth = image.Width * h; imgHeight = image.Height * h; } } double left = Math.Abs((_pageSize.Width - imgWidth) <= 0 ? 2 : (_pageSize.Width - imgWidth)) / 2; double top = Math.Abs((_pageSize.Height - imgHeight) <= 0 ? 2 : (_pageSize.Height - imgHeight)) / 2; dc.DrawImage(image, new Rect(left, top, imgWidth, imgHeight)); } return new DocumentPage(visual, _pageSize, new Rect(_pageSize), new Rect(_pageSize)); } public override bool IsPageCountValid { get { return true; } } public override Size PageSize { get { return _pageSize; } set { _pageSize = value; } } public override IDocumentPaginatorSource Source { get { return null; } } public override int PageCount { get { return 1; } } #endregion }
TestDocumentPaginator内部的重写方法是计算将图片全部居中显示。关于图片打印多说一句:在之前我是使用image加载图片,然后通过PrintVisual打印控件的方式打印的,但是这种方式我发现在win7机器上打印出来的是空白纸张,还没明白是为什么,所以就用这种方式了。 PDF打印(这是调用windows api去打印,不需要乱起八糟的第三方):
// Structure and API declarions: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public class DOCINFOA { [MarshalAs(UnmanagedType.LPStr)] public string pDocName; [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile; [MarshalAs(UnmanagedType.LPStr)] public string pDataType; } [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd); [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool ClosePrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di); [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool EndDocPrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool StartPagePrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool EndPagePrinter(IntPtr hPrinter); [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten); // SendBytesToPrinter() // When the function is given a printer name and an unmanaged array // of bytes, the function sends those bytes to the print queue. // Returns true on success, false on failure. public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount) { Int32 dwError = 0, dwWritten = 0; IntPtr hPrinter = new IntPtr(0); DOCINFOA di = new DOCINFOA(); bool bSuccess = false; // Assume failure unless you specifically succeed. di.pDocName = "My C#.NET RAW Document"; di.pDataType = "RAW"; // Open the printer. if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) { // Start a document. if (StartDocPrinter(hPrinter, 1, di)) { // Start a page. if (StartPagePrinter(hPrinter)) { // Write your bytes. bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten); EndPagePrinter(hPrinter); } EndDocPrinter(hPrinter); } ClosePrinter(hPrinter); } // If you did not succeed, GetLastError may give more information // about why not. if (bSuccess == false) { dwError = Marshal.GetLastWin32Error(); } return bSuccess; } public static bool SendFileToPrinter(string szPrinterName, string szFileName) { // Open the file. FileStream fs = new FileStream(szFileName, FileMode.Open); // Create a BinaryReader on the file. BinaryReader br = new BinaryReader(fs); // Dim an array of bytes big enough to hold the file's contents. Byte[] bytes = new Byte[fs.Length]; bool bSuccess = false; // Your unmanaged pointer. IntPtr pUnmanagedBytes = new IntPtr(0); int nLength; nLength = Convert.ToInt32(fs.Length); // Read the contents of the file into the array. bytes = br.ReadBytes(nLength); // Allocate some unmanaged memory for those bytes. pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength); // Copy the managed byte array into the unmanaged array. Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength); // Send the unmanaged bytes to the printer. bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength); // Free the unmanaged memory that you allocated earlier. Marshal.FreeCoTaskMem(pUnmanagedBytes); return bSuccess; } public static bool SendStringToPrinter(string szPrinterName, string szString) { IntPtr pBytes; Int32 dwCount; // How many characters are in the string? dwCount = szString.Length; // Assume that the printer is expecting ANSI text, and then convert // the string to ANSI text. pBytes = Marshal.StringToCoTaskMemAnsi(szString); // Send the converted ANSI string to the printer. SendBytesToPrinter(szPrinterName, pBytes, dwCount); Marshal.FreeCoTaskMem(pBytes); return true; } public static void PrintPDF(string printName, string fileName) { SendFileToPrinter(printName, fileName); }
以上就是wpf常用的打印
RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
QQ讨论群:953553560
正文
1.topic类型的Exchange
我们之前说过Topic类型的Exchange是direct类型的模糊查询模式,可以通过routkey来实现模糊消费message,topic的模糊匹配有两种模式:
1. 使用*来匹配一个单词
2.使用#来匹配0个或多个单词
我们来看代码
消费端
using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace RabbitMQClient { class Program { private static readonly ConnectionFactory rabbitMqFactory = new ConnectionFactory() { HostName = "39.**.**.**", Port = 5672, UserName = "root", Password = "root", VirtualHost = "/" }; static void Main(string[] args) { var exchangeAll = "changeAll"; var queueman = "queueman"; var quemankey = "man.#"; using (IConnection conn = rabbitMqFactory.CreateConnection()) using (IModel channel = conn.CreateModel()) { channel.ExchangeDeclare(exchangeAll, type: "topic", durable: true, autoDelete: false); channel.QueueDeclare(queueman, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(queueman, exchangeAll, quemankey); channel.BasicQos(prefetchSize: 0, prefetchCount: 50, global: false); EventingBasicConsumer consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { Byte[] body = ea.Body; String message = Encoding.UTF8.GetString(body); Console.WriteLine( message); channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); }; channel.BasicConsume(queue: queueman, autoAck: false, consumer: consumer); Console.ReadLine(); } } } }
生产者代码
using RabbitMQ.Client; using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; namespace RabbitMQConsole { class Program { static void Main(string[] args) { ConnectionFactory factory = new ConnectionFactory(); factory.HostName = "39.**.**.**"; factory.Port = 5672; factory.VirtualHost = "/"; factory.UserName = "root"; factory.Password = "root"; var exchangeAll = "changeAll"; //性别.姓氏.头发长度 var keymanA = "man.chen.long"; var keymanB = "man.liu.long"; var keymanC = "woman.liu.long"; var keymanD = "woman.chen.short"; using (var connection = factory.CreateConnection()) { using (var channel = connection.CreateModel()) { channel.ExchangeDeclare(exchangeAll, type: "topic", durable: true, autoDelete: false); var properties = channel.CreateBasicProperties(); properties.Persistent = true; //发布消息 channel.BasicPublish(exchange: exchangeAll, routingKey: keymanA, basicProperties: properties, body: Encoding.UTF8.GetBytes(keymanA)); channel.BasicPublish(exchange: exchangeAll, routingKey: keymanB, basicProperties: properties, body: Encoding.UTF8.GetBytes(keymanB)); channel.BasicPublish(exchange: exchangeAll, routingKey: keymanC, basicProperties: properties, body: Encoding.UTF8.GetBytes(keymanC)); channel.BasicPublish(exchange: exchangeAll, routingKey: keymanD, basicProperties: properties, body: Encoding.UTF8.GetBytes(keymanD)); } } } } }
我们先运行消费端再运行生产段,结果如下
消费端:
2.headers类型的exchange
生成者代码
using RabbitMQ.Client; using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; namespace RabbitMQConsole { class Program { static void Main(string[] args) { ConnectionFactory factory = new ConnectionFactory(); factory.HostName = "39.**.**.**"; factory.Port = 5672; factory.VirtualHost = "/"; factory.UserName = "root"; factory.Password = "root"; var exchangeAll = "changeHeader"; using (var connection = factory.CreateConnection()) { using (var channel = connection.CreateModel()) { channel.ExchangeDeclare(exchangeAll, type: "headers", durable: true, autoDelete: false); var properties = channel.CreateBasicProperties(); properties.Persistent = true; properties.Headers = new Dictionary<string, object> { { "sex","man"} }; //发布消息 channel.BasicPublish(exchange: exchangeAll, routingKey: "", basicProperties: properties, body: Encoding.UTF8.GetBytes("hihihi")); } } } } }
消费端代码
using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace RabbitMQClient { class Program { private static readonly ConnectionFactory rabbitMqFactory = new ConnectionFactory() { HostName = "39.**.**.**", Port = 5672, UserName = "root", Password = "root", VirtualHost = "/" }; static void Main(string[] args) { var exchangeAll = "changeHeader"; var queueman = "queueHeader"; using (IConnection conn = rabbitMqFactory.CreateConnection()) using (IModel channel = conn.CreateModel()) { channel.ExchangeDeclare(exchangeAll, type: "headers", durable: true, autoDelete: false); channel.QueueDeclare(queueman, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(queueman, exchangeAll, "",new Dictionary<string, object> { { "sex","man" } }); channel.BasicQos(prefetchSize: 0, prefetchCount: 50, global: false); EventingBasicConsumer consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { Byte[] body = ea.Body; String message = Encoding.UTF8.GetString(body); Console.WriteLine( message); channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); }; channel.BasicConsume(queue: queueman, autoAck: false, consumer: consumer); Console.ReadLine(); } } } }