分区SQL Server函数 Partition By 与 row_number() 、 排序rank()的用法

在 SQL Server 中,PARTITION BY 是用于在窗口函数(如 ROW_NUMBER(), RANK(), DENSE_RANK(), SUM(), AVG(), 等等)中定义分区的一种方法。分区允许你在查询结果集的特定子集中应用窗口函数,而不是在整个结果集上应用。

使用场景
‌1.排名‌:如上面的例子,可以使用 ROW_NUMBER(), RANK(), 或 DENSE_RANK() 来计算分区内的排名。
2‌.累积和/移动平均‌:可以使用 SUM(), AVG() 等聚合函数来计算分区内的累积和或移动平均。
‌3.分组计算‌:可以在每个分区内执行特定的计算,而不需要使用子查询或 GROUP BY。

举个例子,我们先准备好一个学生成绩表的数据源

create table Student  --学生成绩表
(
 id int,  --主键
 Grade int, --班级
 Score int --分数
);

insert into Student values(1,1,88); 
insert into Student values(2,1,66); 
insert into Student values(3,1,75); 
insert into Student values(4,2,30); 
insert into Student values(5,2,70); 
insert into Student values(6,2,80); 
insert into Student values(7,2,60); 
insert into Student values(8,3,90); 
insert into Student values(9,3,70); 
insert into Student values(10,3,80); 
insert into Student values(11,3,80)

一:如果我们想要一个输出每个学生成绩名次的结果,就可以使用row_number() 方法,配合排序

select *,row_number() over(order by Score desc) as Sequence from Student

二:如果我们想要对每个年级的学生的名词分别计算,那就要使用到partition by方法

select *,row_number() over(partition by Grade order by Score desc) as Sequence from Student

接下来,我们还可以利用上述结果进行二次筛选,比如获取每个年级第一名

select * from (
    select *,row_number() over(partition by Grade order by Score desc) as Sequence from Student
  ) T 
where T.Sequence<=1

在这里,我们发现一个现象,当存在一个分数一样的情况,比如三年级有2个80分,但是他们的排名却分了前后,如果要想排名一致,都是第二名,就可以使用rank()方法

三:按照年级,输出每个学生的排名,同分数排名一致

select *,rank() over(partition by Grade order by Score desc) as Sequence from Student;

此时,我们再获取每个年级前2名,就可以取出分数并列第二名的同学

select * from (
    select *,rank() over(partition by Grade order by Score desc) as Sequence from Student
  ) T 
where T.Sequence<=2

注意事项
PARTITION BY 子句中的列应仔细选择,因为它们会影响查询的性能。
如果不需要分区,可以省略 PARTITION BY 子句,此时窗口函数将在整个结果集上应用。
可以结合多个列进行分区,例如 PARTITION BY Grade。
通过使用 PARTITION BY,你可以更灵活和高效地进行数据分析和处理,尤其是在处理大型数据集时。

利用dotnet命令行,快速创建简单WEB三层架构

我们要保证dotnet的开发环境和运行已经安装在本地,我们可以利用“
dotnet –version”命令进行检查,如果发现提示命令行不存在,说明环境还没安装,需要先安装,安装过程本文不在详细描述,参考微软的官方说明即可,本文重点在于如何用dotnet命令行,快速构建简单三层架构

一:首先,我们创建好项目的文件夹,然后进入文件夹,创建一个web的应用程序,具体用到的命令是“dotnet new”,dotnet new – 根据指定的模板,创建新的项目、配置文件或解决方案。常用的有类库“classlib”、控制台应用程序“console”、WebAPI应用“webapi”、ASP.NET Core Web 应用程序“webapp”

好了,我们创建一个名称叫Server的WebAPI程序、业务逻辑层的类库BLL、数据交互层DAL和工具层Common

dotnet new webapi -n Server
dotnet new classlib -n BLL
dotnet new classlib -n DAL
dotnet new classlib -n Common

此时,我们可以用visual code看下目录结构

二:我们要让这些类库至今添加引用关系,让Server引用BLL层和Common;BLL层引用DAL层和Common;DAL层引用Common层;这里,我们用到的命令是“dotnet add/list/remove reference”

dotnet add Server/Server.csproj reference BLL/BLL.csproj
dotnet add Server/Server.csproj reference Common/Common.csproj
dotnet add BLL/BLL.csproj reference DAL/DAL.csproj
dotnet add BLL/BLL.csproj reference Common/Common.csproj
dotnet add DAL/DAL.csproj reference Common/Common.csproj

三:接下来我们要在DAL层引入微软推荐的EntityFrameWork框架来进行数据库数据的访问,此处例子,我已经在“.Net Core + EntityFramework连接数据库”一文中有描述,此处只做简单说明,我用MySQL为例,我们需要引入

Microsoft.EntityFrameworkCore

Microsoft.EntityFrameworkCore.Design

Microsoft.EntityFrameworkCore.Tools

Pomelo.EntityFrameworkCore.MySql

Microsoft.Extensions.Configuration.Json

我们用dotnet add package – 添加或更新项目文件中的包引用。在引用之前,我们可以用dotnet package search 命令搜索 NuGet 包。

dotnet package search Microsoft.EntityFrameworkCore

然后,我们可以选择对应的版本,我这里使用的是8.0.2,进行添加,当然,也可以忽略版本号,用最新版本,前提是你的.net的框架也是最新的,可以兼容此版本

dotnet add DAL/DAL.csproj package Microsoft.EntityFrameworkCore --version 8.0.2
dotnet add DAL/DAL.csproj package Microsoft.EntityFrameworkCore.Design --version 8.0.2
dotnet add DAL/DAL.csproj package Microsoft.EntityFrameworkCore.Tools --version 8.0.2
dotnet add DAL/DAL.csproj package Pomelo.EntityFrameworkCore.MySql --version 8.0.2
dotnet add DAL/DAL.csproj package Microsoft.Extensions.Configuration.Json

三:下面框架和引用已经搭建完成了,我们需要把项目和数据库连接起来,让我们在DAL项目里面新建一个appsettings.json配置文件,把mysql的数据库连接字符串写上,里面填上你的数据库ip,用户名,密码,以及你想存放的数据库名称

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Port=3306;Database=数据库名称;User=root;Password=你的mysql密码;Charset=utf8mb4;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

在DAL项目里创建Modles文件夹用来存放数据库对象,比如User类,对应数据库User表

namespace DAL.Models;
using System.ComponentModel.DataAnnotations.Schema;

public class User
{
  [Column("Id")]
  public int Id { get; set; }
  
  [Column("UserName")]
  public string? UserName { get; set; }
}

在DAL项目里创建Contexts文件用来存放上下文类,此类建议和数据库名称一一对应

namespace DAL.Contexts;
using Microsoft.EntityFrameworkCore;
using DAL.Models;
using Microsoft.Extensions.Configuration;

public class webapplicationContext : DbContext
{
  public webapplicationContext()
  {
    Configuration = new ConfigurationBuilder().SetBasePath(AppDomain.CurrentDomain.BaseDirectory).AddJsonFile("appsettings.json").Build();
  }
  public IConfiguration Configuration;
  public required DbSet<User> Users { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
    var connectionString = Configuration.GetConnectionString("DefaultConnection");
    optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
  }
}

四:初始化数据库,当我们完成了项目基本的搭建,这时候,我们需要用Entity Framework Core 的命令行工具,创建数据库,首先我们打开终端,定位到项目所在位置,然后,就可以用ef命令来初始化数据库,如图所示

cd DAL
dotnet ef migrations add InitialCreate
dotnet ef database update

完成后,发现我们对应的数据库就会有对应的库和表了

关于EntityFramWork的操作,请参考.Net Core+Entity Framework读写数据

下面,让我们回到Server文件夹下,运行dotnet run命令,找到对应端口,访问网页http://localhost:端口/swagger/index.html,看看是否正常

cd ..
cd Server
dotnet run

最后,我们可以给项目文件根里添加.gitignore文件,忽略bin文件,方便以后版本控制

*.log
temp/
.vs/
BLL/bin
BLL/obj
DAL/bin
DAL/obj
Server/bin
Server/obj
Common/bin
Common/obj

我们也可以在Server项目里新建Controllers文件夹,用来存放控制类

using Microsoft.AspNetCore.Mvc;

namespace Server.Controllers;

[ApiController]
[Route("api/[controller]")]
public class WeatherForcecastController : ControllerBase
{
    private readonly ILogger<WeatherForcecastController> _logger;

    public WeatherForcecastController(ILogger<WeatherForcecastController> logger)
    {
        _logger = logger;
    }

    private static readonly string[] Summaries = new[]
    {
      "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> GetWeatherForecast()
    {
        var forecast =  Enumerable.Range(1, 5).Select(index =>  new WeatherForecast
        (
          DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
          Random.Shared.Next(-20, 55),
          Summaries[Random.Shared.Next(Summaries.Length)]
        )).ToArray();
        return forecast;
    }

    public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
    {
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

最后把Server/Program.cs文件调整一下,加载控制类

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.MapControllers();
app.Run();

IIS 应用程序池和站点的导入导出

1. 在IIS7+上导出所有应用程序池的方法:

%windir%/system32/inetsrv/appcmd list apppool /config /xml > c:/apppools.xml

这个命令会将服务器上全部的应用程序池都导出来,但有些我们是我们不需要的,要将他们删掉.比如:DefaultAppPool、Classic .Net AppPool,如果在导入时发现同名的应用程序池已经存在,那么导入就会失败. 

2.导入应用程序池的方法: 

%windir%/system32/inetsrv/appcmd add apppool /in < c:/apppools.xml

3.导出全部站点的方法:

%windir%/system32/inetsrv/appcmd list site /config /xml > c:/sites.xml

同样,我们需要编辑sites.xml文件删除不需要的站点.如:Default 

4.导入站点的方法:

%windir%/system32/inetsrv/appcmd add site /in < c:/sites.xml

申请免费SSL证书并配置Nginx绑定WordPress

SSL证书就是遵守 SSL协议,由受信任的数字证书颁发机构CA,在验证服务器身份后颁发,具有服务器身份验证和数据传输加密功能。

目前,网上也有一些有效时间较长的免费的证书,以SSL FOR FREE为例

申请好后,下载完成将得到3个文件ca_bundle.crt、certificate.crt以及私钥private.key,值得注意的是,如果我们要把证书配置到Nginx,按照官方说法,则需要把ca_bundle.crt、certificate.crt文件合并。

我们先把文件上传到服务器,然后执行如下命令

cat certificate.crt ca_bundle.crt

然后,我们把得到的新文件certificate.crt和私钥private.key放到Nginx对应的证书目录上,然后,修改nginx的配置文件,以我的网站为例,(location反向代理的跳转部分可以忽略)

server {
	listen 80;
	server_name www.tzqutao.top tzqutao.top;
	return 301 https://$host$request_uri; 
}

server {
    listen 443 ssl;
    server_name www.tzqutao.top tzqutao.top;
    ssl_certificate      /var/log/nginx/certificate.crt; 
    ssl_certificate_key  /var/log/nginx/private.key;
    
    access_log  /var/log/nginx/www.tzqutao.top.access.log;
    error_log /var/log/nginx/www.tzqutao.top.error.log;
    location / {
        proxy_pass http://172.17.0.3;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

此处,我们需要确保服务器对外的80端口和443端口是通畅的,如果Nginx是在DOCKER中,则还要保证容器的端口和本地80端口和443端口是有存在映射的,完成之后,重启Nginx即可

下面,我们来具体说明WordPress网站,如何启用Https,证书安装好后,我们进入到WordPress根目录的\wp-includes\functions.php,找到代码 require( ABSPATH . WPINC . ‘/option.php’ );大概在第8行。在下方添加以下代码:

add_filter('script_loader_src', 'agnostic_script_loader_src', 20,2); function agnostic_script_loader_src($src, $handle) { return preg_replace('/^(http|https):/', '', $src); } 
add_filter('style_loader_src', 'agnostic_style_loader_src', 20,2); function agnostic_style_loader_src($src, $handle) { return preg_replace('/^(http|https):/', '', $src); }

打开网站根目录\wp-config.php文件找到代码,在下方添加如下代码:

*
* @package WordPress
*/

$_SERVER['HTTPS'] = 'on';
define('FORCE_SSL_LOGIN', true);
define('FORCE_SSL_ADMIN', true);

此时,我们登录到WordPress后台,把WordPress站点地址修改为HTTPS的路径,就完成了HTTP到HTTPS的迁移

Amazon Linux系统Docker定时重启

我们部署完DOCKER容器之后,有时候需要定时设定重启时间用来回收容器的内存资源,如果当前服务器是亚马逊OS系统的,可以使用以下方法

首先,我们先安装crontab,所谓crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。有点像WINDOS系统中的定时任务

sudo yum install cronie

启动并设置crond服务开机自启:

sudo systemctl start crond
sudo systemctl enable crond

然后,我们可以配置当前用户的crontab文件:

crontab -e

格式如下:比如,我们想每日凌晨一点重启容器WordPress、mystifying_hoover、mathapp,就可以按照下面的写法

0 1 * * * /usr/bin/docker restart mystifying_hoover
0 1 * * * /usr/bin/docker restart wordpress
0 1 * * * /usr/bin/docker restart mathapp

保存并退出编辑器。系统会自动安装新的crontab文件。验证计划任务是否已经正确安装

crontab -l

看到下面的结果,说明已经定时任务已经启用,下面3个容器会在凌晨1点重启

随机返回二次元老婆

今天在网上,发现一个非常有意思的API,很适合我这个二次元爱好者:“获取二次元图片接口”,API地址文档如下:https://docs.loliapi.com/,于是,基于此,我有了一个有趣的想法,开发了一个小页面:随机5秒钟换新老婆。具体代码如下:

<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width; height=device-height;">
        <title>5秒随机返回老婆</title>
        <style>
            body {
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh;
                margin: 0;
            }
            img {
                max-width: 100%;
                height: auto;
            }
        </style>
        <script type="text/javascript">
            window.onload = function() {
                setInterval(function(){
                    change();
                },5000);
            }
            function change()
            {
                var time=new Date().getTime();
                const img = document.getElementById("targImg");
                const randomImage = "https://www.loliapi.com/acg/?time="+time;
                
                // 更新图片src
                img.src = randomImage;
                img.alt = randomImage;
            }
        </script>
    </head>
    <body>
        <img id="targImg" src="https://www.loliapi.com/acg/?time=11" alt="https://www.loliapi.com/acg/?time=11">
    </body>
</html>

实现后,效果还是非常赞的,很适合我这个宅男

下面,我们看看预览的效果:点此地址跳转

再次,特别感谢网友:LoliApi

Linq多条件字段的多表连接

假设,我们有一个类StationCat,它的具体属性如下:

public class StationCat
{
    public string CustomerModel { get; set; }

    public string baj_cZZLINE { get; set; }

    public string IssuePosition { get; set; }

    public string fact { get; set; }

    public int InputCount { get; set; }

    public int RepCount { get; set; }

    public string S_NM { get; set; }
}

此时,我们有2个由这个类实例化出的实体数组SWAPMaterials和StationCats,当我们想要把SWAPMaterials左连接到StationCats时,条件为SWAPMaterials表中元素的CustomerModel、baj_cZZLINE、S_NM与StationCats表元素对应的属性相等,那我们应该按照如下方式写LINQ

var resSWAPMaterials = (from p in SWAPMaterials
join StationCat in StationCats on new { p.CustomerModel, p.baj_cZZLINE,p.S_NM } equals new { StationCat.CustomerModel,StationCat.baj_cZZLINE,StationCat.S_NM } into p_StationCat
from StationCat_join in p_StationCat.DefaultIfEmpty()
select new StationCat()
{
    CustomerModel = p.CustomerModel,
    baj_cZZLINE = p.baj_cZZLINE,
    IssuePosition = p.IssuePosition,
    fact = p.fact,
    RepCount = p.RepCount,
    S_NM = p.S_NM,
    InputCount = StationCat_join == null ? 0 : StationCat_join.InputCount
}).ToList();

到此,我们就实现了Linq多条件字段的多表连接

使用HTTP请求方式访问.Net的WebServer服务

.Net的WebServer服务,是一项已经过时的技术,随着技术迭代,当前跨平台的通讯,更多使用用JSON格式数据,发起HTTP请求来实现,但是,有时候,我们为了和老的.NET的WebServer框架通讯,不得不处理和面对的问题

WebServer服务,其实说白了,就是用XML的格式来处理通讯问题,我们可以试着写一个.Net的WebServer服务,文件名如下:WMI.asmx

/// <summary>
/// WMI 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。 
[System.Web.Script.Services.ScriptService]
public class WMI : System.Web.Services.WebService
{
    [WebMethod(EnableSession = true)]
    public string WMI01(string Par)
    {
        try
        {
            //业务逻辑
            returnOK;
        }
        catch (Exception ex)
        {
            //处理异常信息
            return "存在异常信息";
        }
        finally
        {
            //如不记录就 就注释以下内容
        }
    }
}

此时,我们可以运行程序,预览页面效果,接下来,我们在向这个server地址提交http请求的时候,可以使用以下方法:

一、在HTTP头信息上,加上“Content-Type”,值为“text/xml; charset=utf-8”

二、撰写XML格式的请求对象文本,注意,<Par>节点就是WebServer的参数节点,<WMI01>节点就是WebServer的控制器中对应的方法函数

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <WMI01 xmlns="http://tempuri.org/">
      <Par>让我们试试看</Par>
    </WMI01>
  </soap:Body>
</soap:Envelope>

最后,我们可以在POSTMAN程序下试试,预览下请求结果,发现<WMI01Result>节点,就是我们程序返回的处理结果

C#路由方式调用RabbitMQ

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

所谓RabbitMQ的路由模式(Routing),就是有选择地(Routing key)接收消息,发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key,仅消费指定路由key的消息

Exchange不再把消息交给每一个绑定的队列,而是根据消息的RoutingKey进行判断,只有队列的RoutingKey与消息的RoutingKey完全一致,才会接收消息,路由模式使用的Exchange类型为Direct类型

举个例子:如在商品库存中增加了1台iphone12,iphone12促销活动消费者指定routing key为iphone12,只有此促销活动会接收到消息,其它促销活动不关心也不会消费此routing key的消息

下面,我们用C#代码来具体实现一下,以下逻辑需要NuGet里的RabbitMQ.Client包支持,首先安装RabbitMQ.Client包

接下来,我们声明一个类库,用来存放消息对象ReceiverMsgData,为了通用,我特地申明了类型,操作,具体对象实体等内容,以下属性仅供参考,安装自己的具体业务需求设计即可

/// <summary>
/// 消息内容
/// </summary>
public class ReceiverMsg
{
    /// <summary>
    /// 消息类型
    /// 10001->定时任务(队列:calculation_remind)
    /// 23001->订单下单分佣计算(队列:calculation_orderaward)
    /// 23005->订单支付成功计算报表(队列:calculation_orderoperate)
    /// 23006->订单售后成功计算报表(队列:calculation_orderoperate)
    /// </summary>
    [Required(ErrorMessage = "{0} 必须填写")]
    public int msg_type { get; set; }

    /// <summary>
    /// 操作人
    /// </summary>
    [Required(ErrorMessage = "{0} 必须填写")]
    public long creator_id { get; set; }

    /// <summary>
    /// 单据id
    /// </summary>
    [Required(ErrorMessage = "{0} 必须填写")]
    public int source_id { get; set; }

    /// <summary>
    /// 单据来源,定时任务专用(1订单)
    /// </summary>
    public int source_type { get; set; }

    /// <summary>
    /// 单据操作,定时任务专用 (1添加,2发货,3签收)
    /// </summary>
    public int action_type { get; set; }

    /// <summary>
    /// 定时任务执行的时间(无需填写,定时任务自动生成)
    /// </summary>
    public DateTime next_remind_time { get; set; }

    /// <summary>
    /// 错误消息系统编码(无需填写,自动重试用)
    /// </summary>
    public int error_msg_id { get; set; }

    /// <summary>
    /// 错误消息所在的队列名称(无需填写,自动重试用)
    /// </summary>
    public string error_msg_domain { get; set; }

    /// <summary>
    /// 消息附加内容
    /// </summary>
    public object msg_body { get; set; }
}

/// <summary>
/// 消息对象
/// UserName:admin
/// Password:Myun@123jx
/// VirtualHost:Publish
/// Port:5672
/// ExchangeName:calculation
/// </summary>
[Serializable]
public class ReceiverMsgData
{
    /// <summary>
    /// VirtualHost
    /// </summary>
    [Required(ErrorMessage = "{0} 必须填写")]
    public string pattern { get; set; }

    /// <summary>
    /// Msgid
    /// </summary>
    public string id { get; set; }

    /// <summary>
    /// 消息内容
    /// </summary>
    [Required(ErrorMessage = "{0} 必须填写")]
    public ReceiverMsg data { get; set; }
}

然后,我们生命一个生产者,用来产生消息

public class RabbitMQHelp
{
    public static IConfigurationRoot configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
    private static string HostName = EncryptHelp.Decrypt(configuration.GetSection("RabbitMQ")["HostName"]);  //RabbitMQ服务IP地址
    private static string UserName = EncryptHelp.Decrypt(configuration.GetSection("RabbitMQ")["UserName"]);  //RabbitMQ用户名
    private static string Password = EncryptHelp.Decrypt(configuration.GetSection("RabbitMQ")["Password"]);  //RabbitMQ密码
    private static int Port = Convert.ToInt32(EncryptHelp.Decrypt(configuration.GetSection("RabbitMQ")["Port"]));//RabbitMQ端口 默认5672
    private static string VirtualHost = EncryptHelp.Decrypt(configuration.GetSection("RabbitMQ")["VirtualHost"]);//Virtual Hosts名称
    private static string ExchangeName = "calculation";//交换机名称

    /// <summary>
    /// 连接配置
    /// </summary>
    private static readonly ConnectionFactory rabbitMqFactory = new ConnectionFactory()
    {
        HostName = HostName,
        UserName = UserName,
        Password = Password,
        Port = Port,
        VirtualHost = VirtualHost
    };

    /// <summary>
    /// 单点精确路由模式,重发消息
    /// </summary>
    public static void DirectExchangeSendMsg(string QueueName, string msgBody)
    {
        using (IConnection conn = rabbitMqFactory.CreateConnection())
        {
            using (IModel channel = conn.CreateModel())
            {
                channel.ExchangeDeclare(ExchangeName, ExchangeType.Direct, durable: true, autoDelete: false, arguments: null);
                channel.QueueDeclare(QueueName, durable: true, autoDelete: false, exclusive: false, arguments: null);
                channel.QueueBind(QueueName, ExchangeName, routingKey: QueueName);

                var props = channel.CreateBasicProperties();
                props.Persistent = true; //消息永久化硬盘上,打开会降低些许性能,但是可以防止服务器意外断电导致消息队列数据消失的情况
                var BytmsgBody = Encoding.UTF8.GetBytes(msgBody);
                channel.BasicPublish(exchange: ExchangeName, routingKey: QueueName, basicProperties: props, body: BytmsgBody);
            }
        }
    }
}

最后,我们声明一个消费者,用来接收消息

/// <summary>
/// 消费者
/// </summary>
/// <param name="QueueName"></param>
public static void ReceiveWeatherInfo(string QueueName)
{
    using (var connection = rabbitMqFactory.CreateConnection())
    {
        using (var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(ExchangeName, ExchangeType.Direct, durable: true, autoDelete: false, arguments: null);
            channel.QueueDeclare(QueueName, durable: true, autoDelete: false, exclusive: false, arguments: null);
            channel.QueueBind(QueueName, ExchangeName, routingKey: QueueName);
            channel.BasicQos(0, 1, false);
            var consumer = new EventingBasicConsumer(channel);

            consumer.Received += (model, ea) =>
            {
                var message = Encoding.UTF8.GetString(ea.Body.ToArray());
                Console.WriteLine($"队列{QueueName}收到的气象信息:{message}");
                channel.BasicAck(ea.DeliveryTag, false);
            };
            channel.BasicConsume(queue: QueueName,autoAck: false,consumer: consumer);
            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }
    }
}

下面,我们演示一下消息发送

//自动处理售后消息
public static void doDealReturnOrder(int Return_Id)
{
    RabbitMQHelp.DirectExchangeSendMsg("calculation_orderoperate", JsonConvert.SerializeObject(new ReceiverMsgData
    {
        id = Guid.NewGuid().ToString(),
        pattern = "Publish",
        data = new ReceiverMsg()
        {
            source_id = Return_Id,
            msg_type = 23019,
        }
    }));//重发消息
}

最后,只要由控制台的Main函数指定队列名称,调度这个消费者方法,就能接收指定队列的消息了,注意我这里,应为发送的消息是序列化的JSON,所以,接收消息需要反序列化一下

.Net Core+Entity Framework读写数据

同步操作

查询数据

为了防止连接数过多,忘记及时释放,建议用using方式操作

using (var context = new MySqlTestDBContext())
{
    var blogs = context.Blogs
        .Where(b => b.Url.Contains("dotnet"))
        .ToList();
}

查询数据,使用的是大量的Linq语言,复杂查询也包括Join等多表连接,和Group By后,Sum(),Avg(),Max(),Min(),FirstOrDefault(),LastOrDefault()等聚合函数,具体可查询Linq的语法,这里不再展开

添加数据

using (var context = new MySqlTestDBContext())
{
	var blog = new Blog { Url = "http://example.com" };
	var Id = context.Blogs.Add(blog).Entity.Id;
	context.SaveChanges();
}

使用 DbSet.Add 方法添加实体类的新实例。 调用 SaveChanges 时,数据将插入到数据库中。

更新数据

找到Blogs表中,BlogId为1的数据,将它的Url字段进行更新

using (var context = new MySqlTestDBContext())
{
	var blog = context.Blogs.FirstOrDefault(i=>i.Id==1);
	blog.Url = "http://example.com/blog";
	context.SaveChanges();
}

删除数据

找到Blogs表中,BlogId为1的数据,将它物理删除

using (var context = new MySqlTestDBContext())
{
    var blog = context.Blogs.FirstOrDefault(i=>i.Id==1);
    context.Blogs.Remove(blog);
    context.SaveChanges();
}

事务

对于大多数数据库提供程序,“SaveChanges” 是事务性的。 这意味着所有操作将一起成功或一起失败,绝不会部分的应用这些操作。

using (var context = new MySqlTestDBContext())
{
    // seeding database
    context.Blogs.Add(new Blog { Url = "http://example.com/blog" });
    context.Blogs.Add(new Blog { Url = "http://example.com/another_blog" });
    context.SaveChanges();
}

using (var context = new MySqlTestDBContext())
{
    // add
    context.Blogs.Add(new Blog { Url = "http://example.com/blog_one" });
    context.Blogs.Add(new Blog { Url = "http://example.com/blog_two" });

    // update
    var firstBlog = context.Blogs.First();
    firstBlog.Url = "";

    // remove
    var lastBlog = context.Blogs.Last();
    context.Blogs.Remove(lastBlog);

    context.SaveChanges();
}

异步操作

当在数据库中执行查询时,异步查询可避免阻止线程。 异步查询对于在胖客户端应用程序中保持响应式 UI 非常重要。 异步查询还可以增加 Web 应用程序中的吞吐量,即通过释放线程,以处理 Web 应用程序中的其他请求

查询数据

public async Task<List<Blog>> GetBlogsAsync()
{
    using (var context = new MySqlTestDBContext())
    {
        return await context.Blogs.ToListAsync();
    }
}

数据集除了ToList方法外,还有一个ToListAsync方法,返回的是一个异步的Task对象

保存数据

同理,异步保存也有一个方法SaveChangesAsync(),返回的也是一个异步Task对象

public static async Task AddBlogAsync(string url)
{
    using (var context = new BloggingContext())
    {
        var blog = new Blog { Url = url };
        context.Blogs.Add(blog);
        await context.SaveChangesAsync();
    }
}