事件采购作为许多事情的奖励,例如事件历史/审计跟踪,完整和一致的视图再生等.听起来很棒.我是粉丝.但这些是读取端实现细节,您可以通过将事件存储完全移动到读取端作为另一个订阅者来完成相同的操作..那么为什么不呢?
这里有一些想法:
这似乎是将其保持在写入方面的一个技术论据.这是来自Greg Young的http://codebetter.com/gregyoung/2010/02/20/why-use-event-sourcing/:
但是,使用存储当前状态快照的东西存在一些问题.最大的问题围绕着您为数据引入了两个模型这一事实.您有一个事件模型和一个表示当前状态的模型.
我觉得有趣的是"快照"这个词,它最近也成为了事件采购中的一个杰出术语.在写入端引入事件存储会增加加载聚合的一些开销.您可以讨论多少开销,但这显然是一个感知或预期的问题,因为现在有从快照加载聚合的概念以及自快照以来的所有事件.所以现在我们又......两个模型了.不仅如此,我所看到的快照建议旨在实现为基础设施泄漏,后台进程遍历整个数据存储以保持高性能.
拍摄快照后,快照之前的事件从写入视角变为100%无用,除了...重建读取端!这似乎是错的.
另一个性能相关主题:文件存储.有时我们需要将大型二进制文件附加到实体.从概念上讲,有时这些与实体相关联,但有时它们是实体.将它们放在事件存储中意味着每次加载实体时都必须物理加载这些数据.这已经够糟糕了,但想象一下这些中的几个或几百个.我所看到的每一个答案都是基本上咬了一口子并将uri传递给文件.这是一个警察,破坏了分布式系统.
然后是维护.重建视图需要涉及事件存储的过程.因此,现在您编写的每个视图维护任务都会将您的写入模型绑定到使用事件存储...永远.
CQRS的重点不在于读取模型和写入模型的用例从根本上是不兼容的吗?那么我们为什么要把读取模型的东西放在写入端,牺牲灵活性和性能,并再次将它们重新耦合.为什么要花时间?
总而言之,我很困惑.在我所处的所有方面,事件存储作为阅读模型细节更有意义.您仍然可以实现保留事件存储的诸多好处,但是您不会过度抽象写入持久性,可能会降低灵活性和性能.并且您不会通过泄漏的抽象和维护任务将您的读/写端备份起来.
那么有人可以向我解释一个或多个令人信服的理由将其保留在写入方面吗?或者,为什么它不应该作为维护/报告问题进行阅读?我再次质疑商店的实用性.它应该去哪里:)
AspNetSynchronizationContext是最奇怪的实现.它将处理Post为同步而非异步,并使用锁定一次执行一个委托.
同样,他在同步上下文中写的文章与该评论中的链接表明:
从概念上讲,AspNetSynchronizationContext的上下文很复杂.在异步页面的生命周期中,上下文仅从ASP.NET线程池中的一个线程开始.异步请求启动后,上下文不包含任何线程.当异步请求完成时,执行其完成例程的线程池线程进入上下文.这些线程可能与发起请求的线程相同,但更可能是在操作完成时任何线程都是空闲的.
如果同一应用程序同时完成多个操作,AspNetSynchronizationContext将确保它们一次执行一个.它们可以在任何线程上执行,但该线程将具有原始页面的标识和文化.
挖掘反射器似乎验证了这一点,因为它HttpApplication在调用任何回调时需要锁定.
锁定app对象看起来像是可怕的东西.所以我的第一个问题:这是否意味着今天,整个应用程序的所有异步完成都会一次执行一个,即使是源自具有单独HttpContexts的单独线程上的单独请求的那些?对于任何100%使用异步页面(或MVC中的异步控制器)的应用程序来说,这不是一个巨大的瓶颈吗?如果没有,为什么不呢?我错过了什么?
此外,在.NET 4.5中,它看起来像是一个新的AspNetSynchronizationContext,旧的重命名LegacyAspNetSynchronizationContext,仅在UseTaskFriendlySynchronizationContext未设置新的应用程序设置时使用.问题2:新实现是否会改变这种行为?否则,我想通过同步上下文新的async/await支持编组完成,这种瓶颈会更频繁地被注意到.
这个论坛帖子的答案(从SO答案链接到这里)表明这里有一些根本改变的东西,但我想明确这是什么以及改进了什么行为,因为我们有一个.NET 4 MVC 3应用程序100%异步操作方法进行Web服务调用.
我正在尝试将AsyncController与依赖注入混合使用.有问题的MVC应用程序通过异步Web服务调用获取几乎所有数据.我们将TPL中的异步工作包装在Tasks中,并在完成这些任务时通知控制器的AsyncManager.
有时我们必须在这些任务的延续中触摸HttpContext - 添加cookie,无论如何.根据ASP.NET MVC中的使用异步控制器执行此操作的正确方法是调用该AsyncManager.Sync方法.这会将ASP.NET线程上下文(包括HttpContext)传播到当前线程,执行回调,然后恢复先前的上下文.
但是,该文章还说:
从已经受ASP.NET控制的线程调用Sync()具有未定义的行为.
如果你在控制器中完成所有工作,这不是问题,因为你通常知道你应该在延续中使用什么线程.但我要做的是在我们的异步数据访问和异步控制器之间创建一个中间层.所以这些也是异步的.所有东西都通过DI容器连接起来.
和以前一样,调用链中的一些组件需要使用"当前"的HttpContext.例如,在登录后我们想要存储"会话"令牌,我们从单点登录服务返回.这样做的东西的抽象是ISessionStore.想象一下CookieSessionStore会在响应中放置cookie,或者从请求中获取cookie.
我可以看到两个问题:
为了解决#1,我基本上TaskScheduler.FromCurrentSynchronizationContext()是在请求开始时注入一个抓取的对象,并且可以通过以该调度程序启动的Task调用一个动作,将HttpContextBase作为参数.
也就是说,从我的组件中,我可以调用类似于:
MySyncObject.Sync(httpContext => /* Add a cookie or something else */);
Run Code Online (Sandbox Code Playgroud)
我还没有发现任何问题,但我担心问题#2.我已经查看了两个AsyncManager和SynchronizationContextTaskScheduler在Reflector中,它们的操作类似,在ASP.NET上执行回调SynchronizationContext.那吓到我了:)
当我看到任务调度程序实现将直接调用而不是通过同步上下文(如果它是内联的)时,我有一点希望.但不幸的是,这似乎不是通过正常的Task.Start(scheduler)代码路径发生的.相反,任务可以在其他情况下内联,例如在他们开始之前等待他们.
所以我的问题是: