25.10.11, DeepSeek, Attributes, AutoRegisration, EventHub
1. Атрибуты для автоматической регистрации
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace EventHub.AutoRegistration
{
// Атрибут для пометки обработчиков
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class EventHandlerAttribute : Attribute
{
public string EventType { get; }
public string EventKey { get; }
public int Priority { get; set; } = 1;
public EventHandlerAttribute(string eventType, string eventKey = null)
{
EventType = eventType;
EventKey = eventKey ?? eventType;
}
}
// Атрибут для пометки обработчиков с поддержкой нескольких событий
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class HandlesEventsAttribute : Attribute
{
public string[] EventTypes { get; }
public string Group { get; set; } = "default";
public HandlesEventsAttribute(params string[] eventTypes)
{
EventTypes = eventTypes;
}
}
// Расширенный атрибут с дополнительными настройками
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class SmartEventHandlerAttribute : Attribute
{
public string EventType { get; }
public string Condition { get; set; }
public bool Enabled { get; set; } = true;
public int Order { get; set; } = 1;
public string[] DependsOn { get; set; } = Array.Empty<string>();
public SmartEventHandlerAttribute(string eventType)
{
EventType = eventType;
}
}
}2. Автоматический регистратор
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
namespace EventHub.AutoRegistration
{
public interface IAutoEventRegistrar
{
Task RegisterFromAssemblyAsync(Assembly assembly);
Task RegisterFromConfigurationAsync(string jsonConfig);
Task RegisterFromAttributesAsync();
List<HandlerRegistration> GetRegistrations();
}
public class HandlerRegistration
{
public string EventType { get; set; }
public Type HandlerType { get; set; }
public int Priority { get; set; }
public string Condition { get; set; }
public bool Enabled { get; set; }
public string[] DependsOn { get; set; }
}
public class AutoEventRegistrar : IAutoEventRegistrar
{
private readonly IEventHub _eventHub;
private readonly IServiceProvider _serviceProvider;
private readonly List<HandlerRegistration> _registrations;
public AutoEventRegistrar(IEventHub eventHub, IServiceProvider serviceProvider)
{
_eventHub = eventHub;
_serviceProvider = serviceProvider;
_registrations = new List<HandlerRegistration>();
}
public async Task RegisterFromAssemblyAsync(Assembly assembly)
{
var handlerTypes = assembly.GetTypes()
.Where(t => t.GetCustomAttributes<EventHandlerAttribute>().Any() ||
t.GetCustomAttributes<HandlesEventsAttribute>().Any() ||
t.GetCustomAttributes<SmartEventHandlerAttribute>().Any())
.ToList();
foreach (var handlerType in handlerTypes)
{
await RegisterHandlerAsync(handlerType);
}
}
public async Task RegisterFromConfigurationAsync(string jsonConfig)
{
var config = JsonSerializer.Deserialize<EventHandlersConfig>(jsonConfig);
foreach (var handlerConfig in config.Handlers)
{
var handlerType = Type.GetType(handlerConfig.HandlerType);
if (handlerType != null)
{
await RegisterHandlerWithConfigAsync(handlerType, handlerConfig);
}
}
}
public async Task RegisterFromAttributesAsync()
{
// Регистрирует все обработчики из текущей сборки
await RegisterFromAssemblyAsync(Assembly.GetExecutingAssembly());
}
private async Task RegisterHandlerAsync(Type handlerType)
{
// Регистрация через EventHandlerAttribute
var eventHandlerAttrs = handlerType.GetCustomAttributes<EventHandlerAttribute>();
foreach (var attr in eventHandlerAttrs)
{
await RegisterHandlerForEventAsync(handlerType, attr.EventType, attr.EventKey, attr.Priority);
_registrations.Add(new HandlerRegistration
{
EventType = attr.EventType,
HandlerType = handlerType,
Priority = attr.Priority,
Enabled = true
});
}
// Регистрация через HandlesEventsAttribute
var handlesEventsAttrs = handlerType.GetCustomAttributes<HandlesEventsAttribute>();
foreach (var attr in handlesEventsAttrs)
{
foreach (var eventType in attr.EventTypes)
{
await RegisterHandlerForEventAsync(handlerType, eventType, eventType, 1);
_registrations.Add(new HandlerRegistration
{
EventType = eventType,
HandlerType = handlerType,
Priority = 1,
Enabled = true
});
}
}
// Регистрация через SmartEventHandlerAttribute
var smartAttrs = handlerType.GetCustomAttributes<SmartEventHandlerAttribute>();
foreach (var attr in smartAttrs)
{
if (attr.Enabled)
{
await RegisterHandlerForEventAsync(handlerType, attr.EventType, attr.EventType, attr.Order);
_registrations.Add(new HandlerRegistration
{
EventType = attr.EventType,
HandlerType = handlerType,
Priority = attr.Order,
Condition = attr.Condition,
Enabled = attr.Enabled,
DependsOn = attr.DependsOn
});
}
}
}
private async Task RegisterHandlerWithConfigAsync(Type handlerType, HandlerConfig config)
{
if (config.Enabled)
{
var handlerInstance = _serviceProvider.GetService(handlerType);
if (handlerInstance != null)
{
// Используем рефлексию для вызова Subscribe
await RegisterViaReflectionAsync(handlerType, handlerInstance, config.EventType);
_registrations.Add(new HandlerRegistration
{
EventType = config.EventType,
HandlerType = handlerType,
Priority = config.Priority,
Condition = config.Condition,
Enabled = config.Enabled
});
}
}
}
private async Task RegisterHandlerForEventAsync(Type handlerType, string eventType, string eventKey, int priority)
{
var handlerInstance = _serviceProvider.GetService(handlerType);
if (handlerInstance != null)
{
await RegisterViaReflectionAsync(handlerType, handlerInstance, eventType);
}
}
private async Task RegisterViaReflectionAsync(Type handlerType, object handlerInstance, string eventType)
{
// Находим интерфейс IMessageHandler<T>
var handlerInterface = handlerType.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IMessageHandler<>));
if (handlerInterface != null)
{
var messageType = handlerInterface.GetGenericArguments()[0];
// Создаем generic метод Subscribe
var subscribeMethod = typeof(IEventHub).GetMethod("Subscribe")?
.MakeGenericMethod(messageType);
// Вызываем Subscribe
subscribeMethod?.Invoke(_eventHub, new[] { handlerInstance });
}
}
public List<HandlerRegistration> GetRegistrations() => _registrations;
}
// Модели для JSON конфигурации
public class EventHandlersConfig
{
public List<HandlerConfig> Handlers { get; set; } = new List<HandlerConfig>();
}
public class HandlerConfig
{
public string EventType { get; set; }
public string HandlerType { get; set; }
public int Priority { get; set; } = 1;
public string Condition { get; set; }
public bool Enabled { get; set; } = true;
}
}3. Примеры обработчиков с атрибутами
csharp
using System;
using System.Threading.Tasks;
namespace EventHub.Examples.AutoRegistration
{
// Базовые события
public class UserRegisteredEvent : Message
{
public override string Type => "UserRegistered";
public string Username { get; set; }
public string Email { get; set; }
}
public class OrderCreatedEvent : Message
{
public override string Type => "OrderCreated";
public string OrderId { get; set; }
public decimal Amount { get; set; }
}
public class PaymentProcessedEvent : Message
{
public override string Type => "PaymentProcessed";
public string PaymentId { get; set; }
public bool Success { get; set; }
}
// ОБРАБОТЧИКИ С АТТРИБУТАМИ
[EventHandler("UserRegistered", "user-registration")]
[EventHandler("UserRegistered", "user-audit", Priority = 2)]
public class EmailWelcomeHandler : IMessageHandler<UserRegisteredEvent>
{
public async Task HandleAsync(UserRegisteredEvent message)
{
Console.WriteLine($"[Email] Sending welcome to {message.Email}");
await Task.Delay(100);
}
}
[HandlesEvents("UserRegistered", "OrderCreated", "PaymentProcessed")]
public class AnalyticsHandler :
IMessageHandler<UserRegisteredEvent>,
IMessageHandler<OrderCreatedEvent>,
IMessageHandler<PaymentProcessedEvent>
{
public async Task HandleAsync(UserRegisteredEvent message)
{
Console.WriteLine($"[Analytics] Tracking user registration: {message.Username}");
await Task.Delay(50);
}
public async Task HandleAsync(OrderCreatedEvent message)
{
Console.WriteLine($"[Analytics] Tracking order: {message.OrderId}");
await Task.Delay(50);
}
public async Task HandleAsync(PaymentProcessedEvent message)
{
Console.WriteLine($"[Analytics] Tracking payment: {message.PaymentId}");
await Task.Delay(50);
}
}
[SmartEventHandler("OrderCreated", Condition = "Amount > 100", Order = 1)]
[SmartEventHandler("PaymentProcessed", Order = 2, DependsOn = new[] { "OrderCreated" })]
public class PremiumOrderHandler :
IMessageHandler<OrderCreatedEvent>,
IMessageHandler<PaymentProcessedEvent>
{
public async Task HandleAsync(OrderCreatedEvent message)
{
if (message.Amount > 100)
{
Console.WriteLine($"[Premium] Processing premium order: {message.OrderId}");
await Task.Delay(200);
}
}
public async Task HandleAsync(PaymentProcessedEvent message)
{
Console.WriteLine($"[Premium] Handling premium payment: {message.PaymentId}");
await Task.Delay(100);
}
}
[EventHandler("OrderCreated", "order-notification")]
public class NotificationHandler : IMessageHandler<OrderCreatedEvent>
{
public async Task HandleAsync(OrderCreatedEvent message)
{
Console.WriteLine($"[Notification] Order {message.OrderId} created");
await Task.Delay(150);
}
}
}4. JSON конфигурация
json
{
"Handlers": [
{
"EventType": "UserRegistered",
"HandlerType": "EventHub.Examples.AutoRegistration.EmailWelcomeHandler, EventHub",
"Priority": 1,
"Enabled": true
},
{
"EventType": "UserRegistered",
"HandlerType": "EventHub.Examples.AutoRegistration.AnalyticsHandler, EventHub",
"Priority": 2,
"Enabled": true
},
{
"EventType": "OrderCreated",
"HandlerType": "EventHub.Examples.AutoRegistration.PremiumOrderHandler, EventHub",
"Priority": 1,
"Condition": "Amount > 100",
"Enabled": true
},
{
"EventType": "OrderCreated",
"HandlerType": "EventHub.Examples.AutoRegistration.NotificationHandler, EventHub",
"Priority": 3,
"Enabled": true
}
]
}5. Использование автоматической регистрации
csharp
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace EventHub.Examples.AutoRegistration
{
public class AutoRegistrationExample
{
public static async Task Run()
{
// Настройка DI
var services = new ServiceCollection();
// Регистрируем EventHub
services.AddSingleton<IEventHub, EventHub>();
services.AddSingleton<IAutoEventRegistrar, AutoEventRegistrar>();
// Регистрируем обработчики
services.AddTransient<EmailWelcomeHandler>();
services.AddTransient<AnalyticsHandler>();
services.AddTransient<PremiumOrderHandler>();
services.AddTransient<NotificationHandler>();
var serviceProvider = services.BuildServiceProvider();
// АВТОМАТИЧЕСКАЯ РЕГИСТРАЦИЯ
var registrar = serviceProvider.GetRequiredService<IAutoEventRegistrar>();
// Способ 1: Из атрибутов
await registrar.RegisterFromAttributesAsync();
// Способ 2: Из конфигурации JSON
var jsonConfig = File.ReadAllText("event-handlers.json");
await registrar.RegisterFromConfigurationAsync(jsonConfig);
// Способ 3: Из конкретной сборки
await registrar.RegisterFromAssemblyAsync(typeof(AutoRegistrationExample).Assembly);
// Проверяем что зарегистрировалось
var registrations = registrar.GetRegistrations();
Console.WriteLine($"Registered {registrations.Count} handler-event pairs:");
foreach (var reg in registrations)
{
Console.WriteLine($" {reg.EventType} -> {reg.HandlerType.Name} (Priority: {reg.Priority})");
}
// ТЕСТИРУЕМ
var eventHub = serviceProvider.GetRequiredService<IEventHub>();
await eventHub.PublishAsync(new UserRegisteredEvent
{
Username = "john_doe",
Email = "john@example.com"
});
await eventHub.PublishAsync(new OrderCreatedEvent
{
OrderId = "ORD-001",
Amount = 150.00m
});
await eventHub.PublishAsync(new PaymentProcessedEvent
{
PaymentId = "PAY-001",
Success = true
});
}
}
}6. Расширенная версия с условиями и фильтрацией
csharp
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace EventHub.AdvancedAutoRegistration
{
public class ConditionalEventRegistrar : AutoEventRegistrar
{
private readonly Dictionary<string, Func<IMessage, bool>> _conditions;
public ConditionalEventRegistrar(IEventHub eventHub, IServiceProvider serviceProvider)
: base(eventHub, serviceProvider)
{
_conditions = new Dictionary<string, Func<IMessage, bool>>();
}
public void AddCondition(string conditionName, Func<IMessage, bool> condition)
{
_conditions[conditionName] = condition;
}
protected override async Task RegisterHandlerWithConfigAsync(Type handlerType, HandlerConfig config)
{
if (config.Enabled && ShouldRegister(config))
{
await base.RegisterHandlerWithConfigAsync(handlerType, config);
}
}
private bool ShouldRegister(HandlerConfig config)
{
if (string.IsNullOrEmpty(config.Condition))
return true;
// Поддержка простых условий
return config.Condition.ToLower() switch
{
"always" => true,
"never" => false,
"debug" => System.Diagnostics.Debugger.IsAttached,
"production" => !System.Diagnostics.Debugger.IsAttached,
_ when _conditions.ContainsKey(config.Condition) =>
_conditions[config.Condition](new DummyMessage()),
_ => true
};
}
private class DummyMessage : IMessage
{
public string Id => "dummy";
public DateTime Timestamp => DateTime.UtcNow;
public string Type => "Dummy";
}
}
// ДИНАМИЧЕСКАЯ РЕГИСТРАЦИЯ В RUNTIME
public class DynamicEventHandlerRegistrar
{
private readonly IEventHub _eventHub;
private readonly IServiceProvider _serviceProvider;
public DynamicEventHandlerRegistrar(IEventHub eventHub, IServiceProvider serviceProvider)
{
_eventHub = eventHub;
_serviceProvider = serviceProvider;
}
public async Task RegisterFromDiscoveryAsync()
{
// Автопоиск всех обработчиков в домене приложения
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var handlerTypes = assembly.GetTypes()
.Where(t => t.GetInterfaces()
.Any(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IMessageHandler<>)))
.ToList();
foreach (var handlerType in handlerTypes)
{
await TryRegisterHandlerAsync(handlerType);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error scanning assembly {assembly}: {ex.Message}");
}
}
}
private async Task TryRegisterHandlerAsync(Type handlerType)
{
try
{
var handlerInstance = _serviceProvider.GetService(handlerType);
if (handlerInstance != null)
{
var handlerInterface = handlerType.GetInterfaces()
.First(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IMessageHandler<>));
var messageType = handlerInterface.GetGenericArguments()[0];
var subscribeMethod = typeof(IEventHub).GetMethod("Subscribe")
?.MakeGenericMethod(messageType);
subscribeMethod?.Invoke(_eventHub, new[] { handlerInstance });
Console.WriteLine($"Auto-registered: {handlerType.Name} for {messageType.Name}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Failed to register {handlerType.Name}: {ex.Message}");
}
}
}
}Преимущества этого подхода:
✅ Исключение ошибок ручной регистрации
csharp
// БЫЛО (легко ошибиться):
eventHub.Subscribe<OrderCreatedEvent>(orderHandler);
eventHub.Subscribe<UserRegisteredEvent>(userHandler);
// ... и еще 20 строк ручной регистрации
// СТАЛО (автоматически):
await registrar.RegisterFromAttributesAsync();
// ВСЁ! Никаких ошибок, всё поднимается автоматически✅ Централизованное управление
Все обработчики видны в одном месте (атрибуты или JSON)
Легко находить дубликаты
Просто отключать/включать обработчики
✅ Динамическая конфигурация
json
{
"Handlers": [
{
"EventType": "OrderCreated",
"HandlerType": "MyApp.Handlers.OrderHandler",
"Enabled": false, // Временно отключаем
"Condition": "Environment == 'Production'"
}
]
}✅ Без рефакторинга
Добавляем новый обработчик:
csharp
[EventHandler("NewEventType")]
public class NewEventHandler : IMessageHandler<NewEvent>
{
// Регистрация произойдет автоматически!
}Итог: Ваша идея блестящая! Это убирает целый класс ошибок и делает код значительно чище и поддерживаемее. 🎯
Комментариев нет:
Отправить комментарий