2024年8月

public static Mock<ILogger> GetILogger()
{
    // 创建一个模拟的 ILogger 实例
    var loggerMock = new Mock<Microsoft.Extensions.Logging.ILogger>();
    // 配置模拟对象,在调用 Log 方法时打印日志消息到控制台
    loggerMock.Setup(x => x.Log(
        It.IsAny<LogLevel>(),
        It.IsAny<EventId>(),
        It.IsAny<object>(),
        It.IsAny<Exception>(),
        It.IsAny<Func<object, Exception, string>>()
    )).Callback<LogLevel, EventId, object, Exception, Func<object, Exception, string>>((logLevel, eventId, state, exception, formatter) =>
    {
        Console.WriteLine($"[{logLevel}] - {formatter(state, exception)}");
    });

    return loggerMock;
}

/// <summary>
/// 将DateTime转换为UTC时间字符串
/// </summary>
/// <param name="dateTime"></param>
/// <param name="ignoreTimeZone">
///     默认忽略时区影响,仅将DateTime中的"年月日时分秒"拼接为对应的Utc字符串;<br/>
///     如果考虑时区,会将DateTime转换为Utc时间后再转换为对应Utc字符串
/// </param>
/// <returns></returns>
public static string ToUtcTimeString(this DateTime dateTime, bool ignoreTimeZone = true)
{
    if (!ignoreTimeZone)
    {
        return dateTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ");
    }

    return dateTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
}

本文中的时区通过TimeZoneInfo.FindSystemTimeZoneById获取的,其中的参数Id是windows下特有的,因此其Id在不同的操作系统下不一定存在,为标准起见可采用IANA时区库,可以使用三方库NodaTime来处理时间,本文暂不对NodaTime讲解。


  • DateTime 不储存时区信息
  • DateTimeOffset 可以储存时区信息

代码:

/// <summary>
/// 转换字符串为DateTime
/// </summary>
/// <param name="sourceTimeString">
///     源时间字符串
/// </param>
/// <param name="sourceFormat">
///     源时间的字符串格式<br/>
///     1. 例如: "yyyyMMddHHmmss","yyyy-MM-dd HH:mm:ss","yyyy-MM-ddTHH:mm:ssZ"<br/>
///     2. 本参数仅用于解析源时间时指定的字符串格式,不会对字符串中是否存在时区信息进行处理和转换<br/>
/// </param>
/// <param name="sourceIsUtc">
///     源时间是否UTC时间
/// </param>
/// <param name="sourceTimeZoneInfoId">
///     时区信息ID<br/>
///     1. 如果sourceIsUtc为true,该参数可以为空<br/>
///     2. 可通过<seealso cref="GetAllTimeZoneId"/>获取所有时区查看需要的时区ID<br/>
///     3. 中国时区ID为: "China Standard Time"
/// </param>
/// <param name="isToUtc">
///     是否转换为UTC时间<br/>
///     1. 如果为false,则转换为本地时间(本地时间取决于操作系统时区)<br/>
///     2. 如果需要转换为其他时区的时间,请使"isToUtc"为"true"后得到UTC时间再自行进行转换
/// </param>
/// <returns></returns>
public static DateTime? FormatDate(
    this string sourceTimeString,
    string sourceFormat,
    bool sourceIsUtc,
    string sourceTimeZoneInfoId,
    bool isToUtc
    )
{
    // 将指定时间转换为无时区的时间
    var sourceProvider = CultureInfo.InvariantCulture;// 不指定区域文化
    var srouceStyle = DateTimeStyles.None;// 不指定时区 System.DateTimeKind.Unspecified

    // 这个时间是默认建立在操作系统所在地区的时区上,因此不能直接使用,需要进一步转换为带时区和偏移的时间
    var sourceLocalTime = DateTime.ParseExact(sourceTimeString, sourceFormat, sourceProvider, srouceStyle);

    // 获取源时间对应的国家时区信息,以便找到时间偏移量
    var sourceTimeZoneInfo = GetTimeZoneOffsetHours(sourceIsUtc, sourceTimeZoneInfoId);
    var sourceOffsetHours = sourceTimeZoneInfo.BaseUtcOffset.Hours;

    // 将无时区时间转换为带时区的时间
    var dateTimeOffset = new DateTimeOffset(sourceLocalTime, TimeSpan.FromHours(sourceOffsetHours));

    if (isToUtc)
    {
        return dateTimeOffset.UtcDateTime;
    }
    else
    {
        return dateTimeOffset.LocalDateTime;
    }
}

/// <summary>
/// 根据时区ID获取时区信息
/// </summary>
/// <param name="isUtc"></param>
/// <param name="timeZoneInfoId"></param>
/// <returns></returns>
private static TimeZoneInfo GetTimeZoneOffsetHours(bool isUtc, string timeZoneInfoId = "China Standard Time")
{
    if (isUtc)
    {
        return TimeZoneInfo.Utc;
    }
    else
    {
        return TimeZoneInfo.FindSystemTimeZoneById(timeZoneInfoId);
    }
}

/// <summary>
/// 获取系统中所有可用的时区信息
/// </summary>
/// <returns></returns>
public static List<Tuple<int, string>> GetAllTimeZoneId()
{
    // 获取系统中所有可用的时区信息
    var timeZones = TimeZoneInfo.GetSystemTimeZones();
    return timeZones
        .Select(t => new Tuple<int, string>(t.BaseUtcOffset.Hours, t.Id))
        .OrderBy(t => t.Item1)
        .ThenBy(t => t.Item2)
        .ToList();
}

单元测试:

{
    // 获取当前系统所在时区的时间
    var dateTime = DateTime.Now;
    // 转换为日本时间
    TimeZoneInfo tokyoTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
    DateTime tokyoTime = TimeZoneInfo.ConvertTime(dateTime, tokyoTimeZone);
}

{
    var timeZoneIds = StringExtension.GetAllTimeZoneId();

    // 假设日本时间为 2024-08-16 21:00:00,对应utc时间为 12:00,对应北京时间(本机)为 20:00
    var inputTimeString = "20240816210000";
    string sourceFormat = "yyyyMMddHHmmss";
    bool sourceIsUtc = false;
    string sourceTimeZoneInfoId = "Tokyo Standard Time";
    bool isToUtc = true;
    var outDateTime = inputTimeString.FormatDate(sourceFormat, sourceIsUtc, sourceTimeZoneInfoId, isToUtc);

    var beijingTime = DateTime.Parse("2024-08-16 20:00:00");
    Assert.AreEqual(
        outDateTime,
        beijingTime.ToUniversalTime()
    );
}

insert into

= "INSERT INTO new_sa_original(" & ARRAYTOTEXT(B1:T1,0) & ") VALUES"

value:

="("&TEXTJOIN(",",FALSE,LAMBDA(x,IF(x="NULL",x,"'"&x&"'"))(B2:T2))&"),"
(可自动判断NULL值来决定是否添加引号)

你结婚了吗?

你快乐吗?

罗翔老师说:

三年后,你结婚了,下班回家看着你不爱的人,烦得吃不下饭,床都不愿意上,话也不想说,你会不会后悔曾经太听家人的话?

三年后,你结婚了,下班回家看着你当初奋不顾身要嫁的人,对你很是厌烦,只知道打游戏,话都懒得和你说,再看看满屋狼藉,吵闹的孩子,没洗的碗筷,他甚至连家都不愿意回,会不会后悔曾经不听家人的话?

- 阅读剩余部分 -

命名空间和类名一样,如何指定使用的类

给对应的程序集设置别名,或者通过右键属性指定别名

<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.0">
            <Aliases>MsLog</Aliases>
        </PackageReference>
    </ItemGroup>
</Project>

使用

extern alias MsLog; // 指定要使用的别名
using ILogger = MsLog::Microsoft.Extensions.Logging.ILogger; // 引入别名中的class
using LoggerExtensions = MsLog::Microsoft.Extensions.Logging.LoggerExtensions;

namespace xxxxx.xxxxxx.Tests
{
    [TestClass()]
    public class CreateTests
    {
        [TestMethod()]
        public async Task RunTestAsync()
        {
            // 使用对应的class
            LoggerExtensions.LogInformation("")
            Assert.Fail();
        }
    }
}

AutoMapper 依赖注入 Profile

安装 AutoMapper.Extensions.Microsoft.DependencyInjection 包

依赖注入

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
public class Startup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // AutoMapper
        builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly(), typeof([其他需要反射的程序集的某一个class引用]).Assembly);
    }
}
using AutoMapper;
public class XXXXXXXServiceBus{
    private readonly IMapper mapper;
    public XXXXXXXServiceBus(IMapper _mapper)
    {
        mapper = _mapper;
    }
}

配置Profile

using AutoMapper;
public class AccountMappingProfile : Profile
{
    public AccountMappingProfile()
    {
        CreateMap<AccountTable, MqAccountModel>();
    }
}

使用

var mqAccountModel = mapper.Map<MqAccountModel>(accountTable);

参考:

关于Azure Function找不到dll程序集的问题

大致现象

静态编译没问题,运行报错内容如下:

Could not load file or assembly 'Microsoft.xxxx.xxxx.xxxx, Version=6.15.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.

或:

无法加载文件或程序集 'Microsoft.xxxx.xxxx.xxxx, Version=6.15.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'。系统找不到指定的文件。

- 阅读剩余部分 -