以下是我的情景:
我们有一个中央办公室和一些地点(50个并且正在增长)。我们的消息目前是从中心办公室发布的,这些消息是位于这些地点的机器订阅的。目前,每个位置都将收到此类型的所有已发布消息。消息具有locationnumber属性,该属性标识特定消息与哪个位置相关,并且在该位置的客户端上,忽略没有匹配位置数的任何消息。我想要做的是根据订户所在的位置动态创建一个消息类,并让它订阅该类型的消息。然后,发布者将检查数据并生成“相同”的动态消息类(不确定这是否匹配,在此过程中还没有那么远)并发布该消息,以便只有那些实际感兴趣的位置消息已发送给他们。这可能吗?我是否认为NServiceBus完全错误,是否有另一种方法可以进行此类过滤? 我目前只是试图订阅我创建的动态类型,我在尝试时遇到此错误:
No destination could be found for message type DerivedClassOne. Check the <MessageEndpointMappings> section of the configuration of this endpoint for an entry either for this specific message type or for its assembly.
这是我的配置文件:
<configuration>
<configSections>
<section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<UnicastBusConfig DistributorControlAddress="" DistributorDataAddress="" ForwardReceivedMessagesTo="">
<MessageEndpointMappings>
<!-- publishers don't need to set this for their own message types -->
<add Messages="NSBDynamicSubscriptionSpike.Messages" Endpoint="basequeue" />
</MessageEndpointMappings>
</UnicastBusConfig>
<appSettings>
<add key="MessageClassName" value="DerivedClassOne"/>
</appSettings>
</configuration>
这是我的代码:
namespace NSBDynamicSubscriptionSpike.Messages
{
public class BaseMessageClass : IMessage
{
public string BaseStringProp { get; set; }
public int BaseIntProp { get; set; }
}
}
namespace NSBDynamicSubscriptionSpike.Server
{
public class MessageHandler : AsA_Server,
IWantCustomInitialization,
IConfigureThisEndpoint,
IWantToRunWhenBusStartsAndStops
{
private Type myMessageType;
public IBus Bus { get; set; }
public void Handle(BaseMessageClass message)
{
BaseMessageClass m = (BaseMessageClass) message;
Console.WriteLine(string.Format("Message type: {0}", m.GetType()));
Console.WriteLine(string.Format("{0}: {1}", m.BaseIntProp, m.BaseStringProp));
}
private bool HandleMessage(object message)
{
Handle((BaseMessageClass)message);
return true;
}
public void Init()
{
NServiceBus.Configure.With()
.DefaultBuilder()
.Log4Net()
.MsmqTransport()
.UnicastBus()
.BinarySerializer()
.InMemorySubscriptionStorage()
.UseInMemoryTimeoutPersister()
.InMemoryFaultManagement()
.InMemorySagaPersister();
}
private Type GenerateDynamicType()
{
string dcName = ConfigurationManager.AppSettings["MessageClassName"];
AssemblyName aName = new AssemblyName(dcName);
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
ModuleBuilder mb = ab.DefineDynamicModule(dcName);
TypeBuilder tb = mb.DefineType(dcName,TypeAttributes.Public | TypeAttributes.Class, typeof(BaseMessageClass));
}
public void Start()
{
myMessageType = GenerateDynamicType();
//I tried this method first to get the endpoint set up for this type
MessageEndpointMappingCollection mappings = new MessageEndpointMappingCollection();
MessageEndpointMapping m;
m = new MessageEndpointMapping();
m.AssemblyName = myMessageType.AssemblyQualifiedName;
m.Messages = myMessageType.FullName;
m.Endpoint = "basequeue";
mappings.Add(m);
IComponentConfig<UnicastBusConfig> busConfig = Configure.Instance.Configurer.ConfigureComponent<UnicastBusConfig>(ComponentCallModelEnum.None);
busConfig.ConfigureProperty(u => u.MessageEndpointMappings, mappings);
//I also tried this from another SO question I found somewhat related, pick one or the other, not both (I think)
var ucb = Configure.Instance.Configurer.ConfigureComponent<NServiceBus.Unicast.UnicastBus>(ComponentCallModelEnum.Singleton);
ucb.ConfigureProperty(u => u.MessageOwners, new Dictionary<string, string>()
{
{myMessageType.AssemblyQualifiedName, "basequeue"}
});
//the line below is what throws the error
Bus.Subscribe(myMessageType, HandleMessage);
}
public void Stop()
{
Bus.Unsubscribe(myMessageType);
}
}
}
答案 0 :(得分:1)
我认为没有内置任何可以帮助你的东西。你要做的是content based routing。无论这是不是一个好主意,NSB并不是为了开箱即用而设计的(它是unicast总线)。
我建议采取另一个方向并装饰ISubscriptionStorage
。 StorageDrivenPublisher
(实现IPublishMessages
)使用此接口来获取要将消息发布到的地址列表。
您的位置编号属性应该成为标题,而不是类属性。
包装器/装饰器可以检查一些参数(例如,传入的位置编号消息头)并稀释由IEnumerable<Address>
的修饰实现返回的GetSubscriberAddressesForMessage
。
以下示例装饰RavenSubscriptionStorage
public class DecoratedSubscriptionStorage : ISubscriptionStorage
{
public RavenSubscriptionStorage Base { get; set; } //will be injected
void Subscribe(Address address, IEnumerable<MessageType> messageTypes)
{
Base.Subscribe(address, messageTypes);
}
void Unsubscribe(Address address, IEnumerable<MessageType> messageTypes)
{
Base.Unsubscribe(address, messageTypes);
}
IEnumerable<Address> GetSubscriberAddressesForMessage(
IEnumerable<MessageType> messageTypes)
{
var addresses = Base.GetSubscriberAddressesForMessage(messageTypes);
//your logic goes here
return addresses;
}
public void Init()
{
Base.Init();
}
}
但是,如果由DI框架/引导程序配置,您自己实现的此接口可以动态修饰NSB正在使用的任何实现。