Chemmy's Blog

chengming0916@outlook.com

先决条件
本教程假定 RabbitMQ 已经安装,并运行在localhost 标准端口(5672)。如果你使用不同的主机、端口或证书,则需要调整连接设置。

从哪里获得帮助
如果您在阅读本教程时遇到困难,可以通过邮件列表 联系我们

主题#

(使用 .NET 客户端)

教程[4] 中,我们改进了我们日志系统。我们用direct交换器替换了只能呆滞广播消息的fanout交换器,从而可以有选择性的接收日志。

虽然使用direct交换器改进了我们的系统,但它仍然有局限性 - 不能基于多个标准进行路由。

在我们的日志系统中,我们可能不仅要根据日志的严重性订阅日志,可能还要根据日志分发源来订阅日志。或许您可能从 unix syslog 工具中了解过这种概念,syslog 工具在路由日志的时候是可以既基于严重性(info/warn/crit…)又基于设备(auth/cron/kern…)的。

这种机制会给我们带来极大的灵活性 - 我们可以仅监听来自cron的关键错误日志,与此同时,监听来自kern的所有日志。

要在我们的日志系统中实现这一特性,我们需要学习更复杂的topic交换器。

Topic交换器#

发送到topic交换器的消息不能随意指定routing key,它必须是一个由点分割的单词列表,这些单词可以是任意内容,但通常会在其中指定一些与消息相关的特性。请看一些合法的路由键示例:stock.usd.nysenyse.vmwquick.orange.rabbit,路由键可以包含任意数量的单词,但不能超过255个字节的上限。

binding key也必须是相同的形式,topic交换器的背后逻辑与direct交换器类似 - 使用指定路由键发送的消息会被分发到与其绑定键匹配的所有队列中。不过对于绑定键来说,有两个重要的特殊情况需要注意:

  • *(星号)可以代替一个单词。
  • #(哈希)可以代替零个或多个单词。

下图示例是对上述内容最简单的解释:

在这个示例中,我们打算发送的消息全是用来描述动物的,这些消息会使用由三个单词(两个点)组成的路由键来发送。在路由键中,第一个单词用来描述行动速度、第二个是颜色、第三个是物种,即:<speed>.<colour>.<species>

我们创建了三个绑定:Q1绑定了键.orange.,Q2绑定了键*.*.rabbitlazy.#

这些绑定可以被概括为:

  • Q1对所有橙色的动物感兴趣。
  • Q2对兔子以及所有行动缓慢的动物感兴趣。

路由键为quick.orange.rabbit的消息会被发送到这两个队列,消息lazy.orange.elephant也会被发送到这两个队列。另外,quick.orange.fox只会进入第一个队列,lazy.brown.fox只会进入第二个队列。lazy.pink.rabbit只会被发送到第二个队列一次,尽管它匹配了两个绑定(避免了消息重复)。quick.brown.fox没有匹配的绑定,因此它将会被丢弃。

如果我们打破约定,发送使用一个或四个单词(例如:orangequick.orange.male.rabbit)作路由键的消息会发生什么?答案是,这些消息因为没有匹配到任何绑定,将被丢弃。

但是,另外,例如路由键为lazy.orange.male.rabbit的消息,尽管它有四个单词,也会匹配最后一个绑定,并将被发送到第二个队列。

Topics 交换器
topic交换器的功能是很强大的,它可以表现出一些其他交换器的行为。
当一个队列与键(哈希)绑定时, 它会忽略路由键,接收所有消息,这就像fanout交换器一样。
当特殊字符*(星号)和(哈希)未在绑定中使用时,topic交换器的行为就像direct交换器一样。

组合在一起#

我们将要在我们的日志系统中使用topic交换器,首先假设日志的路由键有两个单词组成:<facility>.<severity>

代码与上一篇 教程 中的代码几乎相同。

EmitLogTopic.cs的代码:

using System;
using System.Linq;
using RabbitMQ.Client;
using System.Text;

class EmitLogTopic
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using(var connection = factory.CreateConnection())
        using(var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(exchange: "topic_logs",
                                    type: "topic");

            var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
            
            var message = (args.Length > 1)
                          ? string.Join(" ", args.Skip(1).ToArray())
                          : "Hello World!";
            var body = Encoding.UTF8.GetBytes(message);
            
            channel.BasicPublish(exchange: "topic_logs",
                                 routingKey: routingKey,
                                 basicProperties: null,
                                 body: body);
                                 
            Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
        }
    }
}

ReceiveLogsTopic.cs的代码:

using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

class ReceiveLogsTopic
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using(var connection = factory.CreateConnection())
        using(var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(exchange: "topic_logs", type: "topic");
            var queueName = channel.QueueDeclare().QueueName;

            if(args.Length < 1)
            {
                Console.Error.WriteLine("Usage: {0} [binding_key...]",
                                        Environment.GetCommandLineArgs()[0]);
                Console.WriteLine(" Press [enter] to exit.");
                Console.ReadLine();
                Environment.ExitCode = 1;
                return;
            }

            foreach(var bindingKey in args)
            {
                channel.QueueBind(queue: queueName,
                                  exchange: "topic_logs",
                                  routingKey: bindingKey);
            }

            Console.WriteLine(" [*] Waiting for messages. To exit press CTRL+C");

            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body);
                var routingKey = ea.RoutingKey;
                Console.WriteLine(" [x] Received '{0}':'{1}'",
                                  routingKey,
                                  message);
            };
            channel.BasicConsume(queue: queueName,
                                 autoAck: true,
                                 consumer: consumer);

            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }
    }
}

请运行以下示例:

要接收所有日志:

cd ReceiveLogsTopic
dotnet run "#"

要接收来自设备kern的所有日志:

cd ReceiveLogsTopic
dotnet run "kern.*"

或者,如果您只想监听级别为critical的日志:

cd ReceiveLogsTopic
dotnet run "*.critical"

您可以创建多个绑定:

cd ReceiveLogsTopic
dotnet run "kern.*" "*.critical"

使用路由键kern.critical发出日志:

cd EmitLogTopic
dotnet run "kern.critical" "A critical kernel error"

希望运行这些程序能让您玩得开心。要注意的是,这些代码没有针对路由键和绑定键做任何预设,您可以尝试使用两个以上的路由键参数。

EmitLogTopic.csReceiveLogsTopic.cs 的完整源码)

接下来,在 教程[6] 中将了解如何将往返消息作为远程过程调用。

写在最后#

本文翻译自 RabbitMQ 官方教程 C# 版本。如本文介绍内容与官方有所出入,请以官方最新内容为准。水平有限,翻译的不好请见谅,如有翻译错误还请指正。

  • 原文链接:RabbitMQ tutorial - Topics
  • 实验环境:RabbitMQ 3.7.4 、.NET Core 2.1.3、Visual Studio Code
  • 最后更新:2018-09-06

环境检查

1
ls /sys/firmware/efi/efivars #UEFI/BIOS检测

若该目录不存在,则 ArchISO 是以 BIOS/CSM 模式启动,否则是以 UEFI 模式启动。

通常而言,UEFI 系统须使用 GPT 分区才能引导,BIOS 系统须使用 MBR 分区才能引导。

分区

fdisk

1
fdisk -l //查看所有分区情况

常用fdisk命令:p 显示当前磁盘分区,d 删除指定分区,n 创建新分区, a 为指定分区创建启动标记,t 更改分区格式, w将磁盘分区信息写入磁盘。

parted

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# parted /dev/sda

检查 MINOR #对文件系统进行一个简单的检查
cp [FROM-DEVICE] FROM-MINOR TO-MINOR #将文件系统复制到另一个分区
help [COMMAND] #打印通用求助信息,或关于 COMMAND 的信息
mklabel 标签类型 #创建新的磁盘标签 (分区表)
mkfs MINOR 文件系统类型 #在 MINOR 创建类型为“文件系统类型”的文件系统
mkpart 分区类型 [文件系统类型] 起始点 终止点 #创建一个分区
mkpartfs 分区类型 文件系统类型 起始点 终止点 #创建一个带有文件系统的分区
move MINOR 起始点 终止点 #移动编号为 MINOR 的分区
name MINOR 名称 #将编号为 MINOR 的分区命名为“名称”
print [MINOR] #打印分区表,或者分区
quit #退出程序
rescue 起始点 终止点 #挽救临近“起始点”、“终止点”的遗失的分区
resize MINOR 起始点 终止点 #改变位于编号为 MINOR 的分区中文件系统的大小
rm MINOR #删除编号为 MINOR 的分区
select 设备 #选择要编辑的设备
set MINOR 标志 状态 #改变编号为 MINOR 的分区的标志


格式化分区

1
2
3
4
mkfs.vfat -F 32 /dev/sda1 #生成ESP分区的文件系统FAT32
mkswap /dev/sda2 #格式化swap
mkfs.ext4 /dev/sda3 #格式化ext4
mkfs.xfx /dev/sda4 #格式化xfs

分区方案

1
2
3
4
/dev/sda1 /boot 500M
/dev/sda2 swap 8G(根据内存大小调整)
/dev/sda3 / 200G
/dev/sda4 /home 剩余所有空间

挂载分区

1
#mount /dev/sda6 /mnt # 挂载根分区

非UEFI挂载boot

1
2
# mkdir -p /mnt/boot
# mount /dev/sda1 /mnt/boot

建立efi目录,把EFI分区装载到刚建立的efi目录上。

1
2
#mkdir -p /mnt/boot/efi
#mount /dev/sdc1 /mnt/boot/efi

挂载交换分区和home

1
2
3
#swap on /dev/sda5
#mkdir -p /mnt/home
#mount /dev/sda7 /mnt/home

生成引导

  • BIOS:

依赖包: grub os-prober

1
2
# grub-install --recheck /dev/<目标磁盘>
# grub-mkconfig -o /boot/grub/grub.cfg
  • UEFI:—如果BIOS是UEFI的,就要用下面的命令安装grub了

依赖包: dosfstools grub efibootmgr

1
2
# grub-install --target=x86_64-efi --efi-directory=<EFI 分区挂载点> --bootloader-id=arch_grub --recheck
# grub-mkconfig -o /boot/grub/grub.cfg

低格填零

1
# dd if=/dev/zero of=/dev/sda bs=16M

1. 下载镜像 制作启动U盘

Arch Linux 官方网站 https://www.archlinux.org/

制作启动盘工具 Rufus - 轻松创建 USB 启动盘

Linux下

1
dd if=*iso of /dev/sdb bs=41M

2. 网络连接

参考 Linux配置网络及SSH配置

3. 选择软件源

推荐国内的用户选择http://mirrors.ustc.edu.cn 默认的mirrorlist是开启所有源的,因此我们使用sed先在所有源的前面加上#

1
2
#sed -i "s/^\b/#/g" /etc/pacman.d/mirrorlist
#nano /etc/pacman.d /mirrorlist

将mirrors.ustc.edu.cn前面的#去掉

4. 分区/格式化/挂载

参考 Linux硬盘分区

5. 安装基本系统

1. 将基本系统安装到根目录上去

1
#pacstrap /mnt base base-devel
其实,这里安装的基本系统也肯定有自己用不到的冗余功能,例如我就用不到nano文本编辑器,但系统会默认给安上。如果知道基本系统每个文件的作用,其实也完全可以自定义安装。比如:
1
2
#pacstrap /mnt bash coreutils file filesystem grub2 linux pacman \
procps-ng syslog-ng glibc systemd-sysvcompat shawd dhcpcd vi
> 如果你想使用ifconfig之类的工具,请在上面加上net-tools

2. 生成fstab

用下面命令生成 fstab。如果想使用 UUIDs,使用 -U 选项;如果想使用标签,用 -L 选项.

1
#genfstab -U -p /mnt >>/mnt/etc/fstab
> [red]**后面如果出现问题,请不要再次运行genfstab**[red],如果需要,手动编辑/etc/fstab /etc/fstab文件在运行genfstab后应该被检查一下。如果之前你生成了一个EFI系统分区,那么 genfstab给EFI分区添加了错误的选项,会导致无法启动。因此你需要移除EFI分区的所有选项,除了noatime. 对其他分区, 替换"codepage=cp437" 为 "codepage=437" , 会挂载失败导致systemd进入恢复模式。

3. 切换到新系统中

1
2
#arch-chroot /mnt
#sh-4.2#bash
> 到这一步之后,开始系统的主要配置,如果下面文件不存在,需要手动创建。 > 理解并完全安装步骤设置是保证系统配置成功的关键。

4. 对新的基本系统进行设置

写入本机的字符编码方式

1
2
3
#nano /etc/locale.conf #

LANG=en_US.UTF-8 #简略写法 echo LANG= en_US.UTF-8 >> locale.conf

locale.conf 文件默认不存在,一般设置LANG就行了,它是其它设置的默认值。

1
2
3
4
/etc/locale.conf

LANG=zh_CN.UTF-8
LC_TIME=en_GB.UTF-8

修改本机编码

1
# nano /etc/locale.gen  将用不到的编码全删掉,只保留en_US与zh_CN的几行。 

默认情况下 /etc/locale.gen 是一个仅包含注释文档的空文件。选定你需要的本地化类型(移除前面的#即可), 比如中文系统可以使用:

1
2
3
4
5
en_US.UTF-8 UTF-8
zh_CN.GB18030 GB18030
zh_CN.GBK GBK
zh_CN.UTF-8 UTF-8
zh_CN GB2312

对系统的编码进行更新

1
#locale-gen 

写入本机的名称

1
# nano /etc/hostname #简略写法:echo {name} >/etc/hostname,也是一样的。

写入键盘布局方案

1
#nano /etc/vconsole.conf

美式键盘,如下:

1
2
3
KEYMAP=us
FONT=
FONT_MAP=

写入时区

1
2
3
# nano /etc/timezone

Asia/Shanghai

建立时区的软链接

1
#ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

设定系统将用的时间方案

1
#hwclock --systohc --utc

这个时间方案我是试过很多次,如果是双系统,电脑里还有win系统的话,建议设为:–localtime,否则可设为—utc。不过,我现在虽然也用双系统,但还是设的utc,因为设为–localtime虽然在win下时间不会出错,但回到linux下,经常系统会有些古怪的问题,比如,升级系统之时,报密钥错误。使用–utc,虽然在linux下时间会慢8个多小时,但毕竟对整个系统没有影响。

生成内核的启动镜象。

1
#mkinitcpio -p linux

安装必要工具

安装必要的网络工具以便于开机后可以配置网络连接(包括无线)

1
2
3
4
#pacman –S wpa_supplicant net-tools
#pacman -S dialog
#pacman -S netctl
#pacman -S wireless_tools

6. 安装引导

安装grub

1
2
#pacman -S grub-bios os-prober
#grub-install /dev/sda

UEFI 注意分区,参考: Linux分区.格式化.挂载

1
2
#pacman -S grub-bios efibootmgr os-prober
#grub-install --efi-directory=/boot/efi --bootloader-id=arch-grub --target=x86_64-efi

生成启动菜单

1
2
3

#grub-mkconfig -o /boot/grub/grub.cfg
#nano /boot/grub/grub.cfg

生成grub引导windows

如何生成grub引导文件grub.cfg 这里我们需要充分参考点击打开链接grub的说明。首先,需要额外安装一个 os-prober的软件包,直接pacman就行;然后grub-makeconfig 到/boot/grub/grub.cfg 。此时才能生成可以引导多系统的引导文件.如下图。
Window引导

开机自启网络

1
2
#systemctl enable dhcpcd@.service
#dhcpcd

卸载挂载的分区并重启

1
2
#umount /mnt/{boot,home,mnt}
# reboot

基本系统已安装完成

7. 系统配置

忘记安装net-tools补救

1
2
ip link show #查看网卡
ip link set eth0 up # 启用网卡

如果是DHCP的当然简单,直接dhcpcd即可,如果是固定IP的,则要如下操作:

1
2
3
#ip addr add 固定IP/24 dev eth0
#ip link set dev eth0 up
#ip route add default via 网关

系统更新

1
#pacman –Syu

添加用户

1
2
3
#useradd -m 新用户 #新建用户
#passwd 新用户 #指定密码:
#usermod -a -G video,audio,lp,log,wheel,optical,scanner,games,users,storage,power 新用户 #指定用户所在的组

sudo权限

1
nano /etc/sudoers (添加sudo权限)

放开%wheel %sudo权限

sudo命令补全

1
2
#sudo pacman -S bash-completion 
#echo "source /usr/share/bash-completion/bash_completion" >>/home/$USER/.bashrc

更新源列表

1
#pacman -S reflector 

reflector是一个可以从arch官方MirrorStatus列表取回最新mirrorlist的脚本,并且可以根据最新同步时间和速度排序。
下面先说如何自动配置源列表。直接终端输入命令5(注意备份原有源列表)

1
#reflector --verbose --country 'China' -l 200 -p http --sort rate --save /etc/pacman.d/mirrorlist

安装yaourt

1
#vim /etc/pacman.conf
1
2
3
4
[archlinuxcn]
#The Chinese Arch Linux communities packages.
SigLevel = Optional TrustAll
Server = http://repo.archlinuxcn.org/$arch
1
# pacman -Syu yaourt

安装powerpill

1
#yaourt -S powerpill

powerpill是一个可以从多个源多线程下载软件包的程序,类似于迅雷一样,可以明显提升更新速度,相当于pacman的外壳程序,使用方法完全和pacman相同。下面说说powerpill,玩arch的人不知道powerpill是不行的,需要注意的是它也是要调用reflector的,但并不是作为依赖。如果安装reflector后powerpill更新前会默认从mirrorstatus取回45个最新更新的源地址,然后并行下载,否则就是读取/etc/pacman.d/mirrorlist然后配置下载。当然我们推荐第一种,总不能每次都手动执行

7. 驱动显卡

安装显卡驱动

1
2
3
4
5
6
7
# pacman -S mesa
# lspci | grep VGA(查看本机的显卡类型)
# pacman -Ss xf86-video | less(查看能够安装的显卡类型)
# pacman -S …… 安装显卡驱动(或者可以直接所有驱动都自动安装)
# pacman –S xf86-video-vesa
# pacman –S xf86-video-nouveau #如果是ATI显卡的话,要安xf86-video-ati;
# pacman –S virtualbox-guest-utils #虚拟机

安装系统基础程序:

1
2
# pacman -S xorg-server xorg-xinit xorg-utils xorg-server-utils dbus # 先安装x-window服务
# pacman –S xterm xorg-xclock xorg-twm # 安装测试环境

重设系统的编码方式

编辑.xinitrc,把以下内容添加到文件最开始。内可以使用你所喜欢的编辑器,比如nano

1
2
LANG=zh_CN.UTF-8
LC_ALL="zh_CN.UTF-8"

更新系统的编码:

1
#locale-gen

更新一下系统的时间

1
2
# date -s "2013-01-14 14:40:10"
# hwclock --systohc

音频管理

1
# pacman -S alsa-utils pulseaudio-alsa

安装网络管理工具

1
2
3
# pacman –S networkmanager network-manager-applet wireless_tools
# systemctl enable NetworkManager
# systemctl start NetworkManager

安装桌面

击右键菜单,找到文件管理器,然后进入到目录/usr/share/applications/下,你会看到你已经安装完成的程序,全都可以从这儿启动。此时,你不妨复制几个常用的到你的用户目录:/home/新用户/桌面/下去。复制之后,你会在你的桌面上,看到这些程序的启动器。

安装完ibus之后,在/home/$USER/.xinitrc文件中,写入:

1
2
3
4
export GTK_IM_MODULE=ibus
export QT_IM_MODULE=ibus
export XMODIFIERS=@im=ibus
ibus-daemon -d -x

Windows下的磁盘挂载
参考Windows下的磁盘挂载

Xfce主题

字体及补丁

1
2
3
# pacman -S ttf-dejavu ttf-ubuntu-font-family ttf-arphic-ukai ttf-arphic-uming
# pacman -S wqy-microhei wqy-bitmapfont wqy-zenhei ttf-fireflysung
$ yaourt -S cairo-ubuntu libxft-ubuntu freetype2-ubuntu fontconfig-ubuntu #以普通用户身份执行

安装系统主题:

1
sudo pacman -S gtk-aurora-engine gtk-engine-murrine 

鼠标主题:

1
sudo pacman -S xcursor-vanilla-dmz xcursor-vanilla-dmz-aa

图标主题:

1
# pacman -S gnome-icon-theme-extras oxygen-icons human-icon-theme lxde-icon-theme tangerine-icon-theme

针对笔记本电脑的配置:(Speed-step 、 Suspend 等功能)

1
2
# pacman -S  gnome-power-manager  volumeicon
$ yaourt -S laptop-mode-tools pmount

Grub主题
在启动过程中发现Xfce桌面启动载入真心简陋,没有关系,我们在AUR里下载一个balou并设置就好了。

1
$ yaourt -S archlinux-themes-balou

下面来配置grub的启动界面。AUR里有一个非常棒的包grub2-theme-archlinux

1
$ yaourt -S grub2-theme-archlinux

安装后编辑/etc/default/grub,
#GRUB_THEME="/path/to/gfxtheme"改为GRUB_THEME="/boot/grub/themes/Archlinux/theme.txt"
GRUB_GFXMODE=auto改为GRUB_GFXMODE=1024x768修改完成后重新生成一下启动文件

1
# grub-mkconfig -o /boot/grub/grub.cfg

安装 i3 窗口管理器

1
# pacman -S i3

安装 lightdm 显示管理器,

1
# pacman -S lightdm-gtk3-greeter

然后

1
2
# systemctl enable lightdm
# systemctl start lightdm

8. 桌面及美化

本文方案适用于Microsoft Sql Server 2008/2012/2012 r2/2014版本,以下简称MSSQLSERVER。

MSSQL默认是不允许远程连接,并且禁用sa账户的。如果想要在本地用SSMS连接远程服务器上的MSSQLSERVER,需要做两个部分的配置:

1. SQL SERVER MANAGEMENT STUDIO(SSMS)

2. SQL SERVER配置管理器(SQL SERVER CONFIGURATION MANAGER - SSCM)

并且需要注意的是,有些地方如果没有生效,请重启一下sql server(可以从SSCM里,也可以从系统服务中找),下面是详细的步骤:

STEP1. 打开SSMS,使用Windows身份连接数据库,登录后,右键选择“属性”

STEP 2. 选择“安全性”,选中SQL SERVER和Windows身份验证模式

STEP 3. 再选择“连接”,勾选“允许远程连接此服务器”,然后点击“确定”按钮。

STEP 4. 展开“安全性” -》登录名 -》sa,右键选择“属性”

STEP 5. 在“常规”中,改好你自己的密码,这是你sa登录的密码。

STEP 6. 在“状态”中,启用sa登录,点击“确定”按钮

STEP 7. 右键数据库server,选择“方面”

STEP 8. 选择“服务器配置”,找到RemoteAccessEnabled,设置为“True”

STEP 9. 重新启动SQL SERVER服务,退出当前的连接,这时候应该可以用sa进行登录了。

STEP 10. 配置SSCM,选中左侧的“SQL SERVER服务”,确保右侧的“SQL SERVER”以及“SQL SERVER BROWER”正在运行,选择“网络配置”,双击TCP/IP,确保状态为“启用”

STEP 11. 在Client里也确保TCP/IP是启用的,默认的端口都是1433,可以自己修改,非默认端口需要在连接字符串里显式指明。

STEP 12. 到这里再次重启SQL SERVER服务,应该就可以用了。不过还不能用,确认防火墙端口设置,并把SQL SERVER安装目录下,C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\Binn\sqlservr.exe添加到允许的列表中。

1、安装nuget包MSBuildTasks

2、编辑项目的csproj文件,找到被注释掉的target的beforebuild,去掉注释,添加如下代码。代码如下。

1
2
3
4
5
6
7
8
9
<Target Name="BeforeBuild">
<Version VersionFile="Propertiesversion.txt" Major="1" Minor="0" BuildType="Automatic" StartDate="09/01/2017" RevisionType="BuildIncrement">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<AssemblyInfo CodeLanguage="CS" OutputFile="Properties\FileVersionInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />
</Target>     

3、编译项目一次就会在项目文件夹下生成 Propertiesversion.txt

4、在AssemblyInfo.cs文件中包含了AssemblyVersion和AssemblyFileVersion,这里把AssemblyFileVersion单独放到了FileVersionInfo.cs中,编译时会自动生成FileVersionInfo.cs文件,其内容为AssemblyFileVersion,把该文件包含到项目中即可。这里并没有让程序自动生成AssemblyVersion。

这种方式生成版本号会在vs编译时更新版本号。

WPF引用xmlns:i=”clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”后可以设置很多自定义的行为:

       <i:Interaction.Triggers\>
            <i:EventTrigger EventName\="ValueChanged"\>
                <i:InvokeCommandAction Command\="{Binding ValueChangedCommand}" />
            </i:EventTrigger\>
        </i:Interaction.Triggers\>

<UserControl.Resources>
<ControlTemplate x:Key=“trackThumb” TargetType=“{x:Type Slider}”>
<Border Background=“{TemplateBinding Background}” BorderBrush=“{TemplateBinding BorderBrush}” BorderThickness=“{TemplateBinding BorderThickness}”>
<Grid>
<Track x:Name=“PART_Track”>
<Track.Thumb>
<Thumb Width=“10”>
<i:Interaction.Triggers>
<i:EventTrigger EventName=“DragCompleted”>
<i:InvokeCommandAction Command=“{Binding ValueChangedCommand}” />
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</UserControl.Resources>

当时当我们有一些自定义的需求时,需要自定义行为,例如给每个控件添加一个双击复制文本的行为。

1. 定义行为

public class MouseDoubleClickCopyTextBehavior : Behavior {
///


/// 需要复制的内容
///

public string CopyText
{ get { return (string)GetValue(CopyTextProperty); } set { SetValue(CopyTextProperty, value); }
} public static readonly DependencyProperty CopyTextProperty = DependencyProperty.Register(“CopyText”, typeof(string), typeof(MouseDoubleClickCopyTextBehavior), new PropertyMetadata(null)); protected override void OnAttached()
{ base.OnAttached();
AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
} protected override void OnDetaching()
{ base.OnDetaching();
AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObject_PreviewMouseLeftButtonDown;
} void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ if (e.ClickCount >= 2)
Clipboard.SetDataObject(CopyText);
}

  1. 控件绑定行为