我一直在使用 Domain Events pattern一段时间以来 - 它使我们能够在我们的领域层中封装尽可能多的行为,并为我们应用程序的其他部分提供一种很好的方式来订阅领域事件。
目前我们正在使用一个静态类,我们的域对象可以调用它来引发事件:
static class DomainEvents
{
public static IEventDispatcher Dispatcher { get; set; }
public static void Raise<TEvent>(TEvent e)
{
if (e != null)
{
Dispatcher.Dispatch(e);
}
}
}
如您所见,这只不过是 IEventDispatcher 的垫片。它实际上执行调度 或发布 事件的工作。
我们的调度程序实现仅使用我们的 IoC 容器 (StructureMap) 来定位指定事件类型的事件处理程序。
public void Dispatch<TEvent>(TEvent e)
{
foreach (var handler in container.GetAllInstances<IHandler<TEvent>>())
{
handler.Handle(e);
}
}
这在大多数 情况下都可以正常工作。但是,这种方法存在一些问题:
仅当实体成功持久化时才应分派(dispatch)事件
参加以下类(class):
public class Order
{
public string Id { get; private set; }
public decimal Amount { get; private set; }
public Order(decimal amount)
{
Amount = amount;
DomainEvents.Raise(new OrderRaisedEvent { OrderId = Id });
}
}
在Order构造函数我们提出一个 OrderRaisedEvent .在我们的应用程序层中,我们可能会创建订单实例,将其添加到我们的数据库“ session ”中,然后提交/保存更改:
var order = new Order(amount: 10);
session.Store(order);
session.SaveChanges();
这里的问题是在我们成功保存订单实体(提交交易)之前引发了领域事件。如果保存失败,我们仍然会发送事件。
更好的方法是将事件排队直到实体被持久化。但是,我不确定如何在维护强类型事件处理程序的同时最好地实现它。
不应创建事件直到实体被持久化
我面临的另一个问题是我们的实体标识符在存储实体之前不会被设置/分配(RavenDB - session.Store)。这意味着在上面的示例中,传递给事件的订单标识符实际上是 null .
因为我不确定如何预先实际生成 RavenDB 标识符,一个解决方案可能是延迟事件的创建,直到实体被实际保存,但我又不知道如何最好地实现这个——也许是排队一个集合的 Func<TEntity, TEvent> ?
最佳答案
一种解决方案(如@synhershko 所建议的那样)是将域事件的调度移到域之外。通过这种方式,我们可以确保我们的实体在引发任何事件之前持久化。
但是,我们现在正在将行为从域(它所属的地方)移到我们的应用程序中,只是为了解决我们的持久性技术——我对此不太高兴。
我的只有在实体成功保留时才应分派(dispatch)事件的解决方案是创建一个延迟事件分派(dispatch)器来对事件进行排队。然后,我们将调度程序注入(inject)到我们的工作单元中,确保我们首先持久化/保存我们的实体,然后发出领域事件:
public class DeferredEventDispatcher : IEventDispatcher
{
private readonly IEventDispatcher inner;
private readonly ConcurrentQueue<Action> events = new ConcurrentQueue<Action>();
public DeferredEventDispatcher(IEventDispatcher inner)
{
this.inner = inner;
}
public void Dispatch<TEvent>(TEvent e)
{
events.Enqueue(() => inner.Dispatch(e));
}
public void Resolve()
{
Action dispatch;
while (events.TryDequeue(out dispatch))
{
dispatch();
}
}
}
public class UnitOfWork
{
public void Commit()
{
session.SaveChanges();
dispatcher.Resolve(); // raise events
}
}
基本上这实现了与@synhershko 所建议的相同的东西,但在我的域内保持事件的“引发”。
至于在实体被持久化之前不应创建事件,主要问题是实体标识符是由 RavenDB 在外部设置的。使我的域持久无知且易于测试的解决方案是简单地将 id 作为构造函数参数传递。如果使用 SQL 数据库(通常传递 Guid),这就是我会做的。
幸运的是,RavenDB 确实为您提供了一种使用 hilo 策略生成标识符的方法(因此我们可以保留 RESTful 标识符)。这是来自 RavenDB Contrib项目:
public static string GenerateIdFor<T>(this IAdvancedDocumentSessionOperations session)
{
// An entity instance is required to generate a key, but we only have a type.
// We might not have a public constructor, so we must use reflection.
var entity = Activator.CreateInstance(typeof(T), true);
// Generate an ID using the commands and conventions from the current session
var conventions = session.DocumentStore.Conventions;
var databaseName = session.GetDatabaseName();
var databaseCommands = session.GetDatabaseCommands();
return conventions.GenerateDocumentKey(databaseName, databaseCommands, entity);
}
然后我可以使用它来生成一个 ID 并将其传递到我的实体构造函数中:
var orderId = session.GenerateIdFor<Order>();
var order = new Order(orderId, 1.99M);
关于c# - 延迟域事件的创建和调度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20792907/
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
是否可以在所有delayed_job任务之前运行一个方法?基本上,我们试图确保每个运行delayed_job的服务器都有我们代码的最新实例,所以我们想运行一个方法来在每个作业运行之前检查它。(我们已经有了“check”方法并在别处使用它。问题只是关于如何从delayed_job中调用它。) 最佳答案 现在有一种官方方法可以通过插件来做到这一点。这篇博文通过示例清楚地描述了如何执行此操作http://www.salsify.com/blog/delayed-jobs-callbacks-and-hooks-in-rails(本文中描述
这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果
我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几
我有一个帖子属于城市的关系,城市又属于一个州,例如:classPost现在我想找到所有帖子及其所属的城市和州。我编写了以下查询来获取带有城市的帖子,但不知道如何在同一查找器中获取带有城市的相应州:@post=Post.find:all,:include=>[:city]感谢任何帮助。谢谢。 最佳答案 Post.all(:include=>{:city=>:state}) 关于ruby-on-rails-使用Rails事件记录获取二级模型,我们在StackOverflow上找到一个类似的问