当前位置: 首页 > news >正文

计算机网站开发背景北京搬家公司哪家服务最好

计算机网站开发背景,北京搬家公司哪家服务最好,优化的近义词,山西省住房与城乡建设厅网站在ASP.NET Core Web API下事件驱动型架构的实现#xff08;一#xff09;#xff1a;一个简单的实现中#xff0c;我介绍了事件驱动型架构的一种简单的实现#xff0c;并演示了一个完整的事件派发、订阅和处理的流程。这种实现太简单了#xff0c;百十行代码就展示了一个… 在ASP.NET Core Web API下事件驱动型架构的实现一一个简单的实现中我介绍了事件驱动型架构的一种简单的实现并演示了一个完整的事件派发、订阅和处理的流程。这种实现太简单了百十行代码就展示了一个基本工作原理。然而要将这样的解决方案运用到实际生产环境还有很长的路要走。今天我们就研究一下在事件处理器中对象生命周期的管理问题。事实上不仅仅是在事件处理器中我们需要关心对象的生命周期在整个ASP.NET Core Web API的应用程序里我们需要理解并仔细推敲被注册到IoC容器中的服务它们的生命周期应该是个怎样的情形这也是服务端应用程序设计必须认真考虑的内容。因为如果生命周期管理不合理程序申请的资源无法合理释放最后便会带来内存泄漏、程序崩溃等各种问题然而这样的问题对于服务端应用程序来说是非常严重的。记得在上一篇文章的结束部分我给大家留下一个练习就是让大家在CustomerCreatedEventHandler事件处理器的HandleAsync方法中填入自己的代码以便对获得的事件消息做进一步的处理。作为本文的引子我们首先将这部分工作做完然后再进一步分析生命周期的问题。Event StoreEvent Store是CQRS体系结构模式中最为重要的一个组成部分它的主要职责就是保存发生于领域模型中的领域事件并对事件数据进行归档。当仓储需要获取领域模型对象时Event Store也会配合快照数据库一起根据领域事件的发生顺序逐步回放并重塑领域模型对象。事实上Event Store的实现是非常复杂的虽然从它的职责上来看并不算太复杂然而它所需要解决的事件同步、快照、性能、消息派发等问题使得CQRS体系结构的实现变得非常复杂。在实际应用中已经有一些比较成熟的框架和工具集能够帮助我们在CQRS中很方便地实现Event Store比如GetEventStore就是一个很好的开源Event Store框架它是基于.NET开发的在微软官方的eShopOnContainers说明文档中也提到了这个框架推荐大家上他们的官网https://eventstore.org/了解一下。在这里我们就先不深入研究Event Store应该如何实现我们先做一个简单的Event Store以便展示我们需要讨论的问题。延续着上一版的代码库https://github.com/daxnet/edasample/tree/chapter_1我们首先在EdaSample.Common.Events命名空间下定义一个IEventStore的接口这个接口非常简单仅仅包含一个保存事件的方法代码如下public interface IEventStore : IDisposable{    Task SaveEventAsyncTEvent(TEvent event)        where TEvent : IEvent;}SaveEventAsync方法仅有一个参数由泛型类型TEvent绑定的event对象。泛型约束表示SaveEventAsync方法仅能接受IEvent接口及其实现类型的对象作为参数传入。接口定义好了下一步就是实现这个接口对传入的事件对象进行保存。为了实现过程的简单我们使用Dapper将事件数据保存到SQL Server数据库中来模拟Event Store对事件的保存操作。Note为什么IEventStore接口的SaveEventAsync方法签名中没有CancellationToken参数严格来说支持async/await异步编程模型的方法定义上是需要带上CancellationToken参数的以便调用方请求取消操作的时候方法内部可以根据情况对操作进行取消。然而有些情况下取消操作并不是那么合理或者方法内部所使用的API并没有提供更深层的取消支持因此也就没有必要在方法定义上增加CancellationToken参数。在此处为了保证接口的简单没有引入CancellationToken的参数。接下来我们实现这个接口并用Dapper将事件数据保存到SQL Server中。出于框架设计的考虑我们新建一个Net Standard Class Library项目在这个新的项目中实现IEventStore接口这么做的原因已经在上文中介绍过了。代码如下public class DapperEventStore : IEventStore{    private readonly string connectionString;    public DapperEventStore(string connectionString)    {        this.connectionString connectionString;    }    public async Task SaveEventAsyncTEvent(TEvent event) where TEvent : IEvent    {        const string sql INSERT INTO [dbo].[Events] ([EventId], [EventPayload], [EventTimestamp]) VALUES (eventId, eventPayload, eventTimestamp);        using (var connection new SqlConnection(this.connectionString))        {            await connection.ExecuteAsync(sql, new            {                eventId event.Id,                eventPayload JsonConvert.SerializeObject(event),                eventTimestamp event.Timestamp            });        }    }    #region IDisposable Support    // 此处省略    #endregion}IDisposable接口的实现部分暂且省略可以看到实现还是非常简单的通过构造函数传入数据库的连接字符串在SaveEventAsyc方法中基于SqlConnection对象执行Dapper的扩展方法来完成事件数据的保存。Note: 此处使用了JsonConvert.SerializeObject方法来序列化事件对象也就意味着DapperEventStore程序集需要依赖Newtonsoft.Json程序集。虽然在我们此处的案例中不会有什么影响但这样做会造成DapperEventStore对Newtonsoft.Json的强依赖这样的依赖关系不仅让DapperEventStore变得不可测试而且Newtonsoft.Json将来未知的变化也会影响到DapperEventStore带来一些不确定性和维护性问题。更好的做法是引入一个IMessageSerializer接口在另一个新的程序集中使用Newtonsoft.Json来实现这个接口同时仅让DapperEventStore依赖IMessageSerializer并在应用程序启动时将Newtonsoft.Json的实现注册到IoC容器中。此时IMessageSerializer可以被MockDapperEventStore就变得可测试了另一方面由于只有那个新的程序集会依赖Newtonsoft.Json因此Newtonsoft.Json的变化也仅仅会影响那个新的程序集不会对框架主体的其它部分造成任何影响。EventStore实现好了接下来我们将其用在CustomerCreatedEventHandler中以便将订阅的CustomerCreatedEvent保存下来。事件数据的保存保存事件数据的第一步就是在ASP.NET Core Web API的IoC容器中将DapperEventStore注册进去。这一步是非常简单的只需要在Startup.cs的ConfigureServices方法中完成即可。代码如下public void ConfigureServices(IServiceCollection services){    services.AddMvc();    services.AddTransientIEventHandler, CustomerCreatedEventHandler();    services.AddTransientIEventStore(serviceProvider new DapperEventStore(Configuration[mssql:connectionString]));    services.AddSingletonIEventBus, PassThroughEventBus();}注意我们使用的是services.AddTransient方法来注册DapperEventStore我们希望应用程序在每次请求IEventStore实例时都能获得一个新的DapperEventStore的实例。接下来打开CustomerCreatedEventHandler.cs文件在构造函数中加入对IEventStore的依赖然后修改HandleAsync方法在该方法中使用IEventStore的实例来完成事件数据的保存。代码如下public class CustomerCreatedEventHandler : IEventHandlerCustomerCreatedEvent{    private readonly IEventStore eventStore;    public CustomerCreatedEventHandler(IEventStore eventStore)    {        this.eventStore eventStore;    }    public bool CanHandle(IEvent event)         event.GetType().Equals(typeof(CustomerCreatedEvent));    public async Taskbool HandleAsync(CustomerCreatedEvent event, CancellationToken cancellationToken default)    {        await this.eventStore.SaveEventAsync(event);        return true;    }    public Taskbool HandleAsync(IEvent event, CancellationToken cancellationToken default)         CanHandle(event) ? HandleAsync((CustomerCreatedEvent)event, cancellationToken) : Task.FromResult(false);}OK代码修改完毕测试一下。看看数据库中客户信息是否已经创建看看数据库中事件数据是否已经保存成功OK数据全部保存成功。然而事情真的就这么简单么No。在追踪了IEventStore实例也就是DapperEventStore的生命周期后你会发现问题没有想象的那么简单。追踪对象的生命周期在使用services.AddTransient/AddScoped/AddSingleton/AddScoped这些方法对服务进行注册时使用不同的方法也就意味着选择了不同的对象生命周期。在此我们也不再深入讨论每种方法之间的差异微软官方有详细的文档和demo抱歉我没有贴出中文链接因为机器翻译的缘故实在有点不堪入目如果对ASP.NET Core的IoC容器不熟悉的话建议先了解一下官网文章的内容。在上面我稍微提了一下我们是用AddTransient方法来注册DapperEventStore的因为我们希望在每次使用IEventStore的时候都会有一个新的DapperEventStore被创建。现在让我们来验证一下看情况是否果真如此。日志的使用追踪程序执行的最有效的方式就是使用日志。在我们的场景中使用基于文件的日志会更合适因为这样我们可以更清楚地看到程序的执行过程以及对象的变化过程。同样我不打算详细介绍如何在ASP.NET Core Web API中使用日志微软官网同样有着非常详尽的文档来介绍这些内容。在这里我简要地将相关代码列出来以介绍如何启用基于文件的日志系统。首先在Web API服务的项目上添加对Serilog.Extensions.Logging.File的nuget包使用它能够非常方便地启用基于文件的日志。然后打开Program.cs文件添加ConfigureLogging的调用public static IWebHost BuildWebHost(string[] args)     WebHost.CreateDefaultBuilder(args)        .ConfigureLogging((context, lb)         {            lb.AddFile(LogFileName);        })        .UseStartupStartup()        .Build();此处LogFileName为本地文件系统中的日志文件文件名为了避免权限问题我将日志写入C:\Users\user\appdata\local目录下因为我的Web API进程是由当前登录用户启动的所以写在这个目录下不会有权限问题。如果今后我们把Web API host在IIS中那么启动IIS服务的用户需要对日志所在的目录具有写入的权限日志文件才能被正确写入这一点是需要注意的。好了现在可以使用日志了先试试看。在Startup类的构造函数中加入ILoggerFactory参数并在构造函数执行时获取ILogger实例然后在ConfigureServices调用中输出一些内容public class Startup{    private readonly ILogger logger;    public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)    {        Configuration configuration;        this.logger loggerFactory.CreateLoggerStartup();    }    public IConfiguration Configuration { get; }    public void ConfigureServices(IServiceCollection services)    {        this.logger.LogInformation(正在对服务进行配置...);        services.AddMvc();        services.AddTransientIEventHandler, CustomerCreatedEventHandler();        services.AddTransientIEventStore(serviceProvider             new DapperEventStore(Configuration[mssql:connectionString]));        services.AddSingletonIEventBus, PassThroughEventBus();        this.logger.LogInformation(服务配置完成已注册到IoC容器);    }    // 其它方法暂时省略}现在重新启动服务然后查看日志文件发现日志可以被正确输出接下来使用类似的方式向PassThroughEventBus的构造函数和Dispose方法中加入一些日志输出在CustomersController的Create方法中、CustomerCreatedEventHandler的构造函数和HandleAsync方法中、DapperEventStore的构造函数和Dispose方法中也加入一些日志输出以便能够观察当新的客户信息被创建时Web API的执行过程。限于文章篇幅就不在此一一贴出各方法中加入日志输出的代码了大家可以根据本文最后所提供的源代码链接来获取源代码。简单地举个例子吧比如对于DapperEventStore我们通过构造函数注入ILogger的实例public class DapperEventStore : IEventStore{    private readonly string connectionString;    private readonly ILogger logger;    public DapperEventStore(string connectionString,        ILoggerDapperEventStore logger)    {        this.connectionString connectionString;        this.logger logger;        logger.LogInformation($DapperEventStore构造函数调用完成。Hash Code{this.GetHashCode()}.);    }    // 其它函数省略}这样一来在DapperEventStore的其它方法中就可以通过logger来输出日志了。发现问题同样再次运行Web API并通过Powershell发起一次创建客户信息的请求然后打开日志文件整个程序的执行过程基本上就一目了然了从上面的日志内容可以得知当应用程序正常退出时由IoC容器托管的PassThroughEventBus和DapperEventStore都能够被正常Dispose目前看来没什么问题因为资源可以正常释放。现在让我们重新启动Web API连续发送两次创建客户信息的请求再次查看日志我们得到了下面的内容从上面的日志内容可以看到在Web API的整个运行期间CustomerCreatedEventHandler仅被构造了一次而且在每次处理CustomerCreatedEvent事件的时候都是使用同一个DapperEventStore实例来保存事件数据。也就是说CustomerCreatedEventHandler和DapperEventStore在整个Web API服务的生命周期中有且仅有一个实例它们是Singleton的然而在进行系统架构的时候我们应该尽量保证较短的对象生命周期以免因为状态的不一致性导致不可回滚的错误出现这也是架构设计中的一种最佳实践。虽然目前我们的DapperEventStore在程序正常退出的时候能够被Dispose掉但如果DapperEventStore使用了非托管资源并且非托管资源并没有很好地管理自己的内存呢久而久之DapperEventStore就产生了内存泄漏点慢慢地Web API就会出现内存泄漏系统资源将被耗尽。假如Web API被部署在云中应用程序监控装置比如AWS的Cloud Watch就会持续报警并强制服务断线整个系统的可用性就无法得到保障。所以我们更期望DapperEventStore能够正确地实现C#的Dispose模式在Dispose方法中合理地释放资源并且仅在需要使用DapperEventStore时候才去构建它用完就及时Dispose以保证资源的合理使用。这也就是为什么我们使用services.AddTransient方法来注册CustomerCreatedEventHandler以及DapperEventStore的原因。然而事实却并非如此。究其原因就是因为PassThroughEventBus是单例实例它的生命周期是整个Web API服务。而在PassThroughEventBus的构造函数中CustomerCreatedEventHandler被作为参数传入于是PassThroughEventBus产生了对CustomerCreatedEventHandler的依赖而连带地也产生了对DapperEventStore的依赖。换句话说在整个应用程序运行的过程中IoC框架完全没有理由再去创建新的CustomerCreatedEventHandler以及DapperEventStore的实例因为事件处理器作为强引用被注册到PassThroughEventBus中而PassThroughEventBus至始至终没有变过Note为什么PassThroughEventBus可以作为单例注册到IoC容器中因为它提供了无状态的全局性的基础结构层服务事件总线。在PassThroughEventBus的实现中这种全局性体现得不明显我们当然可以每一次HTTP请求都创建一个新的PassThroughEventBus来转发事件消息并作处理。然而在今后我们要实现的基于RabbitMQ的事件总线中如果我们还是每次HTTP请求都创建一个新的消息队列不仅性能得不到保证而且消息并不能路由到新创建的channel上。注意我们将其注册成单例一个很重要的依据是由于它是无状态的但即使如此我们也要注意在应用程序退出的时候合理Dispose掉它所占用的资源。当然在这里ASP.NET Core的IoC机制会帮我们解决这个问题因为我注册了PassThroughEventBus但我没有显式调用Dispose方法我仍然能从日志中看到“PassThroughEventBus已经被Dispose”的字样然而有些情况下ASP.NET Core不会帮我们做这些就需要我们自己手工完成。OMG由于构造函数注入使得对象之间产生了依赖关系从而影响到了它们的生命周期这可怎么办既然问题是由依赖引起的那么就需要想办法解耦。解耦解决事件处理器对象生命周期问题经过分析我们需要解除PassThroughEventBus对各种EventHandler的直接依赖。因为PassThroughEventBus是单例的那么由它引用的所有组件也只可能具有相同的生命周期。然而这样的解耦又该如何做呢将EventHandler封装到另一个类中结果还是一样PassThroughEventBus总会通过某种对象关系来间接引用到EventHandler上造成EventHandler全局唯一。或许应该要有另一套生命周期管理体系来管理EventHandler的生命周期使得每当PassThroughEventBus需要使用EventHandler对所订阅的事件进行处理的时候都会通过这套体系来请求新的EventHandler实例这样一来PassThroughEventBus也就不再依赖于某个特定的实例了而仅仅是引用了各种EventHandler在新的生命周期管理体系中的注册信息。每当需要的时候PassThroughEventBus都会将事件处理器的注册信息传给新的管理体系然后由这套新的体系来维护事件处理器的生命周期。通过阅读微软官方的eShopOnContainers案例代码后证实了这一想法。在案例中有如下代码// namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQprivate async Task ProcessEvent(string eventName, string message){    if (_subsManager.HasSubscriptionsForEvent(eventName))    {        using (var scope _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME))        {            var subscriptions _subsManager.GetHandlersForEvent(eventName);            foreach (var subscription in subscriptions)            {                if (subscription.IsDynamic)                {                     var handler scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;                    dynamic eventData JObject.Parse(message);                    await handler.Handle(eventData);                }                else                {                    var eventType _subsManager.GetEventTypeByName(eventName);                    var integrationEvent JsonConvert.DeserializeObject(message, eventType);                    var handler scope.ResolveOptional(subscription.HandlerType);                    var concreteType typeof(IIntegrationEventHandler).MakeGenericType(eventType);                    await (Task)concreteType.GetMethod(Handle).Invoke(handler, new object[] { integrationEvent });                }            }        }    }}可以看到高亮的这一行通过Autofac创建了一个新的LifetimeScope在这个Scope中通过eventName来获得一个subscription对象也就是EventHandler的注册信息进而通过scope的ResolveOptional调用来获得新的EventHandler实例。基本过程就是这样目前也不需要纠结IDynamicIntegrationEventHandler是干什么用的也不需要纠结为什么要使用dynamic来保存事件数据。重点是autofac的BeginLifetimeScope方法调用创建了一个新的IoC Scope在这个Scope中解析resolve了新的EventHandler实例。在eShopOnContainer案例中EventBusRabbitMQ的设计是特定的必须依赖于Autofac作为依赖注入框架。或许这部分设计可以进一步改善使得EventBusRabbitMQ不会强依赖于Autofac。接下来我们会引入一个新的概念事件处理器执行上下文使用类似的方式来解决对象生命周期问题。事件处理器执行上下文事件处理器执行上下文Event Handler Execution Context, EHEC为事件处理器提供了一个完整的生命周期管理机制在这套机制中事件处理器及其引用的对象资源可以被正常创建和正常销毁。现在让我们一起看看如何在EdaSample的案例代码中使用事件处理器执行上下文。事件处理器执行上下文的接口定义如下当然这部分接口是放在EdaSample.Common.Events目录下作为消息系统的框架代码提供给调用方public interface IEventHandlerExecutionContext{    void RegisterHandlerTEvent, THandler()        where TEvent : IEvent        where THandler : IEventHandlerTEvent;    void RegisterHandler(Type eventType, Type handlerType);    bool HandlerRegisteredTEvent, THandler()        where TEvent : IEvent        where THandler : IEventHandlerTEvent;    bool HandlerRegistered(Type eventType, Type handlerType);    Task HandleEventAsync(IEvent event, CancellationToken cancellationToken default);}这个接口主要包含三种方法注册事件处理器、判断事件处理器是否已经注册以及对接收到的事件消息进行处理。整个结构还是非常清晰简单的。现在需要实现这个接口。根据上面的分析这个接口的实现是需要依赖于IoC容器的目前简单起见我们仅使用微软ASP.NET Core标准的Dependency Injection框架来实现当然也可以使用Autofac取决于你怎样去实现上面这个接口。需要注意的是由于该接口的实现是需要依赖于第三方组件的在这里是微软的Dependency Injection框架因此最佳做法是新建一个类库并引用EdaSample.Common程序集并在这个新的类库中依赖Dependency Injection框架来实现这个接口。以下是基于Microsoft.Extensions.DependencyInjection框架来实现的事件处理器执行上下文完整代码这里有个兼容性问题就是构造函数的第二个参数serviceProviderFactory。在Microsoft.Extensions.DependencyInjection框架2.0版本之前IServiceCollection.BuildServiceProvider方法的返回类型是IServiceProvider但从2.0开始它的返回类型已经从IServiceProvider接口变成了ServiceProvider类。这里引出了框架设计的另一个原则就是依赖较低版本的.NET Core以便获得更好的兼容性。如果我们的EdaSample是使用.NET Core 1.1开发的那么当下面这个类被直接用在ASP.NET Core 2.0的项目中时如果不通过构造函数参数传入ServiceProvider创建委托而是直接在代码中使用registry.BuildServiceProvider调用就会出现异常。public class EventHandlerExecutionContext : IEventHandlerExecutionContext{    private readonly IServiceCollection registry;    private readonly FuncIServiceCollection, IServiceProvider serviceProviderFactory;    private readonly ConcurrentDictionaryType, ListType registrations new ConcurrentDictionaryType, ListType();    public EventHandlerExecutionContext(IServiceCollection registry,         FuncIServiceCollection, IServiceProvider serviceProviderFactory null)    {        this.registry registry;        this.serviceProviderFactory serviceProviderFactory ?? (sc registry.BuildServiceProvider());    }    public async Task HandleEventAsync(IEvent event, CancellationToken cancellationToken default(CancellationToken))    {        var eventType event.GetType();        if (this.registrations.TryGetValue(eventType, out ListType handlerTypes)             handlerTypes?.Count 0)        {            var serviceProvider this.serviceProviderFactory(this.registry);            using (var childScope serviceProvider.CreateScope())            {                foreach(var handlerType in handlerTypes)                {                    var handler (IEventHandler)childScope.ServiceProvider.GetService(handlerType);                    if (handler.CanHandle(event))                    {                        await handler.HandleAsync(event, cancellationToken);                    }                }            }        }    }    public bool HandlerRegisteredTEvent, THandler()        where TEvent : IEvent        where THandler : IEventHandlerTEvent         this.HandlerRegistered(typeof(TEvent), typeof(THandler));    public bool HandlerRegistered(Type eventType, Type handlerType)    {        if (this.registrations.TryGetValue(eventType, out ListType handlerTypeList))        {            return handlerTypeList ! null handlerTypeList.Contains(handlerType);        }        return false;    }    public void RegisterHandlerTEvent, THandler()        where TEvent : IEvent        where THandler : IEventHandlerTEvent         this.RegisterHandler(typeof(TEvent), typeof(THandler));    public void RegisterHandler(Type eventType, Type handlerType)    {        Utils.ConcurrentDictionarySafeRegister(eventType, handlerType, this.registrations);        this.registry.AddTransient(handlerType);    }}好了事件处理器执行上下文就定义好了接下来就是在我们的ASP.NET Core Web API中使用。为了使用IEventHandlerExecutionContext我们需要修改事件订阅器的接口定义并相应地修改PassThroughEventBus以及Startup.cs。代码如下// IEventSubscriberpublic interface IEventSubscriber : IDisposable{    void SubscribeTEvent, TEventHandler()        where TEvent : IEvent        where TEventHandler : IEventHandlerTEvent;}// PassThroughEventBuspublic sealed class PassThroughEventBus : IEventBus{    private readonly EventQueue eventQueue new EventQueue();    private readonly ILogger logger;    private readonly IEventHandlerExecutionContext context;    public PassThroughEventBus(IEventHandlerExecutionContext context,        ILoggerPassThroughEventBus logger)    {        this.context context;        this.logger logger;        logger.LogInformation($PassThroughEventBus构造函数调用完成。Hash Code{this.GetHashCode()}.);        eventQueue.EventPushed EventQueue_EventPushed;    }    private async void EventQueue_EventPushed(object sender, EventProcessedEventArgs e)         await this.context.HandleEventAsync(e.Event);    public Task PublishAsyncTEvent(TEvent event, CancellationToken cancellationToken default)        where TEvent : IEvent             Task.Factory.StartNew(() eventQueue.Push(event));    public void SubscribeTEvent, TEventHandler()        where TEvent : IEvent        where TEventHandler : IEventHandlerTEvent    {        if (!this.context.HandlerRegisteredTEvent, TEventHandler())        {            this.context.RegisterHandlerTEvent, TEventHandler();        }    }    #region IDisposable Support    private bool disposedValue false; // To detect redundant calls    void Dispose(bool disposing)    {        if (!disposedValue)        {            if (disposing)            {                this.eventQueue.EventPushed - EventQueue_EventPushed;                logger.LogInformation($PassThroughEventBus已经被Dispose。Hash Code:{this.GetHashCode()}.);            }            disposedValue true;        }    }    public void Dispose() Dispose(true);    #endregion}// Startup.cspublic void ConfigureServices(IServiceCollection services){    this.logger.LogInformation(正在对服务进行配置...);    services.AddMvc();    services.AddTransientIEventStore(serviceProvider         new DapperEventStore(Configuration[mssql:connectionString],             serviceProvider.GetRequiredServiceILoggerDapperEventStore()));    var eventHandlerExecutionContext new EventHandlerExecutionContext(services,         sc sc.BuildServiceProvider());    services.AddSingletonIEventHandlerExecutionContext(eventHandlerExecutionContext);    services.AddSingletonIEventBus, PassThroughEventBus();    this.logger.LogInformation(服务配置完成已注册到IoC容器);}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){    var eventBus app.ApplicationServices.GetRequiredServiceIEventBus();    eventBus.SubscribeCustomerCreatedEvent, CustomerCreatedEventHandler();    if (env.IsDevelopment())    {        app.UseDeveloperExceptionPage();    }    app.UseMvc();}代码修改完成后再次执行Web API并发送两次或多次创建客户的请求然后查看日志我们发现每次请求都会使用新的事件处理器去处理接收到的消息在保存消息数据时会使用新的DapperEventStore来保存数据而保存完成后会及时将DapperEventStore dispose掉小结本文篇幅比较长或许你没有太多耐心将文章读完。但我尽量将问题分析清楚希望提供给读者的内容是详细的、有理有据的。文章中黑体部分是在设计过程中的一些思考和需要注意的地方希望能够给读者在工作和学习之中带来启发和收获。总而言之对象生命周期的管理在服务端应用程序中是非常重要的需要引起足够的重视。在下文中我们打算逐步摆脱PassThroughEventBus基于RabbitMQ来实现消息总线的基础结构。源代码的使用本系列文章的源代码在https://github.com/daxnet/edasample这个Github Repo里通过不同的release tag来区分针对不同章节的源代码。本文的源代码请参考chapter_2这个tag如下相关文章 ASP.NET Core Web API下事件驱动型架构的实现一一个简单的实现原文地址:https://www.cnblogs.com/daxnet/p/8270480.html.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com
http://wiki.neutronadmin.com/news/198522/

相关文章:

  • 网上打字兼职正规网站wordpress主题打开慢
  • 福州网站设计哪家做的好购物网站开发问题域分析
  • 网页制作与网站建设实战大全光盘wordpress悬浮窗插件
  • 网站制作学什么上海 互联网公司
  • 注册公司网站多少钱wordpress 名站
  • 深圳微信网站个人网站设计论文摘要
  • 做网站没有成本费用如何做账国际化网站
  • 企业门户网站模板高中网站建设计划表
  • 正规专业的网站建设公网上做网站任务
  • 2017网站icp备案青岛北京网站建设价格
  • 网站推广指标包括( )。网站开发与应用
  • 衡阳网站建设报价方案专业的网站建设价格
  • 网站建设不完整 审核惠州专门做网站
  • 徐州住房与建设局网站微信做网站的公司
  • 拓者设计吧官方网站网站常用字体大小
  • 团购网站 seo弄个网站多少钱
  • 知识产权教育平台网站开发总结怎么能让我的网站被百度收录
  • 上饶市网站建设公司国外有哪些做建筑材料的网站
  • 用路由器做网站网站建设成功案例书籍
  • asp企业网站cms界面设计属于什么专业
  • 电子商务专业网站设计开个免费一代发网店
  • c#如何做公司网站wordpress 准迁
  • vs网站开发入门网站优化公司排名
  • 综合服务门户网站建设下载学校网站模板下载地址
  • 上海的企业网站备案车票制作图片的软件
  • 网站文件上传好下一步怎么做征婚网站上拉业务做恒指期货
  • 万网网站建设 优帮云新产品代理项目推荐
  • 坪山医院网站建设龙岗网站建设推广报价
  • 二级域名网站建设second是什么意思
  • 成都网站优化方案商丘互联网公司