当前位置: 首页 > news >正文

02020213 .NET Core重难点知识13-配置日志邮件服务案例、DI读取、DI与扩展方法、VS配置项目环境变量

02020213 .NET Core重难点知识13-配置&日志&邮件服务案例、DI读取、DI与扩展方法、VS配置项目环境变量

1. 配置服务、日志服务、邮件发送器服务案例

1.1 需求
  • 有配置服务、日志服务,然后再开发一个邮件发送服务器服务。
    • 可以通过配置服务从文件、环境变量、数据库等地方读取配置。
    • 可以通过日志服务来讲程序运行过程中的日志信息写入文件、控制台、数据库等。
  • 本案例开发了自己的日志、配置等接口,这只是在揭示原理,.NET有现成的,后面讲。
1.2 实现
图片链接丢失
  • (截图更正)创建三个.NET Core类库项目和一个控制台项目。
    • ConfigServices → 配置服务的项目。
    • LogServices → 日志服务的项目。
    • MailServices → 邮件发送器的项目。
    • MailServicesConsole → 是.NET Core控制台项目。
1.2 项目目录结构
ConsoleAppMailSender → 解决方案名称
├── ConsoleAppMailSender → .NET Core 5.0控制台项目。因为要发邮件,依赖项需要添加MailServices项目的引用。
|   ├── Program.cs → 包含Main方法,程序的入口。
├── LogServices → .NET Standard 2.1类库项目
|   ├── ILogProvider.cs → 接口
|   └── ConsoleLogProvider.cs => 实现类
├── MailServices → .NET Standard 2.1类库项目。因为要写日志和读取配置,依赖项需要添加LogServices项目和ConfigServices项目的引用。
|   ├── IMailService.cs → 接口
|   └── MailServiceImpl → 实现类
├── ConfigServices → .NET Standard 2.1类库项目。
|   ├── IConfigService.cs → 接口
└── └── EnvVarConfigService → 实现类说明:
1. 创建.NET Standard类库是为了便于.NET Core或者.NET Framework项目都可以使用。
2. 该项目为演示作用,并没有真实发送邮件。如果需要发送邮件,可以用MailKit来实现,这里面有提供发送邮件的方法。
3. NuGet包版本为:Install-Package Microsoft.Extensions.DependencyInjection 9.0.8。
1.3 IDE项目资源
图片链接丢失
1.4 源码
// EnvVarConfigService.cs
using System;namespace ConfigServices
{public class EnvVarConfigService : IConfigService{public string GetValue(string name){return Environment.GetEnvironmentVariable(name);}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// IConfigService.cs
namespace ConfigServices
{/// <summary>/// 如果配置找不到,就返回null/// </summary>/// <param name="name"></param>/// <returns></returns>public interface IConfigService{public string GetValue(string name); // 读取名字为name的配置文件}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs
using System;
using ConfigServices;
using LogServices;
using MailServices;
using Microsoft.Extensions.DependencyInjection;namespace ConsoleAppMailSender
{class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<IConfigService, EnvVarConfigService>();services.AddScoped<IMailService, MailServiceImpl>();services.AddScoped<ILogProvider, ConsoleLogProvider>();using( var sp = services.BuildServiceProvider()){// 第一个根上的对象只能用ServiceLocatorvar mailService = sp.GetRequiredService<IMailService>();mailService.Send("Hello", "Trump", "懂王你好");}Console.ReadLine();}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// ConsoleLogProvider.cs
using System;namespace LogServices
{public class ConsoleLogProvider : ILogProvider{public void LogError(string msg){Console.WriteLine($"Error: {msg}");}public void LogInfo(string msg){Console.WriteLine($"Info: {msg}");}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// ILogProvider.cs
namespace LogServices
{public interface ILogProvider{public void LogError(string msg); // 记录错误信息public void LogInfo(string msg); // 记录普通信息}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// IMailService.cs
using System;
using System.Collections.Generic;
using System.Text;namespace MailServices
{public interface IMailService{public void Send(string title, string to, string name); // 发送邮件}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MailServiceImpl.cs
using ConfigServices;
using LogServices;
using System;namespace MailServices
{public class MailServiceImpl : IMailService{private readonly ILogProvider log;private readonly IConfigService config;public MailServiceImpl(ILogProvider log, IConfigService config){this.log = log;this.config = config;}public void Send(string title, string to, string name){this.log.LogInfo("准备发送邮件");string smtpServer = this.config.GetValue("SmtpServer");string userName = this.config.GetValue("UserName");string password = this.config.GetValue("Password");Console.WriteLine($"邮件服务器地址{smtpServer},{userName},{password}");Console.WriteLine($"发送成功:{title},{to}");this.log.LogInfo("邮件发送成功");}}
}控制台输出:
Info: 准备发送邮件
邮件服务器地址,Administrator,
发送成功:Hello,Trump
Info: 邮件发送成功
1.5 项目总结
  • 本例其实和02020212中2.2中是一样的,只是多了项目这个分层。
  • 注意各个项目之间的引用关系。

2. 从配置文件读取数据

2.1 项目目录结构
ConsoleAppMailSender → 解决方案名称
├── ConsoleAppMailSender → .NET Core 5.0控制台项目。因为要发邮件,依赖项需要添加MailServices项目的引用。
|   ├── Program.cs → (※修改※)包含Main方法,程序的入口。
|   └── mail.ini → (※新增※)配置文件。通过文本文件创建,名称后缀带上.ini。文件名 → 右键 → 属性 → 高级 → 复制到输出目录 → 如果较新则复制
├── LogServices → .NET Standard 2.1类库项目
|   ├── ILogProvider.cs → 接口
|   └── ConsoleLogProvider.cs => 实现类
├── MailServices → .NET Standard 2.1类库项目。因为要写日志和读取配置,依赖项需要添加LogServices项目和ConfigServices项目的引用。
|   ├── IMailService.cs → 接口
|   └── MailServiceImpl → 实现类
├── ConfigServices → .NET Standard 2.1类库项目。
|   ├── IConfigService.cs → 接口
|   ├── IniFileConfigService.cs → (※新增※)实现类
└── └── EnvVarConfigService → 实现类说明:
1. 创建.NET Standard类库是为了便于.NET Core或者.NET Framework项目都可以使用。
2. 该项目为演示作用,并没有真实发送邮件。如果需要发送邮件,可以用MailKit来实现,这里面有提供发送邮件的方法。
3. NuGet包版本为:Install-Package Microsoft.Extensions.DependencyInjection 9.0.8。
2.2 源码
  • 在1.4基础上,源代码作如下修改,用于从配置文件获取数据。
// 新增配置文件:mail.ini
SmtpServer=abc.mail.com
UserName=admin
Password=123456
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 新增实现类:IniFileConfigService.cs
using System.IO;
using System.Linq;namespace ConfigServices
{public class IniFileConfigService : IConfigService{public string FilePath { get; set; } // 配置文件路径public string GetValue(string name){var kv = File.ReadAllLines(FilePath).Select(s => s.Split("=")).Select(strs => new { Name = strs[0], Value = strs[1] }).SingleOrDefault(kv => kv.Name == name);if(kv != null){return kv.Value;}else{return null;}}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改:Program.cs
using System;
using ConfigServices;
using LogServices;
using MailServices;
using Microsoft.Extensions.DependencyInjection;namespace ConsoleAppMailSender
{class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();// AddScoped方法的重载形式,用到了回调的知识,用new的目的是为了给属性赋值。services.AddScoped(typeof(IConfigService), s => new IniFileConfigService { FilePath = "mail.ini" });services.AddScoped<IMailService, MailServiceImpl>(); // @1services.AddScoped<ILogProvider, ConsoleLogProvider>(); // @2using( var sp = services.BuildServiceProvider()){// 第一个根上的对象只能用ServiceLocatorvar mailService = sp.GetRequiredService<IMailService>();mailService.Send("Hello", "Trump", "懂王你好");}Console.ReadLine();}}
}控制台输出:
Info: 准备发送邮件
邮件服务器地址abc.mail.com,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功说明:
1. 在@1处和@2处,引入了如何让调用者不关注实现类名称(不显式指定实现类)的问题。
2. 在@1处如果有services.AddConsoleLog方法,即提供一个AddXX方法,能够自动.出来(自动提示出来),此时会更加简单。此时,可以用扩展方法来解决。

3. 使用扩展方法来简化案例

3.1 思路
图片链接丢失
  • 给Microsoft.Extensions.DependencyInjection 9.0.8包里面IServiceCollection接口增加一个扩展方法。
    • 并将这个扩展方法直接生成在Microsoft.Extensions.DependencyInjection这个命名空间底下。
3.2 项目目录结构
ConsoleAppMailSender → 解决方案名称
├── ConsoleAppMailSender → .NET Core 5.0控制台项目。因为要发邮件,依赖项需要添加MailServices项目的引用。
|   ├── Program.cs →(※修改※) 包含Main方法,程序的入口。
|   └── mail.ini → 配置文件。通过文本文件创建,名称后缀带上.ini。文件名 → 右键 → 属性 → 高级 → 复制到输出目录 → 如果较新则复制
├── LogServices → .NET Standard 2.1类库项目。(※新增包的引用※)
|   ├── ILogProvider.cs → 接口
|   ├── ConsoleLogExtensions.cs → (※新增※)实现类
|   └── ConsoleLogProvider.cs → (※修改※)实现类。修改类的访问权限为private,此时调用者已经不关心类名了,在类的内部访问即可。
├── MailServices → .NET Standard 2.1类库项目。因为要写日志和读取配置,依赖项需要添加LogServices项目和ConfigServices项目的引用。
|   ├── IMailService.cs → 接口
|   └── MailServiceImpl → 实现类
├── ConfigServices → .NET Standard 2.1类库项目。
|   ├── IConfigService.cs → 接口
|   ├── IniFileConfigService.cs → 实现类
└── └── EnvVarConfigService → 实现类
3.3 源码
  • 在2.2基础上,用用扩展方法来实现。
// 新增实现类:ConsoleLogExtensions
using LogServices;// 此时命名空间要将默认的LogServices改为Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection
{public static class ConsoleLogExtensions // 扩展方法的类必须是静态的{// 此时LogServices项目也要添加Microsoft.Extensions.DependencyInjection 9.0.8包的引用。public static void AddConsoleLog(this IServiceCollection services) {services.AddScoped<ILogProvider, ConsoleLogProvider>();}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改:ConsoleLogProvider类的权限
using System;namespace LogServices
{class ConsoleLogProvider : ILogProvider // 此时可以将public取消,调用者已经不需要知道类的名称了。{public void LogError(string msg){Console.WriteLine($"Error: {msg}");}public void LogInfo(string msg){Console.WriteLine($"Info: {msg}");}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改:Program.cs
using System;
using ConfigServices;
using MailServices;
using Microsoft.Extensions.DependencyInjection; // 此命名空间里面包含ConsoleLogExtensions这个类namespace ConsoleAppMailSender
{class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();// AddScoped方法的重载形式,用到了回调的知识,用new的目的是为了给属性赋值。services.AddScoped(typeof(IConfigService), s => new IniFileConfigService { FilePath = "mail.ini" });services.AddScoped<IMailService, MailServiceImpl>();services.AddConsoleLog(); // @1 扩展方法using( var sp = services.BuildServiceProvider()){// 第一个根上的对象只能用ServiceLocatorvar mailService = sp.GetRequiredService<IMailService>();mailService.Send("Hello", "Trump", "懂王你好");}Console.ReadLine();}}
}控制台输出:
Info: 准备发送邮件
邮件服务器地址abc.mail.com,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功说明:为了让@1处AddConsoleLog方法能直接.出来,用户不关心实现类和接口的名字。我们给IServiceCollection接口增加了一个AddConsoleLog方法,用来完成注册过程。

4. 用扩展方法继续简化

4.1 项目目录结构
ConsoleAppMailSender → 解决方案名称
├── ConsoleAppMailSender → .NET Core 5.0控制台项目。因为要发邮件,依赖项需要添加MailServices项目的引用。
|   ├── Program.cs → 包含Main方法,程序的入口。(※修改※)
|   └── mail.ini → 配置文件。通过文本文件创建,名称后缀带上.ini。文件名 → 右键 → 属性 → 高级 → 复制到输出目录 → 如果较新则复制
├── LogServices → .NET Standard 2.1类库项目。
|   ├── ILogProvider.cs → 接口
|   ├── ConsoleLogExtensions.cs → 实现类
|   └── ConsoleLogProvider.cs → (※修改※)实现类。修改类的访问权限为private,此时调用者已经不关心类名了,在类的内部访问即可。
├── MailServices → .NET Standard 2.1类库项目。因为要写日志和读取配置,依赖项需要添加LogServices项目和ConfigServices项目的引用。
|   ├── IMailService.cs → 接口
|   └── MailServiceImpl → 实现类
├── ConfigServices → .NET Standard 2.1类库项目。(※新增包的引用※)
|   ├── IConfigService.cs → 接口
|   ├── IniFileConfigService.cs → 实现类
|   ├── IniFileConfigExtensions → (※新增※)
└── └── EnvVarConfigService → 实现类。(※修改访问权限为private※),本例不修改,任然保持public权限用作对比。
4.2 源码
  • 在3.3基础上,继续使用扩展方法。
// 新增实现类:IniFileConfigExtensions
using ConfigServices;namespace Microsoft.Extensions.DependencyInjection
{public static class IniFileConfigExtensions{public static void AddIniFileConfig(this IServiceCollection services, string filePath){services.AddScoped(typeof(IConfigService), s => new IniFileConfigService { FilePath = filePath });}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改:Program.cs
using System;
using MailServices;
using Microsoft.Extensions.DependencyInjection;namespace ConsoleAppMailSender
{class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddIniFileConfig("mail.ini"); // 扩展方法services.AddScoped<IMailService, MailServiceImpl>();services.AddConsoleLog();using( var sp = services.BuildServiceProvider()){// 第一个根上的对象只能用ServiceLocatorvar mailService = sp.GetRequiredService<IMailService>();mailService.Send("Hello", "Trump", "懂王你好");}Console.ReadLine();}}
}控制台输出:
Info: 准备发送邮件
邮件服务器地址abc.mail.com,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功

5. 可覆盖的配置读取器

图片链接丢失
5.1 项目目录结构
ConsoleAppMailSender → 解决方案名称
├── ConsoleAppMailSender → .NET Core 5.0控制台项目。因为要发邮件,依赖项需要添加MailServices项目的引用。
|   ├── Program.cs → 包含Main方法,程序的入口。(※修改※)
|   └── mail.ini → 配置文件。通过文本文件创建,名称后缀带上.ini。文件名 → 右键 → 属性 → 高级 → 复制到输出目录 → 如果较新则复制
├── LogServices → .NET Standard 2.1类库项目。
|   ├── ILogProvider.cs → 接口
|   ├── ConsoleLogExtensions.cs → 实现类
|   └── ConsoleLogProvider.cs → (※修改※)实现类。修改类的访问权限为private,此时调用者已经不关心类名了,在类的内部访问即可。
├── MailServices → .NET Standard 2.1类库项目。因为要写日志和读取配置,依赖项需要添加LogServices项目和ConfigServices项目的引用。
|   ├── IMailService.cs → 接口
|   └── MailServiceImpl.cs → 实现类。(※修改※)
├── ConfigServices → .NET Standard 2.1类库项目。
|   ├── IConfigService.cs → 接口
|   ├── IniFileConfigService.cs → 实现类
|   ├── IniFileConfigExtensions.cs → 实现类
|   ├── IConfigReader.cs → (※新增※)接口。通常放在一个新的类库里面,这里为了演示方便继续放在这个类库当中。
|   ├── LayeredConfigReader.cs → (※新增※)实现类。通常放在一个新的类库里面,这里为了演示方便继续放在这个类库当中。
|   ├── LayeredConfigExtension.cs → (※新增※)扩展方法类,便于使用。
└── └── EnvVarConfigService → 实现类。
5.2 源码
  • 在4.2基础上,实现可覆盖读取器。
// 新增:IConfigReader.cs
using System;
using System.Collections.Generic;
using System.Text;namespace ConfigServices
{public interface IConfigReader{public string GetValue(string name);}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 新增:LayeredConfigReader.cs
using System;
using System.Collections.Generic;
using System.Text;namespace ConfigServices
{class LayeredConfigReader : IConfigReader{private readonly IEnumerable<IConfigService> services;public LayeredConfigReader(IEnumerable<IConfigService> services){this.services = services;}public string GetValue(string name){string value = null; // 如果配置找不到为nullforeach (var service in services) // 按照顺序读{string newValue = service.GetValue(name);if(newValue != null){value = newValue; // 最后一个不为null的值,就是最终值。}}return value;}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 新增:LayeredConfigExtension.cs
using ConfigServices;
using System;
using System.Collections.Generic;
using System.Text;namespace Microsoft.Extensions.DependencyInjection
{public static class LayeredConfigExtension{public static void AddLayeredConfig(this IServiceCollection services){services.AddScoped<IConfigReader, LayeredConfigReader>();}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改:MailServiceImpl.cs
using ConfigServices;
using LogServices;
using System;namespace MailServices
{public class MailServiceImpl : IMailService{private readonly ILogProvider log;private readonly IConfigReader config; // 现在逻辑不是简单的从某一个里面读public MailServiceImpl(ILogProvider log, IConfigReader config){this.log = log;this.config = config;}public void Send(string title, string to, string name){this.log.LogInfo("准备发送邮件");string smtpServer = this.config.GetValue("SmtpServer");string userName = this.config.GetValue("UserName");string password = this.config.GetValue("Password");Console.WriteLine($"邮件服务器地址{smtpServer},{userName},{password}");Console.WriteLine($"发送成功:{title},{to}");this.log.LogInfo("邮件发送成功");}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改:Program.cs
using System;
using ConfigServices;
using MailServices;
using Microsoft.Extensions.DependencyInjection;namespace ConsoleAppMailSender
{class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<IConfigService, EnvVarConfigService>(); // 这里读取环境变量,可以自行修改。这里保留了课堂用法。services.AddIniFileConfig("mail.ini");services.AddLayeredConfig();services.AddScoped<IMailService, MailServiceImpl>();services.AddConsoleLog();using( var sp = services.BuildServiceProvider()){// 第一个根上的对象只能用ServiceLocatorvar mailService = sp.GetRequiredService<IMailService>();mailService.Send("Hello", "Trump", "懂王你好");}Console.ReadLine();}}
}控制台输出:
Info: 准备发送邮件
邮件服务器地址abc.mail.com,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 修改配置文件内容
SmtpServer=abc.mail.com → 修改为 → SmtpServer2=abc.mail.com
UserName=admin
Password=123456控制台输出:
Info: 准备发送邮件
邮件服务器地址,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
总结:配置文件有用配置文件的,否则用环境变量的。

6. 配置环境变量

图片链接丢失
  • VS配置环境变量:项目名称 → 属性 → 调试 → 名称&值
// 做了如上配置之后,通过修改ini文件的SmtpServer名称,可以显式不同的输出结果。
控制台输出1:
Info: 准备发送邮件
邮件服务器地址qinway.QW.com,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功控制台输出2:
Info: 准备发送邮件
邮件服务器地址abc.mail.com,admin,123456
发送成功:Hello,Trump
Info: 邮件发送成功

7. 案例总结

  • 关注于接口,而不是关注于实现,各个服务可以更弱耦合的协同工作。
  • 编写代码的时候,我们甚至都不知道具体的服务是什么。
  • 第三方DI容器:Autofac等。
    • Autofac优点 → 支持属性注入、基于名字注入、基于约定注入等。
    • 老师评价基于构造函数注入比基于属性注入更好,目前学习过程中体会不出来。
  • 重点:如无必要,勿增实体。
    • 不必要的情况下,不要把东西变得很复杂。
    • 与其狂拽吊炸天,实际上满足项目前提下尽量不要扩展一些新东西,保持架构简洁。提高可维护性和稳定性。

8. 个人评价

  • 目前一边看一边写,算是看完了也写完了,目前还是一脸懵逼,“如看”。老师讲的可以,个人理解不够,后续会再看一遍。课程继续往后推进,不纠结。

结尾

书籍:ASP.NET Core技术内幕与项目实战

视频:https://www.bilibili.com/video/BV1pK41137He

著:杨中科

ISBN:978-7-115-58657-5

版次:第1版

发行:人民邮电出版社

※敬请购买正版书籍,侵删请联系85863947@qq.com※

※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

http://www.hskmm.com/?act=detail&tid=1169

相关文章:

  • GJOI 模拟赛题记录声明
  • Ubuntu 卸载 Firefox 浏览器
  • 录无法修改OneDrive文件的解决方法
  • 量子机器学习入门:三种数据编码方法对比与应用
  • 向量数据库
  • UGNX2506下载和安装教程包含激活教程步骤(超详细保姆级图文UGNX安装步骤)
  • ansible剧本
  • 2111111
  • Ubuntu 安装 Google Chrome
  • Cannot call Open vSwitch: ovsdb-server.service is not running
  • uniapp插件开发
  • 【模板】平面最近点对
  • npx playwright install chromium 安装失败,如何离线安装
  • Power BI制作指标达成跟踪器
  • 一个基于 .NET 开源、轻便的 Windows 优化工具,适用于 Win7 - Win11 最新版的优化!
  • 两种求快速幂的方法
  • 杂题20250909-
  • LLM2
  • 第01周 预习、实验与作业:绪论与Java基本语法
  • 第一周作业1
  • NSSCTF强网杯GameMaster
  • ARC199 做题记
  • 深入理解Redis高并发分布式锁
  • 计算机硬件基础认知
  • 测试一下别人的
  • 9.10 NOIP模拟改题记录
  • 文件上传及提权
  • 删除字符串中的所有相邻重复项
  • 测试一下iframe3
  • 测试一下iframe