如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

时间:2021-06-07 08:35:03

如何从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

 

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

之前都是看别人写博客,自己没有写博客的习惯.在工作的过程中,总是会碰到许多的技术问题.有很多时候想记录下来,后面一直有许多的问题等着解决.总想着等系统完成了,再回头总结下.往往结果就把这事抛到脑后了.

总觉得不能一直这样哈.今天简单记一下吧.有表达不清楚的地方,多多包涵.

最近在研究.net orm框架.想开发一套更好用的Orm框架.别嫌*多.碰到一个Expression合并的问题.

一.需求情况描述

需要更新部分数据的时候,可能前端传回的只有部分字段的数据.而更新的时候,需要设置更新人,更新日期等.

举个栗子来说:

现在有一个预约信息表

前端需要修改数据内容如下,我们暂且叫表达A

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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
});
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

而写入数据库的时候需要添加更新人,更新时间.LastUpdateUserId和UpdateTime.

于是我们便又多了一个lambda表达式,我们叫它表达式B

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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
});
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

这里说下ExpressionHelper.CreateExpression<T>方法,只是一个为了缩减代码长度而写的方法.输入的lambda表达式原样返回了.

外面不用写好长的类型了.Expression这个类型平时不用.写外面看着眼晕.  Expression<Func<AppointmentDto, object>> exp1 = a => new {a.Id,a.PatientName};

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
/// <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;
}
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

所以此处,使用var可以看起来更整洁.但并不推荐在正常情况下使用var.

个人觉得使用var让代码可维护性降低.读起来真的是头疼.之前在维护一个比较大的系统的时候,公司的主要项目,缺少项目文档,代码里面也基本上没啥注释.而且又清一色的var,每个方法返回的是啥类型?你得去方法那边看去.看着真是恼火,又不得不去一点一点的改.都改成相应的类型后,看着就清爽多了.看一眼,流程就基本上能明白大概.所以,var在C#这种强类型语言里,能不用就别用了.

上面就当是发牢骚了.我们回到正题.

我们看到表达式B比表达式A只多了两个字段.大多数代码都是重复的.而且,两个lambda表达式严重的加长了代码行数.几个这样的表达式下来,这个类就到了几百行了.

对于喜欢简洁,简单的我来说,类一大了我就头疼.那咋整?要是有办法将这两个表达式简化处理一下就好了.将表达式A加上一个短的表达式,来实现表达式B呢.

比如实现 var exprB = exprA.Add(a => new { a.PatientPhone });

So,开始捯饬...

二.解决方法

因为这个合并表达式的方法是在个人系统内部使用满足我定制的Orm的类名称需求

所以定义了一个新的Expression表达式类型NewObjectExpression来处理

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
  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     }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

待处理的属性都放到了Members里面.后面解析使用的也是Members.其它方法Copy自NewExpression的源码,可以删了不用.

下面我们来扩展Expression<Func<T, object>>,让Expression<Func<T, object>>拥有Add和Remove属性的方法.

直接上代码,看前两个方法.后面两个方法是扩展Expression<Func<T, bool>>表达式的And和Or.等有回头有空再介绍.

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
  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     }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

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时,解析方法如下

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
 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         }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

至此,就完成了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,如下

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

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 在项目的包管理器控制台中使用命令根据实体业务模型创建数据库

现在,打开项目中的包管理器控制台

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

  • 在控制台中输入以下两组命令
Add-Migration Forum_v1
Update-Database

在输入命令 Add-Migration Forum_v1 后,回车,控制台输出 To undo this action, use Remove-Migration. 表示命令执行成功;同时可以看到,在项目中多了一个文件夹 Migrations;
注意:此时,数据库 Forum 并没有被创建

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

在 Migrations 文件夹中;当执行 Update-Database 命令后,EFCore 设计工具将根据 Migrations 中的定义去创建数据库,最终,控制台输出 Done 表示创建完成

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

查看数据库

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

从上图中可以看到,数据库创建成功,同时,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 执行结果

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

如果仅需要生成部分数据表,还可以通过将 -Tables 参数添加到上述命令来指定要为哪些表生成实体。 例如 -Tables Blog,Post。多个数据表以逗号分隔

2.3 项目中生成的实体对象文件夹

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

通过查看生成的代码比较,和 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进行打印的。

获取所有打印机的方法:

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
LocalPrintServer print = new LocalPrintServer();

                    var printers = print.GetPrintQueues();
                    foreach (var item in printers)
                    {
                           //打印机名称  item.Name;
                    }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

Excel打印。三个参数 :1.打印机名称  2要打印的文件路径+名称   3要打印的sheet页名称(可以不写就打印全部sheet)

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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); //关闭工作空间

        }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

excel有时候关不干净,贡献一个强制关闭的方法,可以在excel操作完成后调用:

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
[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();
        }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

Word打印(打印机名称,要打印的文件路径+名称)

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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();
        }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

图片打印WPF(支持格式 png jpg bmp)

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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 "";

        }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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
    }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
TestDocumentPaginator内部的重写方法是计算将图片全部居中显示。关于图片打印多说一句:在之前我是使用image加载图片,然后通过PrintVisual打印控件的方式打印的,但是这种方式我发现在win7机器上打印出来的是空白纸张,还没明白是为什么,所以就用这种方式了。

PDF打印(这是调用windows api去打印,不需要乱起八糟的第三方):
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
// 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);
        }
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
以上就是wpf常用的打印

RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

 

QQ讨论群:953553560

正文

1.topic类型的Exchange

我们之前说过Topic类型的Exchange是direct类型的模糊查询模式,可以通过routkey来实现模糊消费message,topic的模糊匹配有两种模式:

1. 使用*来匹配一个单词

2.使用#来匹配0个或多个单词

我们来看代码

消费端

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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();
            }
        }
    }
}
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

生产者代码

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 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 = "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));
                }
            }
        }
    }
}
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

我们先运行消费端再运行生产段,结果如下

消费端:

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

2.headers类型的exchange

生成者代码

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 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"));
                }
            }
        }
    }
}
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

消费端代码

如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange
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();
            }
        }
    }
}
如何从40亿整数中找到不存在的一个  webservice  Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库  WPF实战案例-打印  RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange