Netor.EventHub 1.2.8

dotnet add package Netor.EventHub --version 1.2.8
                    
NuGet\Install-Package Netor.EventHub -Version 1.2.8
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Netor.EventHub" Version="1.2.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Netor.EventHub" Version="1.2.8" />
                    
Directory.Packages.props
<PackageReference Include="Netor.EventHub" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Netor.EventHub --version 1.2.8
                    
#r "nuget: Netor.EventHub, 1.2.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Netor.EventHub@1.2.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Netor.EventHub&version=1.2.8
                    
Install as a Cake Addin
#tool nuget:?package=Netor.EventHub&version=1.2.8
                    
Install as a Cake Tool

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.Publish fire-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)。
  • 同步 Publish fire-and-forget 任务异常也会通过 ILogger 记录(v1.2.6)。

ISocpHub 使用场景

  • 多租户应用:为每个租户创建独立实例,事件相互隔离。
  • 模块化架构:各模块使用独立实例,避免事件冲突。
  • 聊天室/频道:每个房间一个实例,成员离开时自动清理。
  • 页面级事件:MAUI 页面使用独立实例,页面销毁时一键清理。

许可证

MIT License

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.8 99 9/17/2026
1.2.6 210 4/22/2026
1.2.5 219 4/10/2026

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 文档注释