Closed mookid8000 closed 6 years ago
PS: I started out with the idea of having the string GetTopic(object eventMessage)
signature on the interface too, because I thought it would be neat to have to ability to maybe look at certain messages and come up with topics from their contents.
But then I realized that what we're abstracting away here is actually JUST the type-to-topic-name logic, and nothing else.
More advanced things can still easily be had by taking advantage of the raw topics API.
What about write a method in OptionsConfigurer
class? Something like this:
.Options(options =>
{
options.SetTopicNameConvention(new MyConvention());
})
Or
.Options(options =>
{
options.SetTopicNameConvention(topic => topic.MyExtensionMethod());
})
To begin with, I think it should simply live its life quiet, like many of the other Rebus extensibility points, most of which are configured in the Start
method of RebusConfigurer
(using the PossiblyRegisterDefault
to make a registration unless someone already made one).
"The Rebus Way" then, would be, e.g. for you @heberop – as a developer wanting to use this extensibility point – to create an appropriate extension method on OptionsConfigurer
in your own code, possibly looking like this:
public static class ShortAndReadableTopicsRebusConfigurationExtensions
{
public static void UseShortTopicNames(this OptionsConfigurer configurer)
{
configurer.Register<ITopicNameConvention>(c => new ShortTopicNamesConvention());
}
class ShortTopicNamesConvention : ITopicNameConvention
{
public string GetTopic(Type type) => type.Name;
}
}
which would then make your configuration code look neat like this:
Configure.With(...)
.(...)
.Options(o => {
o.UseShortTopicNames();
})
.Start();
This way we can avoid trying to predict how it's going to be used.
What do you think about this?
I like it ;) I will work on it and send you a pull request ;)
Excellent – I'm looking forward to it 👍
Fixed by #693 which is out in Rebus 5.0.0-b09
Working with AzureServiceBus, and using the ITopicnameConvention and for some reason my topic names have some "cleanup" after the GetTopic() method. They all end up in lowercase and '.' are replaced with '_' Am I missing something? I'm using the namespace and other combination of things to create topic names and I want them to be something like MyDomain.Events and I'm getting mydomain_evetns
Which version of Rebus.AzureServiceBus are you using?
Some changes were made in the latest version that makes it possible to change names to anything you feel like. From version 7 (which is only out as a prerelease right now) the default should be to allow '.' and all other special characters, which Rebus.AzureServiceBus used to "sanitize" away.
Latest stable one. I will give a try the prerelease version. Basically I want rebus to work with an existing established queue/topic convention that might be created for some messages, but might be required to be create by rebus in some cases. There is an existing in-house service bus implementation and I doing a POC to see how much I can make rebus compatible with it (hopefully 110% since there are features in rebus I want to use not available at the moment in-house)
Ok, upgrading to 7.0.0-a16 created an immediate issue with Autofac and the registration (which is working on the latest stable version).
Also tried updating all the packages to the latest pre release versions, and the same error.
Rebus stable + autofac stable + azure prerelease = error Rebus prerelease + autofac prerelease + azure prerelease = error
Should I create a Tikcet on the Rebus.AzureServiceBus repository?
Is it a compile error?
Runnning, I uploaded the example to this repo RebusAzureNamingTest
Could you show me the full stack trace or another description of the error?
Unhandled Exception: Autofac.Core.DependencyResolutionException: An error occurred while attempting to automatically activate registration 'Activator = AutofacHandlerActivator (ProvidedInstanceActivator), Services = [Rebus.Autofac.AutofacHandlerActivator, AutoActivate], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope'. See the inner exception for information on the source of the failure. ---> An exception was thrown while executing a resolve operation. See the InnerException for details. ---> Could not start Rebus (See inner exception for details.) (See inner exception for details.) ---> Autofac.Core.DependencyResolutionException: An exception was thrown while executing a resolve operation. See the InnerException for details. ---> Could not start Rebus (See inner exception for details.) ---> Rebus.Exceptions.RebusConfigurationException: Could not start Rebus ---> Autofac.Core.DependencyResolutionException: An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = IBus (DelegateActivator), Services = [Rebus.Bus.IBus], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope ---> Attempted to register primary -> Rebus.Topic.ITopicNameConvention, but a primary registration already exists: primary -> Rebus.Topic.ITopicNameConvention (See inner exception for details.) ---> System.InvalidOperationException: Attempted to register primary -> Rebus.Topic.ITopicNameConvention, but a primary registration already exists: primary -> Rebus.Topic.ITopicNameConvention
at Rebus.Injection.Injectionist.Register[TService](Func`2 resolverMethod, Boolean isDecorator, String description) in C:\projects-rebus\Rebus\Rebus\Injection\Injectionist.cs:line 121
at Rebus.Injection.Injectionist.Register[TService](Func`2 resolverMethod, String description) in C:\projects-rebus\Rebus\Rebus\Injection\Injectionist.cs:line 74
at RebusAzureNamingTest.RebusConfigurationExtensions.MyTopics(OptionsConfigurer configurer) in C:\Users\Dario\source\repos\RebusAzureNamingTest\RebusAzureNamingTest\RebusConfigurationExtensions.cs:line 12
at RebusAzureNamingTest.Program.<>c.<Main>b__0_2(OptionsConfigurer x) in C:\Users\Dario\source\repos\RebusAzureNamingTest\RebusAzureNamingTest\Program.cs:line 25
at Rebus.Config.RebusConfigurer.Options(Action`1 configurer) in C:\projects-rebus\Rebus\Rebus\Config\RebusConfigurer.cs:line 140
at RebusAzureNamingTest.Program.<>c.<Main>b__0_0(RebusConfigurer configurer, IComponentContext context) in C:\Users\Dario\source\repos\RebusAzureNamingTest\RebusAzureNamingTest\Program.cs:line 21
at Rebus.Config.ContainerBuilderExtensions.<>c__DisplayClass1_0.<RegisterRebus>b__0(RebusConfigurer configurer, IComponentContext context)
at Rebus.Autofac.AutofacHandlerActivator.<>c__DisplayClass3_0.<.ctor>b__1(IComponentContext context)
at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.<ForDelegate>b__0(IComponentContext c, IEnumerable`1 p)
at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
--- End of inner exception stack trace ---
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters)
at Rebus.Autofac.AutofacHandlerActivator.<>c.<.ctor>b__3_0(IActivatedEventArgs`1 e)
--- End of inner exception stack trace ---
at Rebus.Autofac.AutofacHandlerActivator.<>c.<.ctor>b__3_0(IActivatedEventArgs`1 e)
at Autofac.Core.Resolving.InstanceLookup.Complete()
at Autofac.Core.Resolving.ResolveOperation.CompleteActivations()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
--- End of inner exception stack trace ---
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ContainerBuilder.StartStartableComponents(IComponentContext componentContext)
--- End of inner exception stack trace ---
at Autofac.ContainerBuilder.StartStartableComponents(IComponentContext componentContext)
at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)
at RebusAzureNamingTest.Program.Main(String[] args) in C:\Users\Dario\source\repos\RebusAzureNamingTest\RebusAzureNamingTest\Program.cs:line 30
This is the bus configuration and start code
static void Main(string[] args)
{
var containerBuilder = new ContainerBuilder();
const string connString = "";
containerBuilder.RegisterType<DepartmentUpdatedHandler>().AsImplementedInterfaces();
containerBuilder.RegisterRebus((configurer, context) =>
{
return configurer
.Transport(t => t.UseAzureServiceBus(connString, "RebusAzureNamingTest"))
.Options(x => x.MyTopics())
.Routing(c => c.MyRouting());
});
using (var container = containerBuilder.Build())
using (var scope = container.BeginLifetimeScope())
{
scope.Resolve<IBus>().Subscribe<DepartmentUpdated>().GetAwaiter().GetResult();
Thread.Sleep(TimeSpan.FromSeconds(10));
}
}
This is the method to register the convention
public static void MyTopics(this OptionsConfigurer configurer)
{
configurer.Register<ITopicNameConvention>(c => new MyTopicNamesConvention());
}
Ok, the error reads
System.InvalidOperationException: Attempted to register
primary -> Rebus.Topic.ITopicNameConvention, but a primary registration already exists:
primary -> Rebus.Topic.ITopicNameConvention
and the line
at RebusAzureNamingTest.RebusConfigurationExtensions.MyTopics(OptionsConfigurer configurer) in C:\Users\Dario\source\repos\RebusAzureNamingTest\RebusAzureNamingTest\RebusConfigurationExtensions.cs:line 12
from the stacktrace reveals that MyTopics
was where the registration was made.
I'm afraid this is because Rebus.AzureServiceBus makes its own registration of ITopicNameConvention
.
Therefore you need to add your registration as a decorator, which means that you get to intercept all calls to it.
So, if you change your MyTopics
implementation to
public static void MyTopics(this OptionsConfigurer configurer)
{
configurer.Decorate<ITopicNameConvention>(c => new MyTopicNamesConvention());
}
then you should be good. 🙂
Works perfect!
in the form of a
ITopicNameConvention
service, most likely looking like this:This way, it can be used by Rebus when subscribing/unsubscribing to types:
and when publishing messages
Thoughts?