- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我在这里提出一个问题:Raising Domain Events For Multiple Subscribers答案让我想到了以下模式,我可以在其中拥有一个像这样的 IEventPublisher:
public interface IEventPublisher<T>
{
void Publish(T data);
}
和一个像这样的 IEventSubscriber:
public interface IEventSubscriber<T>
{
void Handle(T data);
}
问题是我需要将每个发布者的实例传递给构造函数,如下所示:
public Service(IEventPublisher<ThingyChangedEvent> publisherThingyChanged)
{
// Set publisher to local variable
}
// then call this in a method
_publisherThingyChanged.Publish(new ThingyChangedEvent { ThingyId = model.Id});
理想情况下,我希望能够拥有一个包含任何 IEventPublisher 的通用发布者,这样我就可以调用如下内容:
_genericPublisher.Publish(new ThingyChangedEvent { ThingyId = model.Id});
我不知道该怎么做,因为我无法在不将 T 定义为 ThingyChangedEvent 的情况下传递 IEventPublisher 的集合,而我想要的是根据传递给的类型确定发布者通用发布者。
非常感谢任何建议。
编辑:
确定使用下面的答案和这里的一些信息:http://www.udidahan.com/2009/06/14/domain-events-salvation/我想出了以下内容,但还不够:
public interface IEventManager
{
void Publish<T>(T args) where T : IEvent;
}
公共(public)类 EventManager : IEventManager { Autofac.ILifetimeScope _container;
public EventManager(Autofac.ILifetimeScope container)
{
_container = container;
}
//Registers a callback for the given domain event
public void Publish<T>(T args) where T : IEvent
{
var subscribersProvider = _container.Resolve<IEventSubscribersProvider<T>>();
foreach (var item in subscribersProvider.GetSubscribersForEvent())
{
item.Handle(args);
}
}
我现在可以在 autofac 解析的构造函数中获取 IEventManager eventManager 的实例,并按如下方式调用它:
_eventManager.Publish<ThingyChangedEvent>(new ThingyChangedEvent() { ThingyId = Guid.NewGuid() });
以下是我不喜欢这个解决方案的地方:
我不想在构造函数中获取 ILifetimeScope 的实例,我希望能够获取 IEventSubscribersProvider 的集合,但是如果我要求的话 autofac 不会解决这个问题:
IEnumerable<IEventSubscribersProvider<IEvent>>
如果我将类型传递给发布并调用,我只能解决它:
Resolve<IEventSubscribersProvider<T>>.
第二个问题不是什么大问题,但能够调用发布而不必像这样传递类型会很好:
_eventManager.Publish(new ThingyChangedEvent() { ThingyId = Guid.NewGuid() });
我想如果有人对如何解决这两个问题有任何建议,特别是问题 1,因为我不喜欢在不同的项目中依赖 Autofac。我唯一能想出的是某种管理类,它明确采用我需要的如下内容:
public SomeConstructor(
IEventSubscribersProvider<ThingyChangedEvent> thingChangedSubscribeProviders,
IEventSubscribersProvider<SomeOtherEvent> someOtherSubscribeProviders,
etc....)
{
// Maybe take the EventManager as well and add them to it somehow but would be
// far easier to take a collection of these objects somehow?
}
非常感谢您的任何建议。
编辑 2
经过大量研究并查看此 Autofac Generic Service resolution at runtime我不确定我能否实现我想要的。我能想到的最佳解决方案是:
public interface IEventSubscribersProviderFactory : Amico.IDependency
{
IEventSubscribersProvider<T> Resolve<T>() where T : IEvent;
}
public class EventSubscribersProviderFactory : IEventSubscribersProviderFactory
{
Autofac.ILifetimeScope _container;
public EventSubscribersProviderFactory(Autofac.ILifetimeScope container)
{
_container = container;
}
public IEventSubscribersProvider<T> Resolve<T>() where T : IEvent
{
return _container.Resolve<IEventSubscribersProvider<T>>();
}
}
然后让 EventManager 在构造函数中使用 IEventSubscribersProviderFactory 以从该项目中删除对 Autofac 的依赖。
我现在会继续这样做,但希望从长远来看会找到更好的解决方案。
最佳答案
当您必须处理多种类型的事件时,它会变得有点复杂。您可能已经注意到,您不能只使用派生的泛型类型并期望像使用基本泛型一样使用它——.NET 变体不支持您想要使用它的地方。
您需要一个“基本”类型,它是您接受为“事件”的最小(或最窄)类型。我通常使用 marker interface喜欢public interface IEvent{}
.当然,您可以从 Object
派生;但我发现使用标记接口(interface)来表明您正在订阅或发布“事件”这一事实很有用(这意味着您不能只发布任何类型的对象,而只能发布“事件”)。
从多类型方面的处理来看,您需要编写一个类来缩小范围(采用派生类型并将同一对象“发布”为派生类型)。即使这样,您也需要通过更窄的适当实例来路由您的事件(以“绕过”方差限制)。那么,当然,您可以让多个订阅者订阅同一事件类型;所以你需要一些东西来将事件路由到多个订阅者。这通常称为多路复用器,它是 IEventPublisher
的特化。 .每种事件类型需要一个多路复用器——这将使用较窄的。给定事件使用哪个多路复用器取决于类型,因此多路复用器的集合将由字典管理,因此您可以按类型查找它们。然后,要按类型向多个订阅者发布事件,您只需查找多路复用器并调用其 IEventPublisher.Publish
方法。管理多路复用器的是一种类型 IEventPublisher
通常称为 Dispatcher (有些人可能称之为路由器)。
例如:
public class NarrowingSubscriber<TBase, TDerived> : IEventSubscriber<TBase>
where TDerived : TBase
where TBase : IEvent
{
private IEventSubscriber<TDerived> inner;
public NarrowingSubscriber(IEventSubscriber<TDerived> inner)
{
if (inner == null) throw new ArgumentNullException("inner");
this.inner = inner;
}
public void AttachSubscriber(IEventSubscriber<TDerived> subscriber)
{
inner = subscriber;
}
public void Handle(TBase data)
{
inner.Handle((TDerived)data);
}
}
public class Multiplexor<T> : IEventSubscriber<T> where T : IEvent
{
private readonly List<IEventSubscriber<T>> subscribers =
new List<IEventSubscriber<T>>();
public void AttachSubscriber(IEventSubscriber<T> subscriber)
{
subscribers.Add(subscriber);
}
public void RemoveSubscriber(IEventSubscriber<T> subscriber)
{
subscribers.Remove(subscriber);
}
public void Handle(T data)
{
subscribers.ForEach(x => x.Handle(data));
}
}
public class Dispatcher<TBase> : IEventPublisher<TBase> where TBase : IEvent
{
private readonly Dictionary<Type, Multiplexor<TBase>> subscriptions =
new Dictionary<Type, Multiplexor<TBase>>();
public void Publish(TBase data)
{
Multiplexor<TBase> multiplexor;
if (subscriptions.TryGetValue(data.GetType(), out multiplexor))
{
multiplexor.Handle(data);
}
}
public void Subscribe<TEvent>(IEventSubscriber<TEvent> handler)
where TEvent : TBase
{
Multiplexor<TBase> multiplexor;
if (!subscriptions.TryGetValue(typeof(TEvent), out multiplexor))
{
multiplexor = new Multiplexor<TBase>();
subscriptions.Add(typeof(TEvent), multiplexor);
}
multiplexor.AttachSubscriber(new NarrowingSubscriber<TBase, TEvent>(handler));
}
}
所以,您基本上发布了 4 次。一次到调度器,一次到多路复用器,一次到窄器,一次到非基础设施用户。如果您有两个订阅者(EventOneEventSubscriber
和 EventTwoEventSubscriber
)订阅了两种类型的事件(EventOne
和 EventTwo
),那么您可以创建一个调度程序并像这样发布事件:
var d = new Dispatcher<IEvent>();
var eventTwoSubscriber = new EventTwoEventSubscriber();
d.Subscribe(eventTwoSubscriber);
var eventOneSubscriber = new EventOneEventSubscriber();
d.Subscribe(eventOneSubscriber);
d.Publish(new EventOne());
d.Publish(new EventTwo());
当然,事件将从 IEvent 派生:
public class EventOne : IEvent
{
}
public class EventTwo : IEvent
{
}
此特定限制未考虑多次分派(dispatch)事件。例如,我可以订阅 EventOne
和一名订阅者 IEvent
.此实现仅发布到 EventOne
订户如果 EventOne
对象通过调度程序发布——它不会发布到 IEvent
订户。我会把它留给读者作为练习:)
如果您希望做的是自动连接订阅者而无需构建它们(我认为这没有多大值(value),请考虑 Composition Root ),那么您可以向Dispatcher
(或其他地方,如果需要)订阅所有兼容的订阅者:
public void InitializeSubscribers()
{
foreach (object subscriber in
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where !type.IsAbstract && type.IsClass && !type.ContainsGenericParameters &&
type.GetInterfaces().Any(t => t.IsGenericType &&
t.GetGenericTypeDefinition() == typeof (IEventSubscriber<>))
select type.GetConstructor(new Type[0])
into constructorInfo
where constructorInfo != null
select constructorInfo.Invoke(new object[0]))
{
Subscribe((dynamic) subscriber);
}
}
您将在其中使用如下内容:
var d = new Dispatcher<IEvent>();
d.InitializeSubscribers();
...使用容器来解析 Dispatcher<T>
对象,如果你愿意的话。
关于c# - 领域事件模式单点队列事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15690396/
对此感到疯狂,真的缺少一些东西。 我有webpack 4.6.0,webpack-cli ^ 2.1.2,所以是最新的。 在文档(https://webpack.js.org/concepts/mod
object Host "os.google.com" { import "windows" address = "linux.google.com" groups = ["linux"] } obj
每当我安装我的应用程序时,我都可以将数据库从 Assets 文件夹复制到 /data/data/packagename/databases/ .到此为止,应用程序工作得很好。 但 10 或 15 秒后
我在 cc 模式缓冲区中使用 hideshow.el 来折叠我不查看的文件部分。 如果能够在 XML 文档中做到这一点就好了。我使用 emacs 22.2.1 和内置的 sgml-mode 进行 xm
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 我们不允许提出有关书籍、工具、软件库等方面的建议的问题。您可以编辑问题,以便用事实和引用来回答它。 关闭
根据java: public Scanner useDelimiter(String pattern) Sets this scanner's delimiting pattern to a patt
我读过一些关于 PRG 模式以及它如何防止用户重新提交表单的文章。比如this post有一张不错的图: 我能理解为什么在收到 2xx 后用户刷新页面时不会发生表单提交。但我仍然想知道: (1) 如果
看看下面的图片,您可能会清楚地看到这一点。 那么如何在带有其他一些 View 的简单屏幕中实现没有任何弹出/对话框/模式的微调器日期选择器? 我在整个网络上进行了谷歌搜索,但没有找到与之相关的任何合适
我不知道该怎么做,我一直遇到问题。 以下是代码: rows = int(input()) for i in range(1,rows): for j in range(1,i+1):
我想为重写创建一个正则表达式。 将所有请求重写为 index.php(不需要匹配),它不是以/api 开头,或者不是以('.html',或'.js'或'.css'或'.png'结束) 我的例子还是这样
MVC模式代表 Model-View-Controller(模型-视图-控制器) 模式 MVC模式用于应用程序的分层开发 Model(模型) - 模型代表一个存取数据的对象或 JAVA PO
我想为组织模式创建一个 RDF 模式世界。您可能知道,组织模式文档基于层次结构大纲,其中标题是主要的分组实体。 * March auxiliary :PROPERTIES: :HLEVEL: 1 :E
我正在编写一个可以从文件中读取 JSON 数据的软件。该文件包含“person”——一个值为对象数组的对象。我打算使用 JSON 模式验证库来验证内容,而不是自己编写代码。符合代表以下数据的 JSON
假设我有 4 张 table 人 公司 团体 和 账单 现在bills/persons和bills/companys和bills/groups之间是多对多的关系。 我看到了 4 种可能的 sql 模式
假设您有这样的文档: doc1: id:1 text: ... references: Journal1, 2013, pag 123 references: Journal2, 2014,
我有这个架构。它检查评论,目前工作正常。 var schema = { id: '', type: 'object', additionalProperties: false, pro
这可能很简单,但有人可以解释为什么以下模式匹配不明智吗?它说其他规则,例如1, 0, _ 永远不会匹配。 let matchTest(n : int) = let ran = new Rand
我有以下选择序列作为 XML 模式的一部分。理想情况下,我想要一个序列: 来自 my:namespace 的元素必须严格解析。 来自任何其他命名空间的元素,不包括 ##targetNamespace和
我希望编写一个 json 模式来涵盖这个(简化的)示例 { "errorMessage": "", "nbRunningQueries": 0, "isError": Fals
首先,我是 f# 的新手,所以也许答案很明显,但我没有看到。所以我有一些带有 id 和值的元组。我知道我正在寻找的 id,我想从我传入的三个元组中选择正确的元组。我打算用两个 match 语句来做到这
我是一名优秀的程序员,十分优秀!