.NET 程序设计语言
LDAPS
[Route("QueryUser")]
[HttpGet]
public AdUser QueryAd()
{
AdUser adUser = new AdUser();
string username = "administrator@wlync.com";
string password = "Win@2012R2";
string searchFilter = "(sAMAccountName=luo.sd)";
LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier("192.168.10.21", 636, false, false));
connection.AuthType = AuthType.Basic;
connection.SessionOptions.ProtocolVersion = 3;
connection.SessionOptions.SecureSocketLayer = true;
// 如果设置VerifyServerCertificate 处理证书将会收到: The LDAP server is unavailable
//connection.SessionOptions.VerifyServerCertificate += (sender, e) => true;
connection.Credential = new NetworkCredential(username, password);
try
{
connection.Bind();
Console.WriteLine("Connected to AD successfully.");
SearchRequest searchRequest = new SearchRequest("OU=Exrecall,OU=lsd,DC=wlync,DC=com", searchFilter, SearchScope.Subtree);
SearchResponse searchResponse = (SearchResponse)connection.SendRequest(searchRequest);
foreach (SearchResultEntry entry in searchResponse.Entries)
{
adUser.Name = entry.DistinguishedName;
adUser.SamAccountName = entry.Attributes["samAccountName"][0].ToString();
}
}
catch (LdapException ex)
{
adUser.Description = ex.Message;
}
finally
{
connection.Dispose();
}
return adUser;
}
}
Cron 表达式
Cron 表达式用于指定定期任务的调度时间,一般包含以下五段(标准 cron 表达式)或六段(带年字段的扩展 cron 表达式)。每段的具体含义如下:
标准 Cron 表达式(五段)
- 分钟(0-59):表示任务执行的分钟。
- 小时(0-23):表示任务执行的小时(0表示午夜)。
- 日期(1-31):表示任务执行的日期。
- 月份(1-12 或者 JAN-DEC):表示任务执行的月份。
- 周几(0-7 或者 SUN-SAT):表示任务执行的星期几(0 和 7 都表示星期日)。
示例:
0 12 15 * * ? 表示每天中午12点执行任务,但仅在每月的15日。
扩展 Cron 表达式(六段,带年字段)
在一些系统中,cron 表达式会增加第六段,表示年份(通常是为了兼容更多的调度需求)。
- 分钟(0-59)
- 小时(0-23)
- 日期(1-31)
- 月份(1-12 或 JAN-DEC)
- 周几(0-7 或 SUN-SAT)
- 年(可选,用于指定特定年份)
示例:
0 0 1 1 ? 2024 表示在2024年1月1日的00:00执行任务。
缺省值的表示
\*(星号):表示任何值。例如,*在分钟字段中表示“每一分钟”。?:表示“不指定”,通常用于日期和星期几字段中,当你只关心其中一个时,另一个使用?表示忽略。比如在日期和星期几都指定时,你只能用一个来进行明确设置,另一个用?来表示不关心。-(连字符):表示一个范围。例如,1-5表示从1到5。,(逗号):表示列举值。例如,1,3,5表示第1、第3和第5个时间点。/(斜杠):表示步进值。例如,0/15在分钟字段中表示每15分钟。L:表示“最后”,通常用于日期字段。例如,L在日期字段中表示“每月最后一天”。W:表示最近的工作日。例如,15W表示接近15号的最近的工作日。
示例解析
0 0 12 \* \* ?:每天中午12点执行任务。0 15 10 ? \* 2:每周二上午10:15执行任务。0 0 1 1 \* ?:每年1月1日午夜执行任务。
了解这些基本的 cron 表达式规则可以帮助你更好地配置和管理定期任务。
Quartz Scheduler 的Cron
Quartz Scheduler 使用的 cron 表达式与传统的 Unix/Linux cron 表达式有所不同,尤其是在字段的数量和含义上。Quartz 的 cron 表达式提供了更多的灵活性和功能,允许更复杂的调度需求。下面是 Quartz 与标准 cron 表达式的主要区别:
标准 Cron 表达式(五段)
标准的 cron 表达式通常有以下五个字段:
- 分钟(0-59)
- 小时(0-23)
- 日期(1-31)
- 月份(1-12 或 JAN-DEC)
- 周几(0-7 或 SUN-SAT)
Quartz 的 Cron 表达式(六段或七段)
Quartz Scheduler 扩展了标准 cron 表达式,使用了六个或七个字段:
- 秒(0-59)
- 分钟(0-59)
- 小时(0-23)
- 日期(1-31)
- 月份(1-12 或 JAN-DEC)
- 周几(0-7 或 SUN-SAT)
- 年份(可选)
Quartz 的 Cron 表达式示例
六段式 Cron 表达式
0 0 23 * * ?
- 秒(0):每分钟的第0秒。
- 分钟(0):每小时的第0分钟。
- 小时(23):每天的第23小时,即晚上11点。
- 日期(*):每个月的每一天。
- 月份(*):每年的每个月。
- 周几(?):不指定具体的星期几。
七段式 Cron 表达式
0 0 23 1 1 ? 2024
- 秒(0):每分钟的第0秒。
- 分钟(0):每小时的第0分钟。
- 小时(23):每天的第23小时,即晚上11点。
- 日期(1):1月1日。
- 月份(1):1月。
- 周几(?):不指定具体的星期几。
- 年份(2024):指定2024年。
主要区别
- 秒字段:Quartz 表达式包括秒字段,而标准的 cron 表达式没有。
- 年份字段:Quartz 的表达式可以包含年份字段,但标准的 cron 表达式没有。
- 问号
?:在 Quartz 表达式中,?用于“周几”字段,以避免与“日期”字段的冲突,表示不指定具体的星期几。这在标准 cron 表达式中没有。
总结
Quartz Scheduler 确实使用了一个不完全与传统标准相同的 cron 表达式语法。虽然它在基本概念上与标准 cron 表达式相似,但在功能和灵活性上提供了额外的扩展。这使得 Quartz 更适合处理复杂的调度需求。如果你在使用 Quartz Scheduler,理解这些扩展和功能是很重要的。如果有其他问题或需要进一步的帮助,请随时告诉我!
JWT的应用
创建 JWT Token 示例
首先,我们创建一个简单的方法来生成 JWT Token。在实际应用中,你需要根据具体的需求来配置 JWT 的内容和签名密钥。
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace JwtDemo
{
//nuget Microsoft.IdentityModel.Tokens
// nuget Microsoft.IdentityModel.JsonWebTokens
public class AccessToken
{
public string Token { get; set; }
public DateTime Expiration { get; set; }
public bool Success { get; set; }
}
public class UserModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class JwtService
{
private string secretKey = "72Q+x7x088CyZx7445LsQz2CUHInC4piAIrDevGBdVs="; // 替换为你的实际密钥
//string keyStr = "72Q+x7x088CyZx7445LsQz2CUHInC4piAIrDevGBdVs=";
//public JwtService(IConfiguration configuration)
//{
// keyStr = configuration.GetValue<string>("SecretKey");
//}
public string GenerateJwtToken(string username)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(secretKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = DateTime.UtcNow.AddHours(1), // Token 有效期为1小时
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
public bool ValidateJwtToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(secretKey);
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
try
{
SecurityToken validatedToken;
ClaimsPrincipal principal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
return true; // Token is valid
}
catch (Exception ex)
{
Console.WriteLine($"Token validation failed: {ex.Message}");
return false; // Token validation failed
}
}
public async Task<AccessToken> GenerateTokenAsync(UserModel userModel)
{
if (userModel.UserName == "admin" && userModel.Password == "P@ssw0rd!")
{
var claims = new[]
{
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Sub,"luo_sd@hotmail.com"),
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.UniqueName,"admin")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature
);
var token = new JwtSecurityToken("localhost", "localhost", claims,
expires: DateTime.Now.AddMinutes(10), signingCredentials: creds);
return new AccessToken
{
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo,
Success = true
};
}
return new AccessToken
{
Success = false
};
}
public bool ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
try
{
SecurityToken validatedToken;
var claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
return true; // Token is valid
}
catch (Exception ex)
{
Console.WriteLine($"Token validation failed: {ex.Message}");
return false; // Token validation failed
}
}
private TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters
{
ValidateIssuer = true, // 此处设置为true时需要指定一个Issuer
ValidIssuer= "localhost",
ValidateAudience = false, // You may need to change audience validation according to your requirements
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
};
}
}
}
验证 JWT Token 示例
namespace JwtDemo
{
internal class Program
{
static void Main(string[] args)
{
string keyStr = "72Q+x7x088CyZx7445LsQz2CUHInC4piAIrDevGBdVs=";
var userModel = new UserModel() { UserName="admin",Password= "P@ssw0rd!" };
var jwtService = new JwtService();
var token = jwtService.GenerateTokenAsync(userModel).GetAwaiter().GetResult();
Console.WriteLine(token.Token);
var isOk = jwtService.ValidateToken(token.Token);
// 生成一个 JWT Token
string username = "alice";
string accessToken = jwtService.GenerateJwtToken(username);
Console.WriteLine($"Generated JWT Token: {token}");
// 验证 JWT Token
bool isValid = jwtService.ValidateJwtToken(accessToken);
Console.WriteLine($"Token validation result: {isValid}");
Console.WriteLine($"{isValid} press any key to exit!");
Console.ReadKey();
}
}
}
EWS开发笔记
EWS-跟踪SOAP请求
输出SOAP请求跟踪信息
public class TraceListener : ITraceListener { public void Trace(string traceType, string traceMessage) { CreateXMLTextFile(traceType, traceMessage); } private void CreateXMLTextFile(string fileName, string traceContent) { try { if (!Directory.Exists(@"..\\TraceOutput")) { Directory.CreateDirectory(@"..\\TraceOutput"); } System.IO.File.WriteAllText(@"..\\TraceOutput\\" + fileName + DateTime.Now.Ticks + ".txt", traceContent); } catch (IOException ex) { Console.WriteLine(ex.Message); } } } public class EwsInvokeWithSoap { public void Init() { ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1); service.Credentials = new NetworkCredential("jq@wlync.com", "1234.com"); service.Url = new Uri("https://mail.wlync.com/ews/exchange.asmx"); try { // 调用 FindItem 方法并获取生成的 SOAP 消息 service.TraceEnabled = true; service.TraceFlags = TraceFlags.All; service.TraceListener = new TraceListener(); var item =Item.Bind(service, "AAMkADBmMGRlYTc4LWQ4YTQtNDlhNC1iM2ZhLTNjYzVkZjdiZDA="); } catch (Exception ex) { // 处理异常 Console.WriteLine(ex.ToString()); } } }
EWS-在SOAP中设置模拟
<soap:Header>
<t:RequestServerVersion Version="Exchange2013" />
<!-- The following causes the request to run as alfred@contoso.com -->
<t:ExchangeImpersonation>
<t:ConnectingSID>
<t:SmtpAddress>alfred@contoso.com</t:SmtpAddress>
</t:ConnectingSID>
</t:ExchangeImpersonation>
</soap:Header>
EWS-OAUTH-21V 获取令牌
var cca = ConfidentialClientApplicationBuilder
.Create(ConfigurationManager.AppSettings["appId"])
.WithClientSecret(ConfigurationManager.AppSettings["clientSecret"])
.WithTenantId(ConfigurationManager.AppSettings["tenantId"])
//仅在 21V 版本中,添加下一行代码
.WithAuthority(AzureCloudInstance.AzureChina, ConfigurationManager.AppSettings["tenantId"])
.Build();
var ewsScopes = new string[] { "https://partner.outlook.cn/.default" };
var authResult = await
cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://partner.outlook.cn/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "user@abc.com");
使用用户自己的身份访问邮件
- 先获取到一个用于访问EWS的accesstoken
// Link to full sample: https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/outlook/85-tokens-and-service-calls/user-callback-token.yaml
Office.context.mailbox.getCallbackTokenAsync(function (result) {
if (result.status !== Office.AsyncResultStatus.Succeeded) {
console.error(`Token retrieval failed with message: ${result.error.message}`);
} else {
console.log(result.value);
}
});
- 使用accesstoken访问EWS
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkFwSWhPRlV2NmsxOXlGN2VQdy1mb1VaTnlhWSJ9.eyJuYW1laWQiOiIxNDVjMzZmNS1mNzA3LTRkMjYtODQ0Ni0wMTFiZTcwNWJiNmJAd2x5bmMuY29tIiwidmVyIjoiRXhjaGFuZ2UuQ2FsbGJhY2suVjEiLCJhcHBjdHhzZW5kZXIiOiJodHRwczovL2F0cC5seW5jbXAuY29tL2luZGV4Lmh0bWxAd2x5bmMuY29tIiwiYXBwY3R4Ijoie1wic210cFwiOlwia2RAd2x5bmMuY29tXCIsXCJzY29wZVwiOlwiUGFyZW50SXRlbUlkOkFBTWtBR1ZrTXpRek5qTXpMV1ZpTURBdE5ERTVZUzFoTVRZNExUWTNNV1F5TkdReU1UTmpPQUJHQUFBQUFBRHBiUjRMNllZNVQ0QlhDaHZ4SXB5ZUJ3Q2FhNEN5R1R5QlNJRkhTbHVhYU5uVEFBQUFBQUVNQUFDYWE0Q3lHVHlCU0lGSFNsdWFhTm5UQUFBUlJRQ3JBQUE9XCJ9IiwiaXNzIjoiMDAwMDAwMDItMDAwMC0wZmYxLWNlMDAtMDAwMDAwMDAwMDAwQHdseW5jLmNvbSIsImF1ZCI6IjAwMDAwMDAyLTAwMDAtMGZmMS1jZTAwLTAwMDAwMDAwMDAwMC9tYWlsLndseW5jLmNvbUB3bHluYy5jb20iLCJleHAiOjE3MDg0OTM3OTcsIm5iZiI6MTcwODQ5MzQ5N30.J8dtGkxEHuwz0UzFi0IEdZpAWaUVgipj-NYFSAGOBNtdxX9sccbBStouiCHekMQTOEFuY7UoEd13QfcH5aG1khJBLchI_LCiFjPuVbfks8QVROSv0_ooG6VixOB4EM7IUyWQU8ay0ehaVldM7CHGqDAOcIJdCnQZrRIlqDwYEpTACEPR9l9XIRruW4jvPnLT1bYAbMAT3rJw1p_r-hjVvap2kz51YfgqhSkDPm3d6k_99gQlhvMVzlqt02CwEcEBXiA6fUgLiou6qk-IbQJ8cgQUEfGryt7DKmg-UdTK2sDPh-2kpcsNp58vQ6xwI0eb5JNvG2VZv4W_rGCQ02K6WQ";
var itemId = "AAMkAGVkMzQzNjMzLWViMDAtNDE5YS1hMTY4LTY3MWQyNGQyMTNjOABGAAAAAADpbR4L6YY5T4BXChvxIpyeBwCaa4CyGTyBSIFHSluaaNnTAAAAAAEMAACaa4CyGTyBSIFHSluaaNnTAAARRQCrAAA=";
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
//var Credentials = new OAuthCredentials(token);
//service.Credentials = Credentials;
service.Url = new Uri("https://mail.wlync.com/ews/exchange.asmx");
service.HttpHeaders.Add("Authorization", "Bearer" +token);
var item = Item.Bind(service, itemId);
完整示例-获取日历信息
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Identity.Client;
using System;
using System.Configuration;
namespace EwsOAuth
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
// Using Microsoft.Identity.Client 4.22.0
var cca = ConfidentialClientApplicationBuilder
.Create(ConfigurationManager.AppSettings["appId"])
.WithClientSecret(ConfigurationManager.AppSettings["clientSecret"])
.WithTenantId(ConfigurationManager.AppSettings["tenantId"])
// Use the next line of code only in a 21V environment
.WithAuthority(AzureCloudInstance.AzureChina, ConfigurationManager.AppSettings["tenantId"])
.Build();
//the permission scope required for EWS access
var ewsScopes = new string[] { "https://partner.outlook.cn/.default" };
try
{
//make the token request
var authResult = await cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://partner.outlook.cn/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "S02@ms10.online");
//Include x-anchormailbox header
ewsClient.HttpHeaders.Add("X-AnchorMailbox", "S02@ms10.online");
// Make an EWS call
DateTime endDate = DateTime.Now;
DateTime startDate = endDate.AddDays(-30);
CalendarFolder calendar = CalendarFolder.Bind(ewsClient, WellKnownFolderName.Calendar, new PropertySet());
CalendarView cView = new CalendarView(startDate, endDate);
cView.PropertySet = new PropertySet(AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End);
FindItemsResults<Appointment> appointments = calendar.FindAppointments(cView);
Console.WriteLine("\nThe first " + " appointments on your calendar from " + startDate.Date.ToShortDateString() +
" to " + endDate.Date.ToShortDateString() + " are: \n");
foreach (Appointment a in appointments)
{
Console.Write("Subject: " + a.Subject.ToString() + " ");
Console.Write("Start: " + a.Start.ToString() + " ");
Console.Write("End: " + a.End.ToString());
Console.WriteLine();
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
}
}
1. NET CORE
1.1 RabbitMQ配置
交换配置
基本
客户端应用程序使用交换和队列,这些交换和队列必须以某种方式相互“声明”和“绑定”,然后才能使用。 还可以使用其他参数自定义队列和交换。此库允许您执行此例程,只需调用向其传递其他参数的方法即可。 您可以配置绑定多个队列的多个交易所。AddExchange
您的代码将如下所示。Startup
public class Startup
{
public static IConfiguration Configuration;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
var clientConfiguration = Configuration.GetSection("RabbitMq");
var exchangeConfiguration = Configuration.GetSection("RabbitMqExchange");
services.AddRabbitMqClient(clientConfiguration)
.AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
}
文件将是这样的。appsettings.json
{
"RabbitMqExchange": {
"Type": "direct",
"Durable": true,
"AutoDelete": false,
"DeadLetterExchange": "default.dlx.exchange",
"RequeueFailedMessages": true,
"RequeueTimeoutMilliseconds": 200,
"RequeueAttempts": 2,
"Arguments": { "key": "value" },
"Queues": [
{
"Name": "MyQueueName",
"Durable": true,
"AutoDelete": false,
"Exclusive": false,
"Arguments": { "key": "value" },
"RoutingKeys": [ "first.routing.key", "second.routing.key" ]
}
]
}
}
此示例中未指定 RabbitMQ 客户端配置部分,有关更多信息,请参阅文档文件。
可以使用属性配置交换:
Type- 交换类型(直接、主题、扇出)。默认值为 。"direct"Durable- 耐用性选项。默认值为 。trueAutoDelete- 交换自动删除的选项。默认值为 。falseArguments- 附加参数的字典。默认值为 。nullRequeueFailedMessages- 一个选项,用于指定通过死信交换以一定延迟重新排队失败消息的行为。默认值为 。文档中介绍了发送延迟消息的机制。trueRequeueTimeoutMilliseconds- 超时(以毫秒为单位),之后消息将重新排队。默认值为 200。RequeueAttempts- 排队服务将尝试重新排队消息的次数。默认值为 2。DeadLetterExchange- 死信交换的值。死信交换名称的默认值为 。"default.dlx.exchange"Queues- 绑定到交易所的队列集合。
队列选项:
Name- 队列名称。Durable- 耐用性选项。默认值为 。trueAutoDelete- 队列自动删除选项。默认值为 。falseExclusive- 独家选项。默认值为 。falseArguments- 附加参数的字典。默认值为 。nullRoutingKeys- 队列“侦听”的路由密钥集合。
考虑到您可以跳过的所有默认值,配置将如下所示。
{
"RabbitMqExchange": {
"Type": "direct",
"Queues": [
{
"Name": "MyQueueName",
"RoutingKeys": [ "first.routing.key", "second.routing.key" ]
}
]
}
}
如果要使用与队列名称匹配的路由键,则可以跳过该选项,队列将通过其名称绑定到交换。"RoutingKeys"
{
"RabbitMqExchange": {
"Type": "direct",
"Queues": [
{
"Name": "queue.name.as.routing.key"
}
]
}
}
生产和消费Exchange
此库中有两种自定义的交换“类型”——生产和****消费。生产交换应该只在生成消息的应用程序中使用。消费交换既针对生产和消费。 为什么甚至有必要进行这样的划分?您可以控制多用途应用程序中一组交换的行为,这些交换使用和使用消息,并避免收到不需要的消息。
要定义交换的自定义“类型”,您必须设置方法接受的参数。如果该值为 ,则应用程序将从绑定到该交换的队列中获取(使用)消息。如果是交换将仅用于生成消息。isConsuming``AddExchange``true``false
services.AddRabbitMqClient(clientConfiguration)
.AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration);
您也可以使用 or 但在引擎盖下,它与使用带有参数的方法相同。AddConsumptionExchange``AddProductionExchange``AddExchange``isConsuming
services.AddRabbitMqClient(clientConfiguration)
.AddConsumptionExchange("ConsumptionExchange", exchangeConfiguration);
// And the other method.
services.AddRabbitMqClient(clientConfiguration)
.AddProductionExchange("ProductionExchange", exchangeConfiguration);
手动配置
您还可以配置交换,手动将 和 类的实例传递给其中一个方法。RabbitMqExchangeOptions``RabbitMqQueueOptions``AddExchange
var exchangeOptions = new RabbitMqExchangeOptions
{
Type = "topic",
Durable = true,
AutoDelete = true,
Arguments = null,
RequeueFailedMessages = true,
DeadLetterExchange = "default.dlx.exchange",
Queues = new List<RabbitMqQueueOptions>
{
new RabbitMqQueueOptions
{
Name = "MyQueueName",
Durable = true,
AutoDelete = false,
Exclusive = false,
Arguments = null,
RoutingKeys = new HashSet<string> { "routing.key" }
}
}
};
services.AddRabbitMqClient(clientConfiguration)
.AddExchange("ExchangeName", isConsuming: true, exchangeOptions);
相同的配置将适用于或重载。AddConsumptionExchange``AddProductionExchange
var exchangeOptions = new RabbitMqExchangeOptions
{
Queues = new List<RabbitMqQueueOptions>
{
new RabbitMqQueueOptions
{
Name = "MyQueueName",
RoutingKeys = new HashSet<string> { "routing.key" }
}
}
};
services.AddRabbitMqClient(clientConfiguration)
.AddProductionExchange("ProductionExchange", exchangeOptions);
// Or the other method.
services.AddRabbitMqClient(clientConfiguration)
.AddConsumptionExchange("ConsumptionExchange", exchangeConfiguration);
1.2 Linux上访问AD报错ldap.so.2
o load shared library 'ldap.so.2' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libldap.so.2: cannot open shared object file: No such file or directory
未找到原因:换一个dll版本不报错了。 Include="LdapForNet" Version="2.7.13"
# 以CentOS为例 执行以下命令 以下的也不能解决问题
yum update
yum install -y --no-install-recommends libldap
2. Exchange Powershell
IIS 上访问 powersehll 报错:拒绝访问,解决办法需要给应用程序池提升权限
## .net core 调用powershell 报错,。<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
.NET6 在IIS上不支持PUT、Delete
在web.config中;找到<system.webServer>元素下添加以下配置:
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
</modules>
重启IIS
.NET Core开发Windows服务
- 使用VS新建辅助服务项目
- 发布应用
dotnetpublish -c Release -r win-x64 --no-self-contained -p:PublishSingleFile=true 编译生成单一 .exe 文件
- 部署服务使用“管理员身份运行”windows powershell命令行
- 使用sc.exe安装windows服务
sc.exe create "ServiceName" binpath="D:\CPULoader\WorkerService1.exe" #安装服务
sc.exe start "ServiceName" #启动服务
sc.exe stop "ServiceName" #停止服务
sc.exe qfailure "ServiceName" #查看服务信息
sc.exe delete "CPU Loader" #删除windows服务
sc.exe create "NETCoreServiceTest" binpath="D:\corewinservice\WorkerService1.exe"
cmd
sc.exe create MessageTrackingLogService binpath = "C:\Program Files\MessageTrackingLogService\ExAtp.MessageTackingLogSyncService.exe" start= auto
示例:
// Program.cs
using WorkerService1;
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "CPU Load Logger";
})
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();
//Worker.cs
using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace WorkerService1
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly string logPath;
private StreamWriter cpuLogger = null!;
public Worker(ILogger<Worker> logger, IConfiguration config)
{
_logger = logger;
logPath = Path.Combine(
config.GetValue<string>("LogPath") ??
System.AppContext.BaseDirectory!,
"cpu.log");
}
// 服務啟動時
public override async Task StartAsync(CancellationToken stoppingToken)
{
cpuLogger = new StreamWriter(logPath, true);
_logger.LogInformation("Service started");
Log("Service started");
// 基底類別 BackgroundService 在 StartAsync() 呼叫 ExecuteAsync、
// 在 StopAsync() 時呼叫 stoppingToken.Cancel() 優雅結束
await base.StartAsync(stoppingToken);
}
// 服務停止時
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Service stopped");
Log("Service stopped");
cpuLogger.Dispose();
cpuLogger = null!;
await base.StopAsync(stoppingToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
ThreadPool.QueueUserWorkItem(
(obj) =>
{
try
{
var cancelToken = (CancellationToken)obj!;
if (!stoppingToken.IsCancellationRequested)
{
Log($"CPU: {GetCpuLoad()}%");
_logger.LogInformation($"Logging CPU load");
}
}
catch (Exception ex)
{
_logger.LogError(ex.ToString());
throw;
}
}, stoppingToken);
await Task.Delay(5000, stoppingToken);
}
}
catch (OperationCanceledException)
{
// When the stopping token is canceled, for example, a call made from services.msc,
// we shouldn't exit with a non-zero exit code. In other words, this is expected...
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
// Terminates this process and returns an exit code to the operating system.
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
// performs one of two scenarios:
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
//
// In order for the Windows Service Management system to leverage configured
// recovery options, we need to terminate the process with a non-zero exit code.
Environment.Exit(1);
}
}
void Log(string message)
{
if (cpuLogger == null) return;
cpuLogger.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {message}");
cpuLogger.Flush();
}
int GetCpuLoad()
{
using (var p = new Process())
{
p.StartInfo.FileName = "wmic.exe";
p.StartInfo.Arguments = "cpu get loadpercentage";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
int load = -1;
var m = System.Text.RegularExpressions.Regex.Match(
p.StandardOutput.ReadToEnd(), @"\d+");
if (m.Success) load = int.Parse(m.Value);
p.WaitForExit();
return load;
}
}
}
}
在服务中启用GRPC
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly RecallWorker _recallWorker;
private MailboxGrpcServiceImplement _mailboxGrpcService;
private Server _server;
public Worker(ILogger<Worker> logger, RecallWorker mailRecallWorker, MailboxGrpcServiceImplement mailboxGrpcServiceImplement)
{
_logger = logger;
_mailboxGrpcService = mailboxGrpcServiceImplement;
_recallWorker = mailRecallWorker;
}
public override Task StartAsync(CancellationToken cancellationToken)
{
string keyPath = Path.Combine(AppContext.BaseDirectory, "cert/exatp.wlync.com.key");
string certificatePath = Path.Combine(AppContext.BaseDirectory, "cert/exatp.wlync.com.crt");
var key = File.ReadAllText(keyPath);
var cert = File.ReadAllText(certificatePath);
var keyPair = new KeyCertificatePair(cert, key);
var credentials = new SslServerCredentials(new List<KeyCertificatePair> { keyPair });
_server = new Server
{
Services = { BindService(_mailboxGrpcService) },
Ports = { new ServerPort("0.0.0.0", 5001, credentials),
new ServerPort("0.0.0.0", 5000, ServerCredentials.Insecure)}
};
_server.Start();
_logger.LogInformation($"gRPC server is runing at http://0.0.0.0:{5000}, https://0.0.0.0:{5001} port");
return base.StartAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
_server?.ShutdownAsync().Wait();
return base.StopAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
if (!WorkerStatus.MailRecallWorkerStatus)
{
_recallWorker.Start();
}
await Task.Delay(1000 * 60, stoppingToken);
}
}
3. 异步编程
3.1 Task
Task myTask = Task.Run(() =>
{
// 异步任务的代码
});
// 异步任务开始后,状态变为Running
if (myTask.Status == TaskStatus.Running)
{
// 异步任务已经开始
}
// 异步任务结束后,状态变为RanToCompletion
myTask.ContinueWith(task =>
{
// 异步任务已经结束
});
3.2 异步等待超时示例
using System.DirectoryServices;
using System.Threading.Tasks;
using System.Threading;
async Task ConnectWithTimeoutAsync(string server, TimeSpan timeout)
{
using var cts = new CancellationTokenSource(timeout);
try
{
var task = Task.Run(() =>
{
var de = new DirectoryEntry("LDAP://" + server);
// 尝试建立连接
var obj = de.NativeObject;
return de;
}, cts.Token);
return await task;
}
catch (OperationCanceledException)
{
// 处理超时
Console.WriteLine("LDAP connection timed out.");
return null;
}
}
// 使用示例
var result = await ConnectWithTimeoutAsync("ldap.example.com", TimeSpan.FromSeconds(10));
if (result == null)
{
// 处理超时情况
}
工具
文件大小友好显示转换ByteConverter
public static object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string size = "0 KB";
if (value != null)
{
double byteCount;
if (value.GetType().Name == "Single")
{
byteCount = double.Parse(value.ToString());
}
else
{
byteCount = (double)value;
}
if (byteCount >= 1073741824)
size = String.Format("{0:##.##}", byteCount / 1073741824) + " GB";
else if (byteCount >= 1048576)
size = String.Format("{0:##.##}", byteCount / 1048576) + " MB";
else if (byteCount >= 1024)
size = String.Format("{0:##.##}", byteCount / 1024) + " KB";
else if (byteCount > 0 && byteCount < 1024)
size = "1 KB"; //Bytes are unimportant ;)
}
return size;
}