Netor.EventHub
1.2.8
dotnet add package Netor.EventHub --version 1.2.8
NuGet\Install-Package Netor.EventHub -Version 1.2.8
<PackageReference Include="Netor.EventHub" Version="1.2.8" />
<PackageVersion Include="Netor.EventHub" Version="1.2.8" />
<PackageReference Include="Netor.EventHub" />
paket add Netor.EventHub --version 1.2.8
#r "nuget: Netor.EventHub, 1.2.8"
#:package Netor.EventHub@1.2.8
#addin nuget:?package=Netor.EventHub&version=1.2.8
#tool nuget:?package=Netor.EventHub&version=1.2.8
Netor.EventHub
项目概述
Netor.EventHub 是一个面向 .NET 的线程安全内存事件中心,支持通过 EventID 字符串常量、EventID<T> 强类型标识、自定义字符串或筛选器订阅事件,并以同步/异步方式发布实现 IEventArgs 的事件参数。通过 AddEventHub DI 扩展即可在任意组件中注入使用。
v1.2.8 新特性:
- 源代码生成器现在直接内嵌在
Netor.EventHub主包的analyzers/dotnet/cs/目录里,安装Netor.EventHub即可自动获得Emit/EmitAsync/On强类型扩展,无需再单独引用Netor.EventHub.SourceGenerator。 - 解决了
Netor.EventHub自身(如DefaultEventID)享受不到代码生成的问题。
v1.2.7 新特性(破坏性变更):
- 命名空间
Netor.EventHub.Interfances修正为Netor.EventHub.Interfaces,显式using该命名空间的代码需要相应替换。 - 类型
SmapleEvent修正为SampleEvent(仅示例项目受影响)。 - 包输出目录
Releaces修正为Releases(仅本仓库构建产物,对消费者无影响)。
v1.2.6 新特性:
- EventStore 引入按
eventid的二级索引,Publish / FindSubscribers / HasSubscribers / Unsubscribe(string) 从 O(N) 降到 O(matches)。 - EventDispatcher 重构:工作线程数 =
MaxConcurrency,移除冗余 Semaphore;并行模式同样走 Channel,QueueCapacity在并行模式下生效。 - EventDispatcher 新增
IAsyncDisposable,避免在 ASP.NET 关停时同步阻塞。 Publisher.Publishfire-and-forget 任务的异常会通过ILogger记录,不再被静默吞没。- 修复
ISubscriberExtensions静态缓存导致跨实例污染的 Bug,改为按ISubscriber实例缓存。 - 修复
Event<T>+=操作符在并发场景下重复订阅的问题,并修复SocpSubscriber在 lock 内调用用户委托的死锁风险。
v1.2.4 新特性:源代码生成器新增 IPublisher 强类型扩展方法 Emit/EmitAsync,编译期自动生成,无需手动指定泛型参数。
v1.1.2/v1.1.3 新特性:新增 ISocpSubscriber 接口,支持订阅自动跟踪和批量清理。
v1.1.1 新特性:新增 IEventDispatcher 事件调度中心,支持并发控制、超时处理和背压控制。
v1.1.0 新特性:支持通过 ISocpHub 创建隔离的命名事件中心实例,实现多租户/多模块的订阅隔离。
核心功能
- 多种订阅方式:支持
EventID字符串常量、EventID<T>强类型标识、自定义字符串或筛选器订阅。 - 异步调度:
PublishAsync使用EventHandler<T>委托处理器,适配网络、数据库等异步逻辑。 - 事件上下文:处理器接收
EventHubContext上下文,包含发布者引用、事件标识、关联 Socp 与时间戳。 - 订阅管理:提供
Unsubscribe、HasSubscribers、FindSubscribers,便于实时查询及清理订阅。 - 可扩展事件标识:支持
EventID常量集、EventID<T>与自定义字符串三种方式按需扩展。 - 订阅隔离:通过
ISocpHub创建命名实例,实现多租户/多模块的订阅隔离。 - 职责分离:
IPublisher负责发布,ISubscriber负责订阅,支持单独注入使用。 - 订阅自动跟踪:DI 解析得到的
ISubscriber默认就是ISocpSubscriber,Dispose时一键清理所有订阅(v1.1.2 新增 / v1.2.6 起 DI 统一注册)。 - 调度中心:
IEventDispatcher基于 Channel 的高性能异步任务队列,工作线程数 =MaxConcurrency,支持背压(v1.1.1 新增 / v1.2.6 重构)。 - 源代码生成器:编译期自动为
EventID<T>子类型生成ISubscriber.On与IPublisher.Emit/EmitAsync强类型扩展方法(v1.2.4 增强)。
核心类型
EventHandler<TArgs>
事件处理器委托,定义了统一的处理器签名:
public delegate Task<bool> EventHandler<in TArgs>(EventHubContext context, TArgs args) where TArgs : IEventArgs;
| 参数 | 说明 |
|---|---|
context |
事件上下文,包含 Publisher、EventID、Socp、Timestamp |
args |
事件参数 |
| 返回值 | true 停止传播,false 继续传播(仅顺序模式生效) |
EventHubContext
事件上下文结构体(readonly record struct),避免堆分配:
public readonly record struct EventHubContext(
IPublisher Publisher, // 发布者实例引用
string? EventID = null, // 当前事件的字符串标识
ISocpHub? Socp = null, // 关联的隔离事件中心
long Timestamp = 0 // UTC Unix 时间戳(秒)
);
EventID(静态字符串常量集)
框架预置的事件标识常量。注意:这是一个 static class,不是 C# 枚举,每个成员都是字符串。
public static class EventID
{
public static string System => "Netor.EventHub.System";
public static string MessageReceived => "Netor.EventHub.MessageReceived";
public static string MessageSent => "Netor.EventHub.MessageSent";
public static string Broadcast => "Netor.EventHub.Broadcast";
public static string Connection => "Netor.EventHub.Connection";
public static string Disconnect => "Netor.EventHub.Disconnect";
public static string Custom => "Netor.EventHub.Custom";
}
| 常量 | 用途说明 |
|---|---|
System |
框架级别或平台通知,如服务启动/停止、配置刷新等。 |
MessageReceived |
接收到新的用户消息或即时通信负载。 |
MessageSent |
发送动作完成后的回调,便于推送发送确认。 |
Broadcast |
面向所有连接/客户端的公告或系统消息。 |
Connection |
连接建立、登录完成等事件。 |
Disconnect |
连接关闭或心跳超时。 |
Custom |
预留扩展槽位,可结合 argsid 区分租户、场景。 |
EventID<T>(强类型事件标识)
泛型记录类型,用于定义强类型事件标识,与源代码生成器联动产生 Emit/On 扩展方法:
public record EventID<T>(string Eventid) where T : IEventArgs
{
public T? Args { get; }
public static implicit operator string(EventID<T>? e) => e?.Eventid ?? string.Empty;
public static implicit operator EventID<T>(string id) => new(id);
}
// 框架预置的默认实例
public record DefaultEventID() : EventID<EventArgs>("system.event.default");
ISocpHub(v1.1.0 新增)
隔离事件中心接口,支持创建命名实例实现订阅隔离:
public interface ISocpHub : IDisposable
{
Guid Id { get; } // 当前实例唯一标识
string Name { get; } // 实例名称
IPublisher Publisher { get; } // 发布单元(Lazy 缓存)
ISubscriber Subscriber { get; } // 订阅单元(Lazy 缓存)
ISocpHub Create(string? name = null); // 创建新实例
void Remove(string eventid, string? argsid = null); // 移除指定订阅
void Remove(); // 移除所有订阅(等同于 Dispose)
}
IPublisher(v1.1.0 新增)
发布者接口,专注于事件发布职责:
| 方法 | 说明 |
|------|------|
| Publish<T>(EventID<T>, payload, ct?) | 同步发布(fire-and-forget,异常通过 ILogger 记录) |
| Publish<T>(string, payload, ct?) | 同步发布(字符串标识) |
| PublishAsync<T>(EventID<T>, payload, ct?) | 异步发布 |
| PublishAsync<T>(string, payload, ct?) | 异步发布(字符串标识) |
ISubscriber(v1.1.0 新增)
订阅者接口,专注于事件订阅职责:
| 方法 | 说明 |
|------|------|
| Subscribe<T>(string, handler) | 使用字符串标识订阅 |
| Subscribe<T>(Func<(EventID<T>, string?), bool>, handler) | 使用 EventID<T> 筛选器订阅 |
| Subscribe<T>(Func<(string, string?), bool>, handler) | 使用字符串筛选器订阅 |
| Unsubscribe(Guid) | 根据订阅 ID 取消 |
| Unsubscribe(string, argsid?) | 根据事件标识取消 |
| HasSubscribers<T>(EventID<T>, argsid?) | 强类型检查订阅者 |
| HasSubscribers(string, argsid?) | 字符串检查订阅者 |
| FindSubscribers(string, argsid?) | 查找订阅者 ID |
提示:源代码生成器为每个
EventID<T>子类型生成强类型On(this ISubscriber, ...)扩展方法。
ISocpSubscriber(v1.1.2 新增)
支持订阅跟踪的订阅者接口,扩展 ISubscriber:
public interface ISocpSubscriber : ISubscriber
{
IEnumerable<Guid> Subscriptions { get; } // 获取所有跟踪的订阅 ID 快照
}
特性:
- 自动跟踪通过此实例创建的所有订阅。
Dispose()时自动批量取消所有订阅。- 取消订阅时自动从跟踪列表移除。
- 线程安全(v1.2.6 起 Unsubscribe 时不会在持锁状态下调用用户委托,杜绝死锁风险)。
使用场景:
- ViewModel 生命周期管理:页面销毁时一键清理所有订阅。
- 组件级订阅:组件卸载时自动清理。
- 避免手动管理多个订阅 ID。
v1.2.6 起,通过 DI 解析
ISubscriber返回的就是ISocpSubscriber实例,二者共享同一行为。
IEventArgs
事件参数接口,所有事件参数必须实现此接口:
public interface IEventArgs
{
string ID { get; set; } // 事件参数唯一标识,用于筛选
}
框架预置基类(可继承以省去 ID 实现):
public record EventArgs : IEventArgs { public string ID { get; set; } = Guid.NewGuid().ToString("N"); }
public record HubEventArgs : IEventArgs { public string ID { get; set; } = Guid.NewGuid().ToString("N"); }
IEventHub
事件中心门面接口,统一暴露订阅、发布与查询能力:
| 方法 | 说明 |
|---|---|
Socp 属性 |
关联的 ISocpHub 实例 |
Subscribe<T>(EventID<T>, handler) |
使用强类型标识订阅 |
Subscribe<T>(string, handler) |
使用字符串订阅 |
Subscribe<T>(filter, handler) |
使用筛选器订阅(两种重载) |
Unsubscribe(Guid) / Unsubscribe(string, argsid?) |
取消订阅 |
Publish<T> / PublishAsync<T> |
同步/异步发布 |
HasSubscribers(string, argsid?) |
检查订阅者 |
FindSubscribers(string, argsid?) |
查找订阅者 ID |
IEventDispatcher(v1.1.1 新增 / v1.2.6 重构)
事件调度中心接口,基于 Channel 的高性能异步任务队列:
internal interface IEventDispatcher : IDisposable, IAsyncDisposable // net6+
{
Task DispatchAsync<T>(EventHubContext context, T payload,
IReadOnlyList<EventHandler<T>> handlers,
CancellationToken cancellationToken = default) where T : IEventArgs;
int PendingCount { get; } // 排队等待的任务数
int ActiveCount { get; } // 正在执行的任务数
bool IsRunning { get; } // 调度器是否运行中
}
EventOptions(v1.1.1 新增)
事件调度器配置选项:
public class EventOptions
{
public int MaxConcurrency { get; set; } = Environment.ProcessorCount * 2;
public int? QueueCapacity { get; set; } = null;
public TimeSpan HandlerTimeout { get; set; } = TimeSpan.FromSeconds(30);
public bool EnableParallelHandlers { get; set; } = false;
}
| 配置项 | 默认值 | 说明 |
|---|---|---|
MaxConcurrency |
CPU × 2 | 工作线程数 = 此值,由此天然形成并发上限 |
QueueCapacity |
null | 队列容量,超过后阻塞生产者(背压控制) |
HandlerTimeout |
30 秒 | 单个处理器超时时间;超时后顺序模式继续下一个处理器 |
EnableParallelHandlers |
false | true = 并行执行(不支持停止传播);false = 顺序执行 |
快速使用
1. 注册服务
// Minimal API / ASP.NET Core
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEventHub();
// 自定义配置调度器选项(v1.1.1 新增)
builder.Services.AddEventHub(options =>
{
options.MaxConcurrency = 16; // 最大并发数
options.QueueCapacity = 1000; // 队列容量
options.HandlerTimeout = TimeSpan.FromSeconds(10); // 超时时间
options.EnableParallelHandlers = false; // 顺序执行
});
// .NET MAUI (MauiProgram.cs)
builder.Services.AddEventHub();
2. 定义事件参数
public class MessageEventArgs : IEventArgs
{
public string From { get; set; } = string.Empty;
public string To { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public string ID { get; set; } = Guid.NewGuid().ToString("N");
}
3. 订阅事件
var hub = app.Services.GetRequiredService<IEventHub>();
// 方式一:使用 EventID 字符串常量订阅
var subscriptionId = hub.Subscribe<MessageEventArgs>(
EventID.MessageReceived,
async (context, args) =>
{
Console.WriteLine($"[{context.EventID}] 收到消息: {args.Content}");
return false; // false 继续传递,true 停止传播
});
// 方式二:使用自定义字符串订阅(适合动态事件)
hub.Subscribe<MessageEventArgs>(
"chat.room.123",
async (context, args) =>
{
Console.WriteLine($"聊天室消息: {args.Content}");
return false;
});
// 方式三:使用筛选器订阅(区分会话/租户)
hub.Subscribe<MessageEventArgs>(
filter: info => info.eventid == EventID.Custom && info.argsid == "tenant-A",
handler: async (context, args) =>
{
return true; // 停止传播
});
4. 发布事件
// 使用 EventID 字符串常量发布
await hub.PublishAsync(
EventID.MessageReceived,
new MessageEventArgs
{
From = "Alice",
To = "Bob",
Content = "Hello from MAUI"
});
// 使用自定义字符串发布
await hub.PublishAsync(
"chat.room.123",
new MessageEventArgs { Content = "房间消息" });
// 同步发布(fire-and-forget,异常通过 ILogger 记录)
hub.Publish(EventID.MessageReceived, new MessageEventArgs { Content = "同步消息" });
5. 管理订阅
// 取消订阅(推荐方式)
hub.Unsubscribe(subscriptionId);
// 按事件标识批量取消
hub.Unsubscribe(EventID.MessageReceived);
hub.Unsubscribe("chat.room.123");
// 检查是否有订阅者(避免无订阅时构造复杂负载)
if (hub.HasSubscribers(EventID.MessageReceived))
{
await hub.PublishAsync(EventID.MessageReceived, payload);
}
// 查找所有订阅者 ID
var subscribers = hub.FindSubscribers(EventID.Custom, "tenant-A");
foreach (var id in subscribers)
{
Console.WriteLine($"订阅者: {id}");
}
6. 使用源代码生成器扩展方法(v1.2.4 新增)
定义自定义 EventID<T> 子类型后,源代码生成器会自动生成强类型扩展方法:
using Netor.EventHub;
// 定义自定义事件参数
public record ChatMessageArgs : IEventArgs
{
public string ID { get; set; } = Guid.NewGuid().ToString("N");
public string From { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
}
// 定义自定义事件标识(继承 EventID<T>)
public record ChatMessageEventID() : EventID<ChatMessageArgs>("chat.message");
var subscriber = app.Services.GetRequiredService<ISubscriber>();
var publisher = app.Services.GetRequiredService<IPublisher>();
// 自动生成的 On 扩展方法 — 无需手动指定泛型参数
subscriber.On(new ChatMessageEventID(), async (context, args) =>
{
Console.WriteLine($"收到来自 {args.From} 的消息: {args.Content}");
return false;
});
// 自动生成的 Emit 扩展方法 — 同步发布
publisher.Emit(new ChatMessageEventID(), new ChatMessageArgs
{
From = "Alice",
Content = "Hello!"
});
// 自动生成的 EmitAsync 扩展方法 — 异步发布
await publisher.EmitAsync(new ChatMessageEventID(), new ChatMessageArgs
{
From = "Bob",
Content = "Hi there!"
});
7. 使用隔离事件中心(v1.1.0 新增)
// 注入 ISocpHub 创建隔离实例
var socp = app.Services.GetRequiredService<ISocpHub>();
// 创建命名实例(订阅相互隔离)
var roomHub = socp.Create("chat-room-123");
var systemHub = socp.Create("system-notifications");
// 在隔离实例中订阅(仅接收该实例发布的事件)
roomHub.Subscriber.Subscribe<MessageEventArgs>(
EventID.MessageReceived,
async (context, args) =>
{
Console.WriteLine($"[房间消息] {args.Content}");
return false;
});
// 在隔离实例中发布(仅该实例的订阅者收到)
await roomHub.Publisher.PublishAsync(
EventID.MessageReceived,
new MessageEventArgs { Content = "仅房间内可见" });
// 销毁实例时自动清理所有订阅
roomHub.Dispose();
8. 单独使用 IPublisher / ISubscriber
// 只需要发布功能时,注入 IPublisher
public class NotificationService(IPublisher publisher)
{
public async Task SendAsync(string message)
{
await publisher.PublishAsync(
EventID.Broadcast,
new BroadcastEventArgs { Message = message });
}
}
// 只需要订阅功能时,注入 ISubscriber
// 注意:通过 DI 解析得到的 ISubscriber 实际上就是 ISocpSubscriber,
// 因此该实例 Dispose 时会自动清理本次组件创建的所有订阅。
public class MessageHandler(ISubscriber subscriber) : IDisposable
{
private Guid _subscriptionId;
public void Start()
{
_subscriptionId = subscriber.Subscribe<MessageEventArgs>(
EventID.MessageReceived,
async (context, args) =>
{
await ProcessMessageAsync(args);
return false;
});
}
public void Stop() => subscriber.Unsubscribe(_subscriptionId);
public void Dispose() => subscriber.Dispose(); // 兜底清理
}
9. 使用 ISocpSubscriber 自动跟踪订阅(v1.1.2 新增)
using Netor.EventHub.Interfaces;
public class ChatViewModel : IDisposable
{
private readonly ISocpSubscriber _subscriber;
public ChatViewModel(ISocpSubscriber subscriber)
{
_subscriber = subscriber;
// 创建多个订阅,无需手动保存每个 ID
_subscriber.Subscribe<MessageEventArgs>(
EventID.MessageReceived,
async (context, args) =>
{
Console.WriteLine($"收到消息: {args.Content}");
return false;
});
_subscriber.Subscribe<BroadcastEventArgs>(
EventID.Broadcast,
async (context, args) =>
{
Console.WriteLine($"广播: {args.Message}");
return false;
});
_subscriber.Subscribe<MessageEventArgs>(
"chat.room.123",
async (context, args) =>
{
Console.WriteLine($"房间消息: {args.Content}");
return false;
});
// 可以查看当前跟踪的订阅数量
Console.WriteLine($"当前订阅数: {_subscriber.Subscriptions.Count()}");
}
public void Dispose()
{
// 一键清理所有订阅!
_subscriber.Dispose();
}
}
对比传统方式:
// ❌ 传统方式:需要手动管理每个订阅 ID
private readonly List<Guid> _subscriptionIds = [];
public void Start()
{
_subscriptionIds.Add(subscriber.Subscribe(...));
_subscriptionIds.Add(subscriber.Subscribe(...));
_subscriptionIds.Add(subscriber.Subscribe(...));
}
public void Stop()
{
foreach (var id in _subscriptionIds)
subscriber.Unsubscribe(id);
_subscriptionIds.Clear();
}
// ✅ ISocpSubscriber 方式:自动跟踪,一键清理
public void Dispose() => _subscriber.Dispose();
完整示例
Samples/EventHub.Samples/Program.cs 展示了控制台主机中集成事件中心的最小流程:
using EventHub.Samples;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Netor.EventHub;
using EventArgs = Netor.EventHub.EventArgs;
var services = new ServiceCollection();
services.AddLogging(b => b.AddConsole().SetMinimumLevel(LogLevel.Debug));
services.AddEventHub();
var provider = services.BuildServiceProvider();
var subscriber = provider.GetRequiredService<ISubscriber>();
var publisher = provider.GetRequiredService<IPublisher>();
subscriber.On(new SampleEvent(), (_, args) =>
{
Console.WriteLine($"收到事件:{args.ID}");
return Task.FromResult(false);
});
publisher.Emit(new SampleEvent(), EventArgs.Empty);
Console.ReadKey();
public record SampleEvent() : EventID<EventArgs>("system.SampleEvent");
.NET MAUI 集成示例
// MauiProgram.cs
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
builder.Services.AddEventHub();
return builder.Build();
}
// ViewModel 中使用(传统方式)
public partial class ChatViewModel : ObservableObject
{
private readonly IEventHub _hub;
private Guid _subscriptionId;
public ChatViewModel(IEventHub hub)
{
_hub = hub;
_subscriptionId = _hub.Subscribe<MessageEventArgs>(
EventID.MessageReceived,
async (context, args) =>
{
MainThread.BeginInvokeOnMainThread(() => Messages.Add(args));
return false;
});
}
public void Cleanup() => _hub.Unsubscribe(_subscriptionId);
}
// ViewModel 中使用(推荐:使用 ISocpSubscriber,v1.1.2+)
public partial class ChatViewModel : ObservableObject, IDisposable
{
private readonly ISocpSubscriber _subscriber;
public ChatViewModel(ISocpSubscriber subscriber)
{
_subscriber = subscriber;
_subscriber.Subscribe<MessageEventArgs>(
EventID.MessageReceived,
async (context, args) =>
{
MainThread.BeginInvokeOnMainThread(() => Messages.Add(args));
return false;
});
_subscriber.Subscribe<BroadcastEventArgs>(
EventID.Broadcast,
async (context, args) =>
{
MainThread.BeginInvokeOnMainThread(() => Notifications.Add(args));
return false;
});
}
// 页面销毁时自动清理所有订阅
public void Dispose() => _subscriber.Dispose();
}
项目结构
Netor.EventHub/
├── Src/Netor.EventHub/
│ ├── Compatibility/
│ │ └── Polyfills.cs # net48 / netstandard2.1 编译占位类型
│ ├── Extensions/
│ │ ├── ISubscriberExtensions.cs # ISubscriber.On(...) 扩展(实例缓存)
│ │ └── ServiceCollectionExtensions.cs # AddEventHub DI 扩展
│ ├── Interfaces/
│ │ ├── IEventHub.cs # 事件中心门面接口
│ │ ├── IPublisher.cs # 发布者接口
│ │ ├── ISubscriber.cs # 订阅者接口
│ │ ├── ISocpHub.cs # 隔离事件中心接口
│ │ ├── ISocpSubscriber.cs # 订阅跟踪接口(v1.1.2)
│ │ ├── IEventDispatcher.cs # 事件调度中心接口(v1.1.1,internal)
│ │ ├── IPublisherProvider.cs # Publisher 工厂(internal)
│ │ └── ISubscriberProvider.cs # Subscriber 工厂(internal)
│ ├── Services/
│ │ ├── EventHub.cs # IEventHub 门面实现
│ │ ├── Publisher.cs # IPublisher 实现
│ │ ├── PublisherProvider.cs # IPublisherProvider 实现
│ │ ├── Subscriber.cs # ISubscriber 基础实现
│ │ ├── SubscriberProvider.cs # ISubscriberProvider 实现
│ │ ├── SocpSubscriber.cs # ISocpSubscriber 实现(v1.1.2)
│ │ ├── SocpHub.cs # ISocpHub 实现(Publisher/Subscriber 使用 Lazy)
│ │ ├── EventDispatcher.cs # IEventDispatcher 实现(Channel + Worker 池)
│ │ ├── EventStore.cs # 共享订阅存储(含 eventid 二级索引)
│ │ ├── Subscription.cs # 单条订阅项数据结构
│ │ ├── EventHubContext.cs # 事件上下文 readonly record struct
│ │ ├── EventID.cs # EventID 字符串常量集
│ │ ├── EventID~.cs # EventID<T> 泛型记录类型
│ │ ├── EventHandler~.cs # EventHandler<T> 委托
│ │ ├── Event~.cs # Event<T> 操作符封装(+=/-=)
│ │ └── EventTypedProvider.cs # EventTypedProvider<T> 索引器
│ ├── Interfaces/IEventArgs.cs # IEventArgs / EventArgs / HubEventArgs
│ └── EventOptions.cs # 调度器配置选项(v1.1.1)
├── Src/Netor.EventHub.Generator/
│ ├── SubscriberExtensionGenerator.cs # 增量源代码生成器(生成 On + Emit/EmitAsync)
│ └── EventIdTypeInfo.cs # EventID<T> 元数据结构体
└── Samples/EventHub.Samples/
├── Program.cs # 示例程序入口
├── Events.cs # SampleEvent 定义
└── Diagnostics.cs # 示例事件参数类型
历史拼写说明:v1.2.6 起目录名
Interfances/、Releaces/已修正为正确拼写Interfaces/、Releases/,同时类型SmapleEvent修正为SampleEvent。
最佳实践
架构选择
- 简单场景:直接注入
IEventHub,统一管理订阅和发布。 - 职责分离:注入
IPublisher或ISubscriber,按需使用。 - 多租户/模块隔离:使用
ISocpHub.Create()创建命名实例。
性能优化
- 使用
HasSubscribers在关键路径前做预检,避免无订阅场景下构造复杂负载。 - 优先使用
Unsubscribe(Guid)精确取消订阅,性能优于按事件标识批量取消。 - 长耗时操作使用
PublishAsync,避免阻塞调用线程。 - 字符串事件标识尽量复用(例如静态常量),让
EventStore的二级索引发挥最大效用。
调度器配置(v1.1.1 新增 / v1.2.6 重构)
根据应用场景调整 EventOptions:
builder.Services.AddEventHub(options =>
{
// I/O 密集型场景:提高并发数
options.MaxConcurrency = Environment.ProcessorCount * 4;
// 高负载场景:启用背压控制
options.QueueCapacity = 5000;
// 快速响应场景:缩短超时时间
options.HandlerTimeout = TimeSpan.FromSeconds(5);
// 无依赖处理器:启用并行执行
options.EnableParallelHandlers = true;
});
| 场景 | 推荐配置 |
|---|---|
| I/O 密集型 | MaxConcurrency = CPU × 4 |
| CPU 密集型 | MaxConcurrency = CPU |
| 内存受限 | QueueCapacity = 1000 |
| 实时响应 | HandlerTimeout = 5s |
| 独立处理器 | EnableParallelHandlers = true |
注意:v1.2.6 起,工作线程数 =
MaxConcurrency(之前版本仅启动MaxConcurrency/2个 worker)。 升级后实际并发翻倍,请根据实际负载重新评估配置。
事件设计
- 将自定义事件标识与业务模块对应,配合
argsid传递会话或租户信息。 - 使用字符串事件标识实现动态事件(如
chat.room.{roomId})。 - 事件参数类实现
IEventArgs,ID属性用于筛选分发。 - 优先定义
EventID<T>子类型,结合源代码生成器获得强类型On/Emit扩展。
MAUI 注意事项
- 在处理器中使用
MainThread.BeginInvokeOnMainThread切换到 UI 线程。 - 在 ViewModel 销毁时调用
Unsubscribe清理订阅,避免内存泄漏。 - 注入
ISocpSubscriber(或ISubscriber)并在IDisposable.Dispose中调用subscriber.Dispose(),可一键清理所有订阅。 - 使用
ISocpHub.Create()为每个页面创建隔离实例,页面销毁时自动清理。
异常处理
- 处理器中抛出的异常会被捕获并通过
ILogger记录,不会向上传播。 - 异常不会中断后续订阅者的执行。
- 处理器超时会记录警告日志,并继续执行下一个处理器(v1.1.1)。
- 同步
Publishfire-and-forget 任务异常也会通过ILogger记录(v1.2.6)。
ISocpHub 使用场景
- 多租户应用:为每个租户创建独立实例,事件相互隔离。
- 模块化架构:各模块使用独立实例,避免事件冲突。
- 聊天室/频道:每个房间一个实例,成员离开时自动清理。
- 页面级事件:MAUI 页面使用独立实例,页面销毁时一键清理。
许可证
MIT License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| .NET Framework | net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
- System.Threading.Channels (>= 10.0.5)
-
.NETStandard 2.1
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
- System.Threading.Channels (>= 10.0.5)
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.2.8
- 源代码生成器现在直接内嵌在 Netor.EventHub 主包的 analyzers/dotnet/cs/ 目录里,安装 Netor.EventHub 即可自动获得 Emit / EmitAsync / On 强类型扩展,无需再单独引用 Netor.EventHub.SourceGenerator
- 解决了 Netor.EventHub 自身(含 DefaultEventID 等)享受不到代码生成的问题
v1.2.7(破坏性变更)
- 命名空间 Netor.EventHub.Interfances 修正为 Netor.EventHub.Interfaces;显式 using 该命名空间的代码需要相应替换
- 类型 SmapleEvent 修正为 SampleEvent(仅 Samples 受影响)
- 包输出目录 Releaces 修正为 Releases(仅本仓库构建产物,对消费者无影响)
- 内部清理:旧版死代码注释、ISubscriber/IPublisher/IEventHub 文档对齐
v1.2.6
- 性能优化:EventStore 引入按 eventid 的二级索引,Publish/FindSubscribers/HasSubscribers/Unsubscribe(string) 从 O(N) 降到 O(matches)
- EventDispatcher 重构:去除工作线程数自动减半的限制,移除冗余 Semaphore;并行模式同样走 Channel,QueueCapacity 在并行模式下生效
- EventDispatcher 新增 IAsyncDisposable 支持,避免在 ASP.NET 关停时同步阻塞
- Publisher 同步 Publish 不再吞没异常,fire-and-forget 任务异常会通过 ILogger 记录
- 修复 ISubscriberExtensions 静态缓存导致跨 ISubscriber 实例污染的 Bug,改为按实例缓存
- 修复 Event<T> 的 += 操作符在并发场景下重复订阅的问题
- 修复 SocpSubscriber 在 lock 内调用用户 Filter 委托可能造成的死锁风险
- 修复 Subscription.Filter 元组参数 agrgsid 拼写错误为 argsid
- 同步 IEventDispatcher 接口与实现的 nullable annotation
- AssemblyVersion / FileVersion 与 Version 同步
v1.2.5
- 内部清理与文档完善
v1.2.4
- 源代码生成器新增 IPublisher 强类型扩展方法 Emit/EmitAsync
- 编译期自动为 EventID<T> 子类型生成 IPublisherExtensions,省去手动指定泛型参数
- SourceGenerator 版本同步升级至 1.1.0
v1.1.3
- ISocpSubscriber 接口新增继承 IDisposable
- 完善 ISocpSubscriber 和 SocpSubscriber 的 XML 文档注释
- 修正 ISocpSubscriber.Subscriptions 返回类型为 IEnumerable(原为 IEnumerator)
v1.1.2
- 新增 ISocpSubscriber 接口,扩展 ISubscriber 支持订阅跟踪
- 新增 SocpSubscriber 实现,自动跟踪所有订阅 ID
- SocpSubscriber.Dispose() 时自动批量取消所有订阅
- 修复 Subscriber 基类方法未标记 virtual 的问题
- 优化线程安全,使用 lock 保护订阅列表
v1.1.1
- 新增 IEventDispatcher 事件调度中心,支持异步任务队列调度
- 新增 EventOptions 配置类,支持并发控制、超时设置、队列容量配置
- Publisher 重构为委托 EventDispatcher 进行事件分发
- 所有 Publish 方法新增 CancellationToken 参数,支持取消操作
- 完善 XML 文档注释
v1.1.0 (重构版本)
- 新增 ISocpHub 接口,支持创建命名事件中心实例,实现订阅隔离
- 新增 IPublisher / ISubscriber 接口,解耦发布与订阅职责
- 重构 EventHub 为门面模式,委托给 Publisher 和 Subscriber 处理
- 新增 EventStore 共享存储,支持跨组件订阅管理
- 新增 Subscription.SocpID 属性,支持按事件中心实例过滤订阅
- 完善 EventHubContext 构造函数,支持关联 ISocpHub
- 修正 PublishAsync 方法注释(顺序执行而非并行)
- 补充 EventArgsBase 等类型的 XML 文档注释