《ASP.NET Core 6框架揭秘》实例演示[10]:Options基本编程模式

依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中。除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对象,这篇文章演示几种典型的编程模式。[本文节选《ASP.NET Core 6框架揭秘》第6章]

[601]将配置绑定为Options对象(源代码)
[602]具名Options的注册和提取(源代码)
[603]Options与配置源的实时同步(匿名Options)(源代码)
[604]Options与配置源的实时同步(具名Options)(源代码)
[605]用代码方式初始化Options(匿名Options)(源代码)
[606]用代码方式初始化Options(具名Options)(源代码)
[607]针对依赖服务的Options设置(源代码)
[608]验证Options的有效性(源代码)

[601]将配置绑定为Options对象

Options模式采用依赖注入的方式提供Options对象,但是由依赖注入容提供的是一个IOptions<TOptions>对象,后者为我们提供承载配置选项的Options对象。Options模式的核心接口和类型定义在“Microsoft.Extensions.Options”这个NuGet包。在为创建的控制台项目添加了该NuGet包的引用后,我们定义了如下这个Profile类型。

public class Profile
{public Gender Gender { get; set; }public int Age { get; set; }public ContactInfo? ContactInfo { get; set; }
}public class ContactInfo
{public string? EmailAddress { get; set; }public string? PhoneNo { get; set; }
}public enum Gender
{Male,Female
}

我们在项目根目录下创建一个名为profile.json的配置文件,并在启动定义了如下的内容。为了使该文件能够在编译后自动复制到输出目录,我们需要将“Copy to Output Directory”属性设置为“Copy Always”。

{"gender": "Male","age": "18","contactInfo": {"emailAddress" : "foobar@outlook.com","phoneNo": "123456789"}
}

在如下演示的程序中。我们调用AddJsonFile扩展方法将针对JSON配置文件(profile.json)的配置源注册到创建的ConfigurationBuilder对象上,并最终将IConfiguration对象构建出来。我们接下来创建了一个ServiceCollection对象,通过调用它的AddOptions扩展方法注册Options模式的核心服务。我们然后将创建的IConfiguration对象作为参数调用了ServiceCollection对象Configure<Profile>扩展方法,其目的在于利用这个IConfiguration对象来绑定作为Options的Profile对象。扩展方法Configure<TOptions>定义在“Microsoft.Extensions.Options.ConfigurationExtensions”这个NuGet包中,所以我们还得为演示程序添加该包的引用。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;var configuration = new ConfigurationManager();
configuration.AddJsonFile("profile.json");
var profile = new ServiceCollection().AddOptions().Configure<Profile>(configuration).BuildServiceProvider().GetRequiredService<IOptions<Profile>>().Value;
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}");

在成功构建出作为依赖注入容器的IServiceProvider对象后,我们调用其GetRequiredService<T>扩展方法得到一个IOptions<Profile>对象,后者利用其Value属性提供所需的Profile对象。我们将这个Profile承载的相关信息输出到控制台上。程序运行后将在控制台上输出如图1所示结果。

78a90504ac85b988db8c77bb2c213aa0.png
图1 绑定配置生成的Profile对象

[602]具名Options的注册和提取

IOptions<TOptions>对象在整个应用范围内只能提供一个单一的Options对象,但是在很多情况下我们需要利用多个同类型的Options对象来承载不同的配置。就拿演示实例中用来表示个人信息的Profile类型来说,应用程序中可能会使用它来表示不同用户的信息,如张三、李四和王五。为了解决这个问题,我们可以在调用Configure<TOptions>方法对配置选项进行设置的时候指定一个具体的名称,然后使用IOptionsSnapshot<TOptions>来代替IOptions<TOptions>以提供指定名称的Options对象。为了演示提供针对不同用户的Profile对象,我们通过修改profile.json文件使之包含两个用户(“foo”和“bar”)的信息,具体内容如下所示。

{"foo": {"gender": "Male","age": "18","contactInfo": {"emailAddress": "foo@outlook.com","phoneNo": "123"}},"bar": {"gender": "Female","age": "25","contactInfo": {"emailAddress": "bar@outlook.com","phoneNo": "456"}}
}

具名Options的注册和提取体现在如下的演示程序中。如代码片段所示,在调用IServiceCollection接口的Configure<TOptions>扩展方法时,我们将注册的映射关系分别命名为“foo”和“bar”,提供原始配置数据的IConfiguration对象也由原来的ConfigurationRoot对象变成它的两个子配置节。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;var configuration = new ConfigurationManager();
configuration.AddJsonFile("profile.json");
var serviceProvider = new ServiceCollection().AddOptions().Configure<Profile>("foo", configuration.GetSection("foo")).Configure<Profile>("bar", configuration.GetSection("bar")).BuildServiceProvider();var optionsAccessor = serviceProvider.GetRequiredService<IOptionsSnapshot<Profile>>();
Print(optionsAccessor.Get("foo"));
Print(optionsAccessor.Get("bar"));static void Print(Profile profile)
{Console.WriteLine($"Gender: {profile.Gender}");Console.WriteLine($"Age: {profile.Age}");Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");
}

我们调用IServiceProvider对象的GetRequiredService<TService>扩展方法得到一个IOptionsSnapshot<TOptions>服务,并将用户名作为参数调用其Get方法得到对应的Profile对象。程序运行后,针对两个用户的基本信息将以图2所示的形式输出到控制台上。

7888f36d1d46cefe292b6d98b3661438.png
图2 根据用户名提取对应的Profile对象

[603]Options与配置源的实时同步(匿名Options)

前面演示的第一个实例利用JSON文件定义了一个单一Profile对象的信息,我们现在对它做相应的修改来演示如何监控这个JSON文件,并在文件更新之后加载新的内容来生成对Profile对象进行绑定的IConfiuration对象。如下面的代码片段所示,我们在调用AddJsonFile扩展方法注册对应配置源时应将该方法的参数reloadOnChange设置为True,从而开启对对应配置文件的监控功能。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;var configuration = new ConfigurationManager();
configuration.AddJsonFile(path : "profile.json",optional : false,reloadOnChange : true);new ServiceCollection().AddOptions().Configure<Profile>(configuration).BuildServiceProvider().GetRequiredService<IOptionsMonitor<Profile>>().OnChange(profile =>{Console.WriteLine($"Gender: {profile.Gender}");Console.WriteLine($"Age: {profile.Age}");Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");});
Console.Read();

我们利用作为依赖注入容器得到IOptionsMonitor<TOptions>对象,并调用它的OnChange方法注册了一个类型为Action<TOptions>的委托作为回调。该回调会在Options内容发生变化时自动执行,而作为输入的正是重新生成的Options对象。程序启动后针对配置文件的任何修改都会导致新的数据被打印在控制台上。比如我们先后修改了年龄(25)和性别(Female),新的数据将按照图3所示的形式反映在控制台上。

cd4f8cc13392aa97b499ca17507a8421.png

图3 及时提取新的Profile对象并应用到程序中(匿名Options)

[604]Options与配置源的实时同步(具名Options)

具名Options同样可以采用类似的编程模式来。我们在前面演示程序的基础上做了如下修改。如代码片段所示,在得到IOptionsMonitor<TOptions>服务之后,我们调用另一个OnChange方法重载注册了类型为Action<TOptions, String>的委托作为回调,该委托的第二个参数表示的正是在注册Configure<TOptions>指定的Options名称。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;var configuration = new ConfigurationManager();
configuration.AddJsonFile(path : "profile.json",optional : false,reloadOnChange : true);new ServiceCollection().AddOptions().Configure<Profile>("foo", configuration.GetSection("foo")).Configure<Profile>("bar", configuration.GetSection("bar")).BuildServiceProvider().GetRequiredService<IOptionsMonitor<Profile>>().OnChange((profile, name) =>{Console.WriteLine($"Name: {name}");Console.WriteLine($"Gender: {profile.Gender}");Console.WriteLine($"Age: {profile.Age}");Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");});
Console.Read();

改动后的程序启动之后,针对配置文件所作的任何更新都会体现在控制台上。比如我们分别修改了用户foo的年龄(25)和用户bar的性别(Male),新的内容将以图4所示的形式及时呈现在控制台上。

d0857cf433ec3286fac650b2c3c2ef7c.png

图4 及时提取新的Profile对象并应用到程序中(具名Options)

[605]用代码方式初始化Options(匿名Options)

前面演示的几个实例具有一个共同的特征,那就是都采用承载配置的IConfiguration对象来绑定Options对象。实际上Options是一个完全独立于配置系统的框架,利用配置绑定的形式来对Options对象进行初始化仅仅是该框架提供的一个小小的扩展而已。我们现在摒弃配置文件,转而采用编程的方式直接对Options进行初始化。如下面的代码片段所示,在调用IServiceCollection接口的Configure<Profile>扩展方法时,我们指定一个Action<Profile>委托来对作为Options的Profile对象进行初始化。修改后的程序运行后会同样在控制台上产生图1所示的输出结果。

using App;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;var profile = new ServiceCollection().AddOptions().Configure<Profile>(it =>{it.Gender = Gender.Male;it.Age = 18;it.ContactInfo = new ContactInfo{PhoneNo = "123456789",EmailAddress = "foobar@outlook.com"};}).BuildServiceProvider().GetRequiredService<IOptions<Profile>>().Value;Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");

[606]用代码方式初始化Options(具名Options)

具名Options同样可以采用类似的编程方式。如果需要根据指定的名称对Options进行初始化,那么调用方法时就需要指定一个Action<TOptions,String>类型的委托对象,该委托对象的第二个参数表示Options的名称。在如下所示的代码片段中,我们通过类似的方式设置了两个用户(“foo”和“bar”)的信息,然后利用IOptionsSnapshot<Profile>服务将它们分别提取出来。该程序运行后会在控制台上产生图6-2所示的输出结果。(S606)

using App;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;var optionsAccessor = new ServiceCollection().AddOptions().Configure<Profile>("foo", it =>{it.Gender = Gender.Male;it.Age = 18;it.ContactInfo = new ContactInfo{PhoneNo = "123",EmailAddress = "foo@outlook.com"};}).Configure<Profile>("bar", it =>{it.Gender = Gender.Female;it.Age = 25;it.ContactInfo = new ContactInfo{PhoneNo = "456",EmailAddress = "bar@outlook.com"};}).BuildServiceProvider().GetRequiredService<IOptionsSnapshot<Profile>>();Print(optionsAccessor.Get("foo"));
Print(optionsAccessor.Get("bar"));static void Print(Profile profile)
{Console.WriteLine($"Gender: {profile.Gender}");Console.WriteLine($"Age: {profile.Age}");Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");
};

[607]针对依赖服务的Options设置

在很多情况下我们需要针对某个依赖的服务动态地初始化Options的设置,比较典型的就是根据当前的承载环境(开发、预发和产品)对Options做动态设置。我们在第5章“配置选项(上)”中演示了一系列针对日期/时间输出格式的配置,下面沿用这个场景演示如何根据当前的承载环境设置对应的Options。我们将DateTimeFormatOptions的定义进行简化,只保留如下所示的表示日期和时间格式的两个属性。

public class DateTimeFormatOptions
{public string DatePattern { get; set; }public string TimePattern { get; set; }public override string ToString() => $"Date: {DatePattern}; Time: {TimePattern}";
}

我们利用配置来提供当前的承载环境,具体采用的是基于命令行参数的配置源。.NET的服务承载系统通过IHostEnvironment接口表示承载环境,具体实现类型为HostingEnvironment(该类型定义在“Microsoft.Extensions.Hosting”NuGet包中,我们需要添加针对这个包的引用)。如下面的演示程序所示,我们创建了一个ServiceCollection对象,并添加了针对IHostEnvironment接口的服务注册,具体提供的是一个根据环境名称创建的HostingEnvironment对象。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Options;var environment = new ConfigurationBuilder().AddCommandLine(args).Build()["env"];var services = new ServiceCollection();
services
.AddSingleton<IHostEnvironment>(new HostingEnvironment { EnvironmentName = environment }).AddOptions<DateTimeFormatOptions>().Configure<IHostEnvironment>((options, env) => {if (env.IsDevelopment()){options.DatePattern = "dddd, MMMM d, yyyy";options.TimePattern = "M/d/yyyy";}else{options.DatePattern = "M/d/yyyy";options.TimePattern = "h:mm tt";}});var options = services.BuildServiceProvider().GetRequiredService<IOptions<DateTimeFormatOptions>>().Value;Console.WriteLine(options);

我们调用了ServiceCollection对象的AddOptions<DateTimeFormatOptions>扩展方法完成了针对Options框架核心服务的注册,并利用返回的OptionsBuilder<DateTimeFormatOptions>对象对作为配置选项的DateTimeFormatOptions作相应设置。具体来说,我们调用了它的Configure<IHostEnvironment>方法利用提供的Action<DateTimeFormatOptions, IHostEnvironment>委托针对开发环境和非开发环境设置了不同的日期与时间格式。我们采用命令行的方式启动这个应用程序,并利用命令行参数设置不同的环境名称,就可以在控制台上看到图5所示的针对DateTimeFormatOptions的不同设置。

9c35ca6cdb710ce170df374d508bdc2f.png
图5 针对承载环境的Options设置

[608]验证Options的有效性

配置选项是整个应用的全局设置,如果对它进行了错误的设置可能会造成很严重的后果,所以最好能够在使用之前进行有效性验证。接下来我们将上面的程序做了如下改动,从而演示如何对设置的日期和时间格式进行验证。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Globalization;var config = new ConfigurationBuilder().AddCommandLine(args).Build();
var datePattern = config["date"];
var timePattern = config["time"];var services = new ServiceCollection();
services.AddOptions<DateTimeFormatOptions>().Configure(options =>{options.DatePattern = datePattern;options.TimePattern = timePattern;}).Validate(options => Validate(options.DatePattern) && Validate(options.TimePattern), "Invalid Date or Time pattern.");try
{var options = services.BuildServiceProvider().GetRequiredService<IOptions<DateTimeFormatOptions>>().Value;Console.WriteLine(options);
}
catch (OptionsValidationException ex)
{Console.WriteLine(ex.Message);
}static bool Validate(string format)
{var time = new DateTime(1981, 8, 24, 2, 2, 2);var formatted = time.ToString(format);return DateTimeOffset.TryParseExact(formatted, format,null, DateTimeStyles.None, out var value) && (value.Date == time.Date || value.TimeOfDay == time.TimeOfDay);
}

上述演示实例借助配置系统以命令行的形式提供了日期和时间格式化字符串。在创建了OptionsBuilder<DateTimeFormatOptions>对象并对DateTimeFormatOptions做了相应设置之后,我们调用Validate<DateTimeFormatOptions>方法利用提供的Func<DateTimeFormatOptions,bool>委托对最终的设置进行验证。运行该程序并按照图6所示的方式指定不同的格式化字符串,系统会根据我们指定的规则来验证其有效性。

e7b25d6c52c26954be7131ff97b853cc.png
图6 验证Options的有效性

《ASP.NET Core 6框架揭秘》实例演示[01]:编程初体验
《ASP.NET Core 6框架揭秘》实例演示[02]:各种形式的API开发
《ASP.NET Core 6框架揭秘》实例演示[03]:Dapr初体验
《ASP.NET Core 6框架揭秘》实例演示[04]:自定义依赖注入框架
《ASP.NET Core 6框架揭秘》实例演示[05]:依赖注入基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[06]:依赖注入框架设计细节
《ASP.NET Core 6框架揭秘》实例演示[07]:文件系统
《ASP.NET Core 6框架揭秘》实例演示[08]:配置的基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[09]:将配置绑定为对象

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

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

相关文章

实现仿简书选取内容生成分享图片效果

前几天脑子里忽然闪过简书的图片分享效果&#xff0c;感觉很简洁也很漂亮&#xff0c;想着能不能用自己方式实现一下呢&#xff0c;于是今天就有了这篇文章。好了&#xff0c;先看下效果图吧&#xff1a; 项目地址: https://github.com/zhangke301... 欢迎star、issues~ 实现这…

千万级可观测数据采集器--iLogtail代码完整开源

2022年6月29日&#xff0c;阿里云iLogtail开源后迎来首次重大更新&#xff0c;正式发布完整功能的iLogtail社区版。本次更新开源全部C核心代码&#xff0c;该版本在内核能力上首次对齐企业版&#xff0c;开发者可以构建出与企业版性能相当的iLogtail云原生可观测性数据采集器。…

Java8新特性--CompletableFuture

并发与并行 Java 5并发库主要关注于异步任务的处理&#xff0c;它采用了这样一种模式&#xff0c;producer线程创建任务并且利用阻塞队列将其传递给任务的consumer。这种模型在Java 7和8中进一步发展&#xff0c;并且开始支持另外一种风格的任务执行&#xff0c;那就是将任务的…

用 MAUI 在Windows 和 Linux 绘制 PPT 图表

我在做一个图表工具软件&#xff0c;这个软件使用 MAUI 开发。我的需求是图表的内容需要和 PPT 的图表对接&#xff0c;需要用到 OpenXML 解析 PPT 内容&#xff0c;读取到 PPT 图表元素的内容&#xff0c;接着使用 MAUI 渲染层绘制图表元素。图表工具软件需要在 Windows 平台和…

聊聊接口性能优化的11个小技巧

前言 接口性能优化对于从事后端开发的同学来说&#xff0c;肯定再熟悉不过了&#xff0c;因为它是一个跟开发语言无关的公共问题。 该问题说简单也简单&#xff0c;说复杂也复杂。 有时候&#xff0c;只需加个索引就能解决问题。 有时候&#xff0c;需要做代码重构。 有时…

Java中ArrayList,LinkedList,Vector三者的异同点及其使用场景和ArrayList的一些常用方法

相同点&#xff1a;三者存储的都是有序&#xff0c;可重复的数据。 异&#xff1a; ①&#xff1a;ArrayList底层存储类型是Object数组&#xff0c;而LinkedList底层是双向链表 ②&#xff1a;ArrayList和Vector调用创建空参构造器创建对象时&#xff0c;默认的size是10&…

第二百四十六节,Bootstrap弹出框和警告框插件

Bootstrap弹出框和警告框插件 学习要点&#xff1a; 1.弹出框 2.警告框 本节课我们主要学习一下 Bootstrap 中的弹出框和警告框插件。 一&#xff0e;弹出框 弹出框即点击一个元素弹出一个包含标题和内容的容器。 基本用法 注意&#xff1a;必须在js结合popover()方法使用 da…

Intellij IDEA2017 的控制台里不识别maven命令问题处理

2019独角兽企业重金招聘Python工程师标准>>> cmd里运行 mvn -v可以显示出maven的版本信息&#xff0c;可是在IDEA的控制台里却提示不识别maven命令&#xff0c;此情况以管理员的身份运行IDEA即可。 转载于:https://my.oschina.net/u/2364025/blog/1788797

使用IDEA 提交代码到svn

2019独角兽企业重金招聘Python工程师标准>>> 新手第一次使用教程&#xff1a; 一、安装svn TortoiseSVN是个客户端&#xff0c;需要安装VisualSVN服务端。 二、IDEA配置&#xff08;Ctrl alt S&#xff09; 需要配置服务端svn.exe文件。 三、上传代码 svn路径&…

如何在 BackgroundService 获取 ASP.NET Core 启动地址

前言上次&#xff0c;我们介绍了《如何获取 ASP.NET Core 启动地址》。但是&#xff0c;如果要在 BackgroundService 中获取启动地址可不那么容易&#xff0c;因为 BackgroundService 在 app 启动前就开始执行了:var builder WebApplication.CreateBuilder(args); builder.Ser…

016-Spring Boot JDBC

一、数据源装配 通过查看代码可知&#xff0c;默认已装配了数据源和JdbcTemplate System.out.println(context.getBean(DataSource.class)); System.out.println(context.getBean(JdbcTemplate.class)); 1.1、环境搭建 主要是pom引用&#xff1a;spring-boot-starter-jdbc、增加…

分库分表和 NewSQL 到底怎么选?

文章来源&#xff1a;【公众号&#xff1a;CoderW】 目录 背景 分表 分库 分库分表的成本 NewSQL NewSQL 平滑接入方案 NewSQL 真的有那么好吗&#xff1f; NewSQL 的应用 分库分表和 NewSQL 到底怎么选&#xff1f; 背景 曾几何时&#xff0c;“并发高就分库&#xff…

jQuery/javascript实现简单网页计算器

1 <html>2 <head>3 <meta charset"utf-8">4 <title>jQuery实现</title>5 <script src"jquery.js"></script>6 7 <style type"text/css">8 table{background-color:pink;width:300px;height…

雷军招人反被3句话问懵:当我在面试牛人的时候,牛人也在面试我

来 源&#xff5c;环球人力资源智库&#xff08;GHRlib&#xff09; 作 者&#xff5c;Black “你做过手机吗&#xff1f;” “没做过。” “你认识中移动老总王建宙吗&#xff1f;” “不认识。” “你认识富士康老板郭台铭吗&#xff1f;” “我认识他&#xff0c;他不认识我…

C# 11 中的 required members

C# 11 中的 required membersIntro在 C# 11 中引入了一个新的特性 —— Required Members&#xff0c;引入了一个新的 required 关键词&#xff0c;可以用来表示字段或者属性在类型初始化的时候必须要进行初始化&#xff0c;这一特性也进一步的改进了可空引用类型的用法。Sampl…

互联网大佬简史:马云/雷军/罗永浩/刘强东...

燃财经&#xff08;ID:rancaijing&#xff09;原创 作者 | 杜枫 编辑 | 魏佳中国互联网的发展&#xff0c;是一部由大佬撑起的奋斗史&#xff0c;也是一部由大佬主演的打脸史。和传统行业不同&#xff0c;互联网行业日新月异&#xff0c;从业者趋于年轻。马云唱起了摇滚&#x…

Windows 11 新版 22621.575 和 22622.575 推送:照片、URL、文件资源管理器

面向 Beta 频道的 Windows 预览体验成员&#xff0c;微软推送了 Windows 11 预览版 Build 22621.575 和 22622.575。 目前 Beta 频道 Windows 11 预览版分为两组进行测试&#xff0c;通过两组 Windows 预览体验成员的使用数据和反馈&#xff0c;以更好的测试新功能的可靠性。Wi…

linux mysql5.6 安装

2019独角兽企业重金招聘Python工程师标准>>> 1、gcc yum install gcc gcc-c ncurses-devel perl 2、cmake安装 wget http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz tar -xzvf cmake-2.8.10.2.tar.gz cd cmake-2.8.10.2 ./bootstrap ; make ; make insta…

Python常用的12个GUI框架

Graphical User Interface&#xff0c;简称 GUI&#xff0c;又称图形化用户接口&#xff0c;所谓的GUI编程&#xff0c;指的是用户不需要输入代码指令&#xff0c;只通过图形界面的交互就可以操作软件功能。 1.Tkinter 一个轻量级的跨平台图形用户界面&#xff08;GUI&#xff…

PHP下操作Linux消息队列完成进程间通信的方法

2019独角兽企业重金招聘Python工程师标准>>> 来源:http://www.jb51.net/article/24353.htm 关于Linux系统进程通信的概念及实现可查看&#xff1a;http://www.ibm.com/developerworks/cn/linux/l-ipc/   关于Linux系统消息队列的概念及实现可查看&#xff1a;htt…