C#写一个简单的EventBus事件总线

前言:
因为我自己写的一个WPF前后端系统,会涉及到接收某个消息之后,针对这个动作会执行一些其他的数据操作等,为了程序的解耦,写了个简单的事件总线来管理。
插播一条广告:
Gitee仓库:Vampirewal.Admin欢迎大家Start
该事件总线我也打包成Nuget包了,地址:NuGet\Install-Package Vampirewal.Core.EventBus -Version 1.0.0.9

关于事件总线EventBus

网上关于这块的内容太多了,我就不再重复赘述,如需了解详细,请自行搜索一下其他博客的内容。

进入正文

1、代码

直接放出全部代码

文件名称功能描述
IVampirewalCoreEventBusFactoryVampirewalCore事件总线工厂接口
VampirewalCoreEventBusFactoryVampirewalCore事件总线工厂接口实现
IVampirewalCoreEventFactory事件工厂接口
EventFactoryContext事件工厂上下文
EventContext事件上下文
VampirewalCoreEventBusExceptionVampirewalCore事件总线异常类
EventBusExcuteAttribute标记在事件总线中的方法才会执行
EventBusAOPAttribute事件工厂AOP特性,需继承
VampirewalCoreEventBusExtension扩展方法

1.1、VampirewalCore事件总线工厂接口

/// <summary>
/// VampirewalCore事件总线工厂接口
/// </summary>
public interface IVampirewalCoreEventBusFactory
{
    /// <summary>
    /// 推送
    /// </summary>
    /// <param name="EventFactoryName">事件工厂Type名称</param>
    /// <param name="Params"></param>
    void Publish(string EventFactoryName, object? Params = null);

    /// <summary>
    /// 推送
    /// </summary>
    /// <typeparam name="TEventFactory"></typeparam>
    /// <param name="Params"></param>
    void Publish<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory;

    /// <summary>
    /// 并行推送
    /// </summary>
    /// <param name="EventFactoryName"></param>
    /// <param name="Params"></param>
    void PublishParallel(string EventFactoryName, object? Params = null);

    /// <summary>
    /// 并行推送
    /// </summary>
    /// <typeparam name="TEventFactory"></typeparam>
    /// <param name="Params"></param>
    void PublishParallel<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory;
}

1.2、VampirewalCore事件总线工厂接口实现

/// <summary>
/// VampirewalCore事件总线工厂
/// </summary>
internal class VampirewalCoreEventBusFactory : IVampirewalCoreEventBusFactory
{
    private readonly IServiceProvider Service;

    private Dictionary<string, Type> EventBusFactoryTypes { get; set; }

    public VampirewalCoreEventBusFactory(IServiceProvider provider)
    {
        Service = provider;

        EventBusFactoryTypes = new Dictionary<string, Type>();
        RegisterAll();
    }

    /// <summary>
    /// 注册全部
    /// </summary>
    private void RegisterAll()
    {
        Type[]? types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.GetInterfaces().Contains(typeof(IVampirewalCoreEventFactory))).ToArray();

        foreach (Type type in types)
        {
            if (type != null)
            {
                EventBusFactoryTypes.Add(type.Name, type);
            }
        }
    }

    public void Publish(string EventFactoryName, object? Params = null)
    {
        if (EventBusFactoryTypes.TryGetValue(EventFactoryName, out Type type))
        {
            var im = Service.GetService(type) as IVampirewalCoreEventFactory;

            if (im == null)
                throw new VampirewalCoreEventBusException($"未找到类型为{type}的事件工厂!");

            //获取当前事件工厂内的所有标记了EventBusExcuteAttribute的方法
            var methods = type.GetMethods().Where(w => w.GetCustomAttribute<EventBusExcuteAttribute>() != null).ToList();

            if (methods != null && methods.Count() == 0)
            {
                throw new VampirewalCoreEventBusException($"该事件工厂中没有找到标记EventBusExcuteAttribute特性的方法!");
            }
            else if (methods != null)
            {
                methods.Sort((s1, s2) =>
                {
                    var attr1 = s1.GetCustomAttribute<EventBusExcuteAttribute>();
                    var attr2 = s2.GetCustomAttribute<EventBusExcuteAttribute>();

                    if (attr1.ExcuteSort > attr2.ExcuteSort)
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                });

                //new一个事件工厂上下文
                EventFactoryContext FactoryContexts = new EventFactoryContext();

                foreach (var sub in methods)
                {
                    //new一个事件上下文,方便传递数据
                    EventContext context = new EventContext()
                    {
                        PassData = Params,
                        MethodName = sub.Name,
                        ExcutingTime = DateTime.Now
                    };

                    //将事件上下文添加到事件工厂的上下文集合中
                    FactoryContexts.EventContexts.Add(context);
                    //获取到当前方法上的AOP特性
                    var attrs = sub.GetCustomAttributes().Where(w => w is EventBusAOPAttribute).ToList();

                    try
                    {
                        //循环执行AOP特性的执行前方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteBefore(context);
                        }

                        sub.Invoke(im, new object[] { context });
                    }
                    catch (Exception ex)
                    {
                        //捕获方法执行中的异常信息
                        context.ExceptionInfo = ex.InnerException;
                        //将事件工厂的是否全部完成成功标记为false
                        FactoryContexts.IsEventFactoryComplateSuccess = false;
                    }
                    finally
                    {
                        context.ExcutedTime = DateTime.Now;
                        //循环执行AOP特性的执行后方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteAfter(context);
                        }

                        Params = context.PassData;
                    }
                }

                //在事件工厂内的方法全部都执行完成之后,执行事件工厂的完成方法
                im.EventFactoryComplate(FactoryContexts);
            }
        }
        else
        {
            throw new VampirewalCoreEventBusException($"未找到{EventFactoryName}对应的事件工厂!");
        }
    }

    public void Publish<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory
    {
        Type type = typeof(TEventFactory);

        Publish(type.Name, Params);
    }

    public void PublishParallel(string EventFactoryName, object? Params = null)
    {
        if (EventBusFactoryTypes.TryGetValue(EventFactoryName, out Type type))
        {
            var im = Service.GetService(type) as IVampirewalCoreEventFactory;

            if (im == null)
                throw new VampirewalCoreEventBusException($"未找到类型为{type}的事件工厂!");

            //获取当前事件工厂内的所有标记了EventBusExcuteAttribute的方法
            var methods = type.GetMethods().Where(w => w.GetCustomAttribute<EventBusExcuteAttribute>() != null).ToList();

            if (methods != null && methods.Count() == 0)
            {
                throw new VampirewalCoreEventBusException($"该事件工厂中没有找到标记EventBusExcuteAttribute特性的方法!");
            }
            else if (methods != null)
            {
                //new一个事件工厂上下文
                EventFactoryContext FactoryContexts = new EventFactoryContext();

                //这个foreach方法会并行执行,且会等待所有的方法执行完之后,再往下执行方法
                var result = Parallel.ForEach(methods, sub =>
                {
                    //new一个事件上下文,方便传递数据
                    EventContext context = new EventContext()
                    {
                        PassData = Params,
                        MethodName = sub.Name,
                        ExcutingTime = DateTime.Now
                    };
                    //将事件上下文添加到事件工厂的上下文集合中
                    FactoryContexts.EventContexts.Add(context);
                    //获取到当前方法上的AOP特性
                    var attrs = sub.GetCustomAttributes().Where(w => w is EventBusAOPAttribute).ToList();

                    try
                    {
                        //循环执行AOP特性的执行前方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteBefore(context);
                        }

                        sub.Invoke(im, new object[] { context });
                    }
                    catch (Exception ex)
                    {
                        //捕获方法执行中的异常信息
                        context.ExceptionInfo = ex.InnerException;
                        //将事件工厂的是否全部完成成功标记为false
                        FactoryContexts.IsEventFactoryComplateSuccess = false;
                    }
                    finally
                    {
                        context.ExcutedTime = DateTime.Now;
                        //循环执行AOP特性的执行后方法
                        foreach (EventBusAOPAttribute item in attrs)
                        {
                            item.ExcuteAfter(context);
                        }
                    }
                });

                if (result.IsCompleted)
                {
                    //在事件工厂内的方法全部都执行完成之后,执行事件工厂的完成方法
                    im.EventFactoryComplate(FactoryContexts);
                }
            }
        }
        else
        {
            throw new VampirewalCoreEventBusException($"未找到{EventFactoryName}对应的事件工厂!");
        }
    }

    public void PublishParallel<TEventFactory>(object? Params = null) where TEventFactory : IVampirewalCoreEventFactory
    {
        Type type = typeof(TEventFactory);

        PublishParallel(type.Name, Params);
    }
}

1.3、事件工厂接口

/// <summary>
/// 事件工厂
/// </summary>
public interface IVampirewalCoreEventFactory
{
    /// <summary>
    /// 工厂名称(方便查找定位)
    /// </summary>
    string FactoryName { get; }

    /// <summary>
    /// 事件工厂完成
    /// </summary>
    /// <param name="context"></param>
    void EventFactoryComplate(EventFactoryContext context);
}

1.4、事件工厂上下文

// <summary>
/// 事件工厂上下文
/// </summary>
public class EventFactoryContext
{
    public EventFactoryContext()
    {
        EventContexts = new List<EventContext>();
        IsEventFactoryComplateSuccess=true;
    }

    /// <summary>
    /// 内部方法上下文
    /// </summary>
    public List<EventContext> EventContexts { get;internal set; }

    /// <summary>
    /// 事件工厂是否全部完成成功
    /// </summary>
    public bool IsEventFactoryComplateSuccess { get;internal set; }
}

1.5、事件上下文

/// <summary>
/// 事件上下文
/// </summary>
public class EventContext
{
    /// <summary>
    /// 传递过来的参数
    /// </summary>
    public object? PassData { get; set; }

    /// <summary>
    /// 执行的方法名
    /// </summary>
    public string MethodName { get; internal set; } = "";

    /// <summary>
    /// 开始执行时间
    /// </summary>
    public DateTime? ExcutingTime { get; internal set; }

    /// <summary>
    /// 执行结束时间
    /// </summary>
    public DateTime? ExcutedTime { get; internal set; }

    /// <summary>
    /// 异常信息
    /// </summary>
    public Exception? ExceptionInfo { get; internal set; }
}

1.6、VampirewalCore事件总线异常类

/// <summary>
/// VampirewalCore事件总线异常类
/// </summary>
internal class VampirewalCoreEventBusException : Exception
{
    /// <summary>
    /// VampirewalCore事件总线异常类
    /// </summary>
    /// <param name="ErrorMsg">异常信息</param>
    public VampirewalCoreEventBusException(string ErrorMsg) : base(ErrorMsg)
    {
    }
}

1.7、EventBusExcuteAttribute,标记在事件总线中的方法才会执行

/// <summary>
/// 标记在事件总线中的方法才会执行
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class EventBusExcuteAttribute : Attribute
{
    /// <summary>
    /// 执行顺序
    /// </summary>
    public int ExcuteSort { get; set; } = 99;
}

1.8、EventBusAOPAttribute,事件工厂AOP特性,需继承

/// <summary>
/// 事件工厂AOP特性,需继承
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public abstract class EventBusAOPAttribute : Attribute
{
    /// <summary>
    /// 方法执行前
    /// </summary>
    /// <param name="context">上下文</param>
    public abstract void ExcuteBefore(EventContext context);
    /// <summary>
    /// 方法执行后
    /// </summary>
    /// <param name="context">上下文</param>
    public abstract void ExcuteAfter(EventContext context);
}

1.9、VampirewalCoreEventBusExtension,扩展

/// <summary>
///
/// </summary>
public static class VampirewalCoreEventBusExtension
{
    /// <summary>
    /// 将事件总线工厂注册到IoC中
    /// </summary>
    /// <param name="services"></param>
    public static void AddVampirewalCoreEventBusFactory(this IServiceCollection services)
    {
        Type[]? types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.GetInterfaces().Contains(typeof(IVampirewalCoreEventFactory))).ToArray();

        AddVampirewalCoreEventBusFactory(services, factory =>
        {
            foreach (var type in types)
            {
                factory.Add(type);
            }
        });
    }

    /// <summary>
    /// 如果事件工厂放在其他项目内,可能自动全部加载的方式无法获取到,可通过这个方法加载
    /// </summary>
    /// <param name="services"></param>
    /// <param name="eventFactorys"></param>
    public static void AddVampirewalCoreEventBusFactory(this IServiceCollection services, Action<List<Type>> eventFactorys)
    {
        List<Type> types = new List<Type>();

        eventFactorys?.Invoke(types);

        foreach (Type type in types)
        {
            if (type != null)
            {
                //var methods = type.GetMethods(BindingFlags.Public);
                var ErrorMethods = type.GetMethods()
                                                  .Where(w => w.GetCustomAttribute<EventBusExcuteAttribute>() != null && (w.GetParameters().Length != 1 || w.GetParameters()[0].ParameterType != typeof(EventContext)))
                                                  .Select(s => $"{type.Name}事件工厂内的{s.Name}方法参数数量不等于1或参数类型不是EventContext!请修改!")
                                                  .ToList();

                if (ErrorMethods != null && ErrorMethods.Count > 0)
                {
                    throw new VampirewalCoreEventBusException(string.Join("\r\n", ErrorMethods));
                }

                //注册事件工厂
                services.AddSingleton(type);
            }
        }

        //注册事件总线工厂
        services.AddSingleton<IVampirewalCoreEventBusFactory, VampirewalCoreEventBusFactory>();
    }
}

2、如何使用

2.1、将EventBusFactory注册进IServiceCollection

因为在上文的代码中,已经将我们的EventBusFactory使用单例注册进了IServiceCollection,之后在使用的时候,就可以通过构造函数注入的方式,直接在其他模块使用

2.1.1 注册进Vampirewal.Admin中

public class TestBootStartUp : VampirewalBootStartUp
{
    protected override string FirstViewKey => "MainView";

    protected override void RegisterService(IServiceCollection services)
    {
       //篇幅有限,此处进展示这里
        services.AddVampirewalCoreEventBusFactory();
    }
}

2.1.2 注册进其他IServiceCollection

services.AddVampirewalCoreEventBusFactory();

2.2、使用

2.2.1、代码准备

public class TestEventBus : IVampirewalCoreEventFactory
{
    public TestEventBus()
    {
        //此处构造函数中并没有其他参数,实际因为当前事件工厂已经被注册到了IServiceCollection中,所以其他被注册为单例的服务,可以在这里通过构造函数传进来
    }

    public string FactoryName => "测试用";

    public void EventFactoryComplate(EventFactoryContext context)
    {
    }

    [EventBusExcute(ExcuteSort = 3)]
    public void TestEvent111111(EventContext context)
    {
        context.PassData = "已修改2";
    }

    [TestEventBusAop]
    [EventBusExcute(ExcuteSort = 2)]
    public void TestEvent2222222(EventContext context)
    {
        //throw new Exception("test");
        context.PassData = "已修改1";
    }

    [EventBusExcute(ExcuteSort =1)]
    public void haha123(EventContext context123)
    {
        context123.PassData = "已修改";
    }
}
//该类为AOP使用
public class TestEventBusAopAttribute : EventBusAOPAttribute
{
    public override void ExcuteAfter(EventContext context)
    {
    }

    public override void ExcuteBefore(EventContext context)
    {
    }
}

2.2.2、代码说明

[EventBusExcute(ExcuteSort =1)]该特性的标记有2个作用
1、接收到消息之后,有这个特性的方法才会执行
2、使用PublishParallel推送的消息,并不会管ExcuteSort,只有Publish推送,会根据ExcuteSort进行排序,顺序执行
[TestEventBusAop]AOP特性
此处的AOP特性并不限一个,从上文的代码中可以看到,是根据标记在方法上的AOP数量进行顺序执行,可在方法上叠加多种AOP特性,仅需多次继承EventBusAOPAttribute并重写方法即可

2.2.3、调用

public class MainViewModel
{
    public MainViewModel(IVampirewalCoreEventBusFactory factory)
    {
        factory.Publish("TestEventBus", "Hello World");
        factory.Publish<TestEventBus>("Hello World");
        
        factory.PublishParallel<TestEventBus>("Hello World");
        factory.PublishParallel("TestEventBus", "Hello World");
    }
}

总结

以上就是我这边构思的事件总线全部内容,请大家指正
总体来看,和能搜索出来的主流EventBus有点不一样,就是我这里并没有将订阅者标记到某一个方法上,而是通过一个事件工厂的方式,通篇执行完这个工厂内的所有有标记的方法。这个也是业务需要导致的,抛砖引玉吧。
大家有更好的想法可以一起交流哈

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值