A:调整整体README
This commit is contained in:
1 parent
26a2661625
commit
433e206d45
5 files changed
+166
-265
No files matched your search
@@ -1,287 +1,81 @@
|
||||
# sharp-basic
|
||||
# Sharp Basic
|
||||
|
||||
C# 公共基础核心(模块化、多项目可复用)
|
||||
一个基于 .NET 8 的现代化工业自动化与企业级应用开发套件,采用模块化架构设计,提供了从底层通信、基础设施到上层 UI 的完整解决方案。
|
||||
|
||||
sharp-basic 旨在将常用基础能力拆分为可独立复用的模块(库项目),帮助业务项目即取即用,减少样板代码与重复实现。
|
||||
## 🏗️ 架构概览
|
||||
|
||||
当前仓库采用多项目结构,包含 Common、Config 等核心模块,以及 Logging、EventBus、NetClient/NetServer 等待实现或扩展的模块,同时提供 WPF/Client 示例项目便于演示与调试。
|
||||
本项目包含以下核心模块,旨在实现高内聚、低耦合的开发体验:
|
||||
|
||||
## 功能概览
|
||||
### 🔌 核心基础设施
|
||||
- **[Sharp.Common](Common/README.md)**: 基础工具库。提供轻量级 DI 容器 (`Global`)、函数式结果模式 (`Result<T>`)、以及丰富的扩展方法(String, DateTime, Encryption, Reflection)。
|
||||
- **[Sharp.Config](Config/README.md)**: 强大的配置管理。支持 JSON/XML/INI/TOML 多源聚合、强类型绑定和数据注解验证。
|
||||
- **[Sharp.Log](Log/README.md)**: 统一日志门面。基于 Serilog 封装,支持一行代码接入,提供结构化日志和文件滚动策略。
|
||||
- **[Sharp.EventBus](EventBus/README.md)**: (开发中) 轻量级事件总线。
|
||||
|
||||
- 通用工具(Sharp.Common)
|
||||
- 结果类型 Result/Result<T>:统一成功/失败表达、错误码/异常、Map/Bind/LINQ 风格链式操作、同步/异步 Try 包装
|
||||
- 轻量服务容器 Global:线程安全注册与获取(Singleton/Transient)、实例变更通知(订阅发布)、批量管理
|
||||
- 实用工具与扩展:JSON 序列化选项与 IPAddress 转换、端口探测、管理员权限检测、字符串解析等
|
||||
- 配置中心(Sharp.Config)
|
||||
- 统一构建器:支持 Json/Ini/Xml/Toml 多源叠加、可选/必选、基路径/文件提供器、环境变量、热重载
|
||||
- 强类型绑定与验证:DataAnnotations 递归验证、TryGet/异常详情、对象保存与读取(多格式序列化)
|
||||
- 扩展钩子:自定义 IConfigurationBuilder 管道(ConfigureBuilderHook)
|
||||
- 日志(Sharp.Log)【规划中】
|
||||
- 基于 Microsoft.Extensions.Logging 的统一抽象,适配 Console/File/Serilog/NLog,结构化日志与上下文
|
||||
- 事件总线(Sharp.EventBus)【规划中】
|
||||
- 进程内发布订阅、异步/顺序/粘性事件,支持 WPF 弱订阅;外部 Broker(Redis/RabbitMQ/Kafka)适配扩展
|
||||
- 网络(Sharp.NetClient / Sharp.NetServer)【规划中】
|
||||
- HttpClientFactory 封装、重试熔断超时、请求签名、统一 Result<T> 返回;最小 API Host 与中间件
|
||||
- 桌面工具(Sharp.Wpf / Sharp.Client)
|
||||
- WPF 示例/样板,用于演示配置热更、事件订阅、日志等集成的参考
|
||||
### 💾 数据与存储
|
||||
- **[Sharp.Data](Data/README.md)**: 极简数据访问层。基于 EF Core,提供开箱即用的仓储模式、自动审计(创建人/时间)、软删除和分页支持。
|
||||
- **[Sharp.Repository](Repository/README.md)**: 数据库集成模块。通过配置切换 SQL Server/MySQL/PostgreSQL/Sqlite,支持自动迁移。
|
||||
|
||||
## 目录结构
|
||||
### 🌐 网络与通信
|
||||
- **[Sharp.NetServer](NetServer/README.md)**: 轻量级 Web 框架。基于 `HttpListener`,提供类似 ASP.NET Core 的 Controller/Route/Middleware 开发体验,适合嵌入式或桌面应用内嵌 Web 服务。
|
||||
- **[Sharp.NetClient](NetClient/README.md)**: 强类型 HTTP 客户端。集成 Polly 重试策略、Bearer Token 认证和 multipart 上传。
|
||||
- **[Sharp.Ports](Ports/README.md)**: 串口通信库。提供串口发现、连接管理、数据监听和诊断工具。
|
||||
|
||||
```
|
||||
src/Sharp/
|
||||
Common/ # 通用能力(Result、Global、Utils、扩展)
|
||||
Config/ # 配置集中管理与序列化
|
||||
Log/ # 日志抽象(规划中)
|
||||
EventBus/ # 事件总线(规划中)
|
||||
NetClient/ # 客户端网络(规划中)
|
||||
NetServer/ # 服务端网络(规划中)
|
||||
Wpf/ # WPF 示例
|
||||
Client/ # WPF 示例(另一个样板)
|
||||
test/
|
||||
Common.Test/ # Common 核心用例(MSTest)
|
||||
### 🏭 工业互联 (IIoT)
|
||||
- **[Sharp.Plc](Plc/README.md)**: PLC 通信抽象层。定义了统一的读写、订阅接口。
|
||||
- **[Sharp.Plc.Siemens](Plc.Siemens/README.md)**: 西门子驱动实现。支持 S7-200/300/400/1200/1500,内置智能批量读取优化算法(自动合并相邻地址请求)。
|
||||
|
||||
### 🖥️ 桌面应用 UI
|
||||
- **[Sharp.Wpf](Wpf/README.md)**: WPF 基础库。提供 MVVM 基类、全局异常捕获、常用转换器、对话框服务和行为扩展。
|
||||
- **[Sharp.Client](Client/README.md)**: 综合客户端示例。集成 PLC 监控、串口调试和用户登录功能的工业控制台应用。
|
||||
- **[Sharp.Server](Server/README.md)**: 服务器控制台示例。展示了如何在一个 WPF 应用中托管 NetServer Web 服务,并提供可视化管理界面。
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
- .NET 8.0 SDK
|
||||
- Visual Studio 2022 或 VS Code
|
||||
|
||||
### 构建项目
|
||||
```bash
|
||||
# 还原依赖
|
||||
dotnet restore
|
||||
|
||||
# 编译解决方案
|
||||
dotnet build Sharp.sln -c Debug
|
||||
```
|
||||
|
||||
目标框架:.NET 8(net8.0 / net8.0-windows)。
|
||||
### 运行示例
|
||||
|
||||
## 快速开始
|
||||
|
||||
> 当前未发布 NuGet 包,可在解决方案内直接引用相关项目,或将模块作为子模块引入你的解决方案。
|
||||
|
||||
### 使用 Result 统一返回
|
||||
|
||||
```csharp
|
||||
using Common.Results;
|
||||
|
||||
Result<int> r1 = Result.Success(123);
|
||||
Result r2 = Result.Failure("something wrong");
|
||||
|
||||
// Try 模式(同步/异步)
|
||||
var r3 = Result.Try(() => int.Parse("456"));
|
||||
var r4 = await Result.TryAsync(async () => { await Task.Delay(10); return 789; });
|
||||
|
||||
// Map/Bind 链式管道
|
||||
var r5 = r3.Map(x => x * 2).Where(v => v > 500, "too small");
|
||||
|
||||
// 提取值(或异常)
|
||||
int value = r5.GetOrDefault(0); // 成功取值,否则默认
|
||||
int must = r5.GetOrThrow(); // 失败抛出异常
|
||||
**1. 运行 WPF 客户端:**
|
||||
```bash
|
||||
dotnet run --project Client/Client.csproj
|
||||
```
|
||||
|
||||
### 使用 Global 注册与订阅
|
||||
|
||||
```csharp
|
||||
using Common;
|
||||
|
||||
// 注册单例(实例/工厂),与瞬态
|
||||
Global.RegisterSingleton(new AppConfig { AppName = "MyApp", Version = "1.0" });
|
||||
Global.RegisterSingleton(() => new CacheService());
|
||||
Global.RegisterTransient(() => new RequestContext());
|
||||
|
||||
// 订阅服务变更
|
||||
Global.Subscribe<AppConfig>(e =>
|
||||
{
|
||||
Console.WriteLine($"Config changed: {e.OldValue?.Version} -> {e.NewValue.Version}");
|
||||
});
|
||||
|
||||
// 更新将触发订阅回调
|
||||
Global.Update(new AppConfig { AppName = "MyApp", Version = "1.1" });
|
||||
|
||||
// 获取
|
||||
var config = Global.Get<AppConfig>();
|
||||
**2. 运行嵌入式 Web 服务器:**
|
||||
```bash
|
||||
dotnet run --project Server/Server.csproj
|
||||
```
|
||||
|
||||
示例类型(自定义)
|
||||
|
||||
```csharp
|
||||
public sealed class AppConfig { public string AppName { get; set; } = ""; public string Version { get; set; } = ""; }
|
||||
public sealed class CacheService { }
|
||||
public sealed class RequestContext { public string RequestId { get; set; } = Guid.NewGuid().ToString(); }
|
||||
**3. 运行 NetServer 纯控制台示例:**
|
||||
```bash
|
||||
dotnet run --project NetServer/Examples
|
||||
```
|
||||
|
||||
### 配置管理(多源叠加/强类型/验证/保存)
|
||||
## 🧪 测试
|
||||
|
||||
```csharp
|
||||
using Sharp.Config;
|
||||
|
||||
// 1) 从多个文件构建(支持 Json/Ini/Xml/Toml),并启用环境变量与热重载
|
||||
var cfg = ConfigBuilder.Build(opt =>
|
||||
{
|
||||
opt.BasePath = AppContext.BaseDirectory;
|
||||
opt.ReloadOnChange = true;
|
||||
opt.IncludeEnvironmentVariables = true;
|
||||
opt.EnvironmentPrefix = "MYAPP_"; // 可选
|
||||
opt.Sources.Add(new ConfigSource { Path = "appsettings.json", Format = ConfigFormat.Json, Optional = false });
|
||||
opt.Sources.Add(new ConfigSource { Path = "appsettings.Development.json", Format = ConfigFormat.Json, Optional = true });
|
||||
});
|
||||
|
||||
// 2) 强类型绑定 + 验证(DataAnnotations)
|
||||
var options = cfg.Get<MyOptions>(validate: true);
|
||||
|
||||
// 3) 热重载回调
|
||||
using var _ = cfg.OnReload(() => Console.WriteLine("Configuration reloaded"));
|
||||
|
||||
// 4) 保存/读取对象(跨格式序列化)
|
||||
cfg.Save(options, path: "out/appsettings.toml", format: ConfigFormat.Toml);
|
||||
var reloaded = cfg.LoadFromFile<MyOptions>("out/appsettings.toml", ConfigFormat.Toml);
|
||||
|
||||
public sealed class MyOptions
|
||||
{
|
||||
[System.ComponentModel.DataAnnotations.Required]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int Port { get; set; } = 8080;
|
||||
}
|
||||
```
|
||||
|
||||
示例 appsettings.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "demo",
|
||||
"port": 5000
|
||||
}
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
- 测试框架:MSTest(并行执行)。
|
||||
- 运行:
|
||||
项目包含完整的单元测试,位于解决方案同级的 `test` 目录中。
|
||||
|
||||
```bash
|
||||
dotnet test
|
||||
# 运行所有测试
|
||||
dotnet test Sharp.sln
|
||||
```
|
||||
|
||||
核心用例位于 `test/Common.Test`,覆盖 Result 与 Global 的主要功能与边界场景。
|
||||
## 🤝 贡献指南
|
||||
欢迎提交 Pull Request 或 Issue。请确保:
|
||||
1. 代码风格符合 .NET 规范。
|
||||
2. 新增功能包含对应的单元测试。
|
||||
3. 提交信息简洁明了。
|
||||
|
||||
## WPF 工具包(Sharp.Wpf)
|
||||
|
||||
提供即插即用的 WPF 常用基础能力:
|
||||
|
||||
- 全局异常处理:统一捕获 UI/后台/未观察任务异常,支持弹窗与自定义日志
|
||||
- MVVM 基础:`ViewModelBase`(INPC/IsBusy)、`RelayCommand`、`AsyncCommand`
|
||||
- Dispatcher 封装:`IDispatcher`/`DispatcherService`,提供 UI 线程调用统一入口
|
||||
- 对话与文件对话:`IDialogService`、`IFileDialogService`
|
||||
- 常用转换器:Bool/NullOrEmpty 可见性、布尔取反、枚举描述
|
||||
- 附加行为:EventToCommand(事件转 ICommand)
|
||||
- 窗口工具:`WindowPlacement` 窗口位置/大小/状态持久化
|
||||
|
||||
### 引用
|
||||
|
||||
在你的 WPF 项目中添加项目引用:
|
||||
|
||||
```xml
|
||||
<!-- Client/Client.csproj -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||
<!-- 或通过 NuGet: <PackageReference Include="Sharp.Wpf" Version="x.y.z" /> -->
|
||||
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### 全局异常处理(App 启动)
|
||||
|
||||
```csharp
|
||||
// App.Startup.cs
|
||||
using System.Windows;
|
||||
using Sharp.Wpf.Exceptions;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
new UiExceptionHandler().Register(new ExceptionHandlingOptions
|
||||
{
|
||||
ShowDialog = true,
|
||||
DialogTitle = "应用程序错误"
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### XAML 集成(窗口位置、行为、转换器)
|
||||
|
||||
```xml
|
||||
<Window x:Class="Client.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:b="clr-namespace:Sharp.Wpf.Behaviors;assembly=Sharp.Wpf"
|
||||
xmlns:w="clr-namespace:Sharp.Wpf.Windowing;assembly=Sharp.Wpf"
|
||||
xmlns:conv="clr-namespace:Sharp.Wpf.Converters;assembly=Sharp.Wpf"
|
||||
Title="MainWindow"
|
||||
w:WindowPlacement.RememberPlacement="True"
|
||||
w:WindowPlacement.PlacementId="MainWindow">
|
||||
<Window.Resources>
|
||||
<conv:BoolToVisibilityConverter x:Key="Bool2Vis" />
|
||||
</Window.Resources>
|
||||
<StackPanel Margin="16">
|
||||
<Button Content="点击弹窗"
|
||||
b:EventToCommand.EventName="Click"
|
||||
b:EventToCommand.Command="{Binding ShowDialogCommand}" />
|
||||
<Button Content="触发异常"
|
||||
b:EventToCommand.EventName="Click"
|
||||
b:EventToCommand.Command="{Binding ThrowCommand}" />
|
||||
<TextBlock Text="正在处理..."
|
||||
Visibility="{Binding IsBusy, Converter={StaticResource Bool2Vis}}"/>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
```
|
||||
|
||||
### ViewModel 与命令
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Services;
|
||||
|
||||
public sealed class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialog = new DialogService();
|
||||
|
||||
public string Greeting { get; set; } = "欢迎使用 Sharp.Wpf";
|
||||
public IAsyncCommand ShowDialogCommand { get; }
|
||||
public RelayCommand ThrowCommand { get; }
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
ShowDialogCommand = new AsyncCommand(async () =>
|
||||
{
|
||||
try { IsBusy = true; await Task.Delay(300); await _dialog.ShowInfoAsync("Hello!"); }
|
||||
finally { IsBusy = false; }
|
||||
});
|
||||
|
||||
ThrowCommand = new RelayCommand(() => throw new InvalidOperationException("演示未处理异常"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 文件对话
|
||||
|
||||
```csharp
|
||||
var files = await new FileDialogService().OpenFilesAsync("Images|*.png;*.jpg");
|
||||
var folder = await new FileDialogService().SelectFolderAsync();
|
||||
```
|
||||
|
||||
## 路线图(Roadmap)
|
||||
|
||||
- P0(最优先)
|
||||
- Logging 抽象与 Serilog 适配;EventBus 进程内 Typed 事件;Config 对接 IOptions 与变更广播
|
||||
- P1
|
||||
- Net.Client:HttpClientFactory 封装 + Result<T> 模式;WPF Toolkit:MVVM/事件聚合/通知与主题
|
||||
- P2
|
||||
- 可靠性(Polly)、OpenTelemetry、外部 Broker 适配(Redis/RabbitMQ/Kafka)、敏感字段加密
|
||||
- P3
|
||||
- 文档/示例完善、CI/CD(自动打包发布)、更多单测与性能基准
|
||||
|
||||
## 贡献与规范
|
||||
|
||||
- 欢迎通过 Issue/PR 参与贡献。
|
||||
- 代码风格:.NET 8、可空启用、简化 `using`,命名空间建议统一为 `Sharp.*`(Common 模块后续将统一为 `Sharp.Common`)。
|
||||
- 变更需保持向后兼容(次版本),破坏式变更请在主版本发布并注明迁移指南。
|
||||
|
||||
## 许可
|
||||
|
||||
本项目采用 GNU AGPLv3 许可证,详见根目录 `LICENSE`。
|
||||
|
||||
如需在私有环境或商业场景中使用且不希望开源网络服务端改动代码,请联系作者洽谈其他授权方式。
|
||||
## 📄 许可证
|
||||
MIT License
|
||||
Reference in new issue
Block a user