From 7d98af3556eab669e0216677a982783b8d15a213 Mon Sep 17 00:00:00 2001 From: sanych-sun Date: Fri, 18 Oct 2024 00:27:58 -0700 Subject: [PATCH] DI extensions --- Directory.Build.props | 3 +- README.md | 5 +- RabbitMQ.Next.sln | 14 ++++++ .../Program.cs | 2 +- .../Program.cs | 21 ++++++++ ...Q.Next.Examples.DependencyInjection.csproj | 18 +++++++ .../Worker.cs | 28 +++++++++++ .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../ConnectionBuilderExtensions.cs | 6 +-- .../RabbitMQ.Next.DependencyInjection.csproj | 16 ++++++ .../ServiceCollectionExtensions.cs | 50 +++++++++++++++++++ src/RabbitMQ.Next.Publisher/Publisher.cs | 10 ++-- src/RabbitMQ.Next/Channels/Channel.cs | 5 ++ .../Consumer/ConsumerBenchmarks.cs | 2 +- .../Publisher/PublishNoConfirmBenchmarks.cs | 2 +- .../Publisher/PublisherBenchmarks.cs | 2 +- .../ConnectionBuilderExtensionsTests.cs | 8 +-- 21 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Program.cs create mode 100644 docs/examples/RabbitMQ.Next.Examples.DependencyInjection/RabbitMQ.Next.Examples.DependencyInjection.csproj create mode 100644 docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Worker.cs create mode 100644 src/RabbitMQ.Next.DependencyInjection/RabbitMQ.Next.DependencyInjection.csproj create mode 100644 src/RabbitMQ.Next.DependencyInjection/ServiceCollectionExtensions.cs diff --git a/Directory.Build.props b/Directory.Build.props index fe7cd8c..98636e9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,8 @@ 0.4.0 - net6.0 + net6.0 + latest latest Oleksandr Poliakov RabbitMQ;Messaging diff --git a/README.md b/README.md index ef02748..919279c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ The library was created with idea of the minimal possible allocation. Here is pe ## Packages Too much packages instead of a single... explained: - [RabbitMQ.Next](https://www.nuget.org/packages/RabbitMQ.Next), [RabbitMQ.Next.Abstractions](https://www.nuget.org/packages/RabbitMQ.Next.Abstractions) – core library, contains most of the protocol-level implementations +- [RabbitMQ.Next.DependencyInjection](https://www.nuget.org/packages/RabbitMQ.Next.DependencyInjection) - helper library for convenient registration of IConnection in DI container. - [RabbitMQ.Next.TopologyBuilder](https://www.nuget.org/packages/RabbitMQ.Next.TopologyBuilder), [RabbitMQ.Next.TopologyBuilder.Abstractions](https://www.nuget.org/packages/RabbitMQ.Next.TopologyBuilder.Abstractions) – library contains methods to manage exchanges, queues and bindings - [RabbitMQ.Next.Consumer](https://www.nuget.org/packages/RabbitMQ.Next.Consumer), [RabbitMQ.Next.Consumer.Abstractions](https://www.nuget.org/packages/RabbitMQ.Next.Consumer.Abstractions) – library provides high-level message consumption API - [RabbitMQ.Next.Publisher](https://www.nuget.org/packages/RabbitMQ.Next.Publisher), [RabbitMQ.Next.Publisher.Abstractions](https://www.nuget.org/packages/RabbitMQ.Next.Publisher.Abstractions) – provides high-level message publishing API @@ -47,8 +48,8 @@ First of all have to open the connection to RabbitMQ server: using RabbitMQ.Next; ... -var connection = await ConnectionBuilder.Default - .Endpoint("amqp://guest:password@localhost:5672/") +var connection = ConnectionBuilder.Default + .UseConnectionString("amqp://guest:password@localhost:5672/") .Build(); ``` diff --git a/RabbitMQ.Next.sln b/RabbitMQ.Next.sln index 2ba79f8..d5370ea 100644 --- a/RabbitMQ.Next.sln +++ b/RabbitMQ.Next.sln @@ -54,6 +54,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMQ.Next.Examples.Demo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMQ.Next.Examples.PublisherMiddleware", "docs\examples\RabbitMQ.Next.Examples.PublisherMiddleware\RabbitMQ.Next.Examples.PublisherMiddleware.csproj", "{23340CFA-F105-4F7F-B2C5-16693A8626F9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMQ.Next.DependencyInjection", "src\RabbitMQ.Next.DependencyInjection\RabbitMQ.Next.DependencyInjection.csproj", "{B4DC5006-903A-4E4F-A819-47CE0DDC3CF7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMQ.Next.Examples.DependencyInjection", "docs\examples\RabbitMQ.Next.Examples.DependencyInjection\RabbitMQ.Next.Examples.DependencyInjection.csproj", "{5841CCA0-850A-483D-841C-940967D619AD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -148,6 +152,14 @@ Global {23340CFA-F105-4F7F-B2C5-16693A8626F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {23340CFA-F105-4F7F-B2C5-16693A8626F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {23340CFA-F105-4F7F-B2C5-16693A8626F9}.Release|Any CPU.Build.0 = Release|Any CPU + {B4DC5006-903A-4E4F-A819-47CE0DDC3CF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4DC5006-903A-4E4F-A819-47CE0DDC3CF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4DC5006-903A-4E4F-A819-47CE0DDC3CF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4DC5006-903A-4E4F-A819-47CE0DDC3CF7}.Release|Any CPU.Build.0 = Release|Any CPU + {5841CCA0-850A-483D-841C-940967D619AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5841CCA0-850A-483D-841C-940967D619AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5841CCA0-850A-483D-841C-940967D619AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5841CCA0-850A-483D-841C-940967D619AD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {AB00A6F3-C3E0-4E61-B302-F9CF0288819E} = {C895EE24-13DF-45C3-AF1D-B6B6EE4A10A9} @@ -174,5 +186,7 @@ Global {B93A51A9-0A31-4419-B8E8-C304EF18CFAB} = {750B1D71-E43B-4714-8AA3-AB9B0EC7E931} {C0440748-9787-4891-874A-054F5E64020B} = {750B1D71-E43B-4714-8AA3-AB9B0EC7E931} {23340CFA-F105-4F7F-B2C5-16693A8626F9} = {750B1D71-E43B-4714-8AA3-AB9B0EC7E931} + {B4DC5006-903A-4E4F-A819-47CE0DDC3CF7} = {C895EE24-13DF-45C3-AF1D-B6B6EE4A10A9} + {5841CCA0-850A-483D-841C-940967D619AD} = {750B1D71-E43B-4714-8AA3-AB9B0EC7E931} EndGlobalSection EndGlobal diff --git a/docs/examples/RabbitMQ.Next.Examples.DemoSaslAuthMechanism/Program.cs b/docs/examples/RabbitMQ.Next.Examples.DemoSaslAuthMechanism/Program.cs index 0a8e20f..1f031db 100644 --- a/docs/examples/RabbitMQ.Next.Examples.DemoSaslAuthMechanism/Program.cs +++ b/docs/examples/RabbitMQ.Next.Examples.DemoSaslAuthMechanism/Program.cs @@ -5,7 +5,7 @@ Console.WriteLine("Hello World! Will try to connect RabbitMQ server with RABBIT-CR-DEMO auth mechanism."); await using var connection = ConnectionBuilder.Default - .Endpoint("amqp://localhost:5672/") + .UseConnectionString("amqp://localhost:5672/") .WithRabbitCrDemoAuth("guest", "guest") .Build(); diff --git a/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Program.cs b/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Program.cs new file mode 100644 index 0000000..57f0843 --- /dev/null +++ b/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Program.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using RabbitMQ.Next; +using RabbitMQ.Next.DependencyInjection; +using RabbitMQ.Next.Examples.DependencyInjection; +using RabbitMQ.Next.Serialization.PlainText; + +var builder = Host.CreateDefaultBuilder(args); + +builder.ConfigureServices(services => +{ + services.AddRabbitMQConnection( + builder => builder + .UseConnectionString("amqp://guest:guest@localhost:5672/") + .UsePlainTextSerializer()); + services.AddHostedService(); +}); + +using IHost host = builder.Build(); + +host.Run(); diff --git a/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/RabbitMQ.Next.Examples.DependencyInjection.csproj b/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/RabbitMQ.Next.Examples.DependencyInjection.csproj new file mode 100644 index 0000000..be2d9a7 --- /dev/null +++ b/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/RabbitMQ.Next.Examples.DependencyInjection.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + + + + + + + + + + + + + diff --git a/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Worker.cs b/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Worker.cs new file mode 100644 index 0000000..2b6b880 --- /dev/null +++ b/docs/examples/RabbitMQ.Next.Examples.DependencyInjection/Worker.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using RabbitMQ.Next.Publisher; + +namespace RabbitMQ.Next.Examples.DependencyInjection; + +public class Worker : BackgroundService +{ + private readonly IPublisher _publisher; + private readonly IHostLifetime _hostLifetime; + + public Worker(IConnection connection, IHostLifetime hostLifetime) + { + this._hostLifetime = hostLifetime; + this._publisher = connection.Publisher("amq.fanout"); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await this._publisher.PublishAsync("Hello World!", cancellation: stoppingToken); + Console.WriteLine("Message was published."); + Console.WriteLine("Press [Enter] key to exit..."); + Console.ReadLine(); + await this._hostLifetime.StopAsync(stoppingToken); + } +} diff --git a/docs/examples/RabbitMQ.Next.Examples.DynamicSerializer/Program.cs b/docs/examples/RabbitMQ.Next.Examples.DynamicSerializer/Program.cs index 91318e0..040855d 100644 --- a/docs/examples/RabbitMQ.Next.Examples.DynamicSerializer/Program.cs +++ b/docs/examples/RabbitMQ.Next.Examples.DynamicSerializer/Program.cs @@ -14,7 +14,7 @@ internal static class Program private static async Task Main() { await using var connection = ConnectionBuilder.Default - .Endpoint("amqp://test2:test2@localhost:5672/") + .UseConnectionString("amqp://guest:guest@localhost:5672/") .UseDynamicSerializer(serializer => serializer .When(m => "application/json".Equals(m.ContentType, StringComparison.InvariantCultureIgnoreCase)).UseSystemJsonSerializer() .When(m => "application/msgpack".Equals(m.ContentType, StringComparison.InvariantCultureIgnoreCase)).UseMessagePackSerializer() diff --git a/docs/examples/RabbitMQ.Next.Examples.PublisherMiddleware/Program.cs b/docs/examples/RabbitMQ.Next.Examples.PublisherMiddleware/Program.cs index 106cf24..e4682c8 100644 --- a/docs/examples/RabbitMQ.Next.Examples.PublisherMiddleware/Program.cs +++ b/docs/examples/RabbitMQ.Next.Examples.PublisherMiddleware/Program.cs @@ -5,7 +5,7 @@ Console.WriteLine("Hello World! This is publisher based on RabbitMQ.Next library."); var connection = ConnectionBuilder.Default - .Endpoint("amqp://guest:guest@localhost:5672/") + .UseConnectionString("amqp://guest:guest@localhost:5672/") .UsePlainTextSerializer() .Build(); diff --git a/docs/examples/RabbitMQ.Next.Examples.SimpleConsumer/Program.cs b/docs/examples/RabbitMQ.Next.Examples.SimpleConsumer/Program.cs index 6e4320b..2eab456 100644 --- a/docs/examples/RabbitMQ.Next.Examples.SimpleConsumer/Program.cs +++ b/docs/examples/RabbitMQ.Next.Examples.SimpleConsumer/Program.cs @@ -9,7 +9,7 @@ Console.WriteLine("Hello World! This is consumer based on RabbitMQ.Next library."); var connection = ConnectionBuilder.Default - .Endpoint("amqp://guest:guest@localhost:5672/") + .UseConnectionString("amqp://guest:guest@localhost:5672/") .UsePlainTextSerializer() .Build(); diff --git a/docs/examples/RabbitMQ.Next.Examples.SimplePublisher/Program.cs b/docs/examples/RabbitMQ.Next.Examples.SimplePublisher/Program.cs index 14a5bb4..1d8bc7b 100644 --- a/docs/examples/RabbitMQ.Next.Examples.SimplePublisher/Program.cs +++ b/docs/examples/RabbitMQ.Next.Examples.SimplePublisher/Program.cs @@ -6,7 +6,7 @@ Console.WriteLine("Hello World! This is publisher based on RabbitMQ.Next library."); var connection = ConnectionBuilder.Default - .Endpoint("amqp://guest:guest@localhost:5672/") + .UseConnectionString("amqp://guest:guest@localhost:5672/") .UsePlainTextSerializer() .Build(); diff --git a/docs/examples/RabbitMQ.Next.Examples.TopologyBuilder/Program.cs b/docs/examples/RabbitMQ.Next.Examples.TopologyBuilder/Program.cs index 2368767..a3acd2a 100644 --- a/docs/examples/RabbitMQ.Next.Examples.TopologyBuilder/Program.cs +++ b/docs/examples/RabbitMQ.Next.Examples.TopologyBuilder/Program.cs @@ -5,7 +5,7 @@ Console.WriteLine("Hello World! This is topology builder based on RabbitMQ.Next library."); var connection = ConnectionBuilder.Default - .Endpoint("amqp://guest:guest@localhost:5672/") + .UseConnectionString("amqp://guest:guest@localhost:5672/") .Build(); await connection.ConfigureAsync(async topology => diff --git a/src/RabbitMQ.Next.Abstractions/ConnectionBuilderExtensions.cs b/src/RabbitMQ.Next.Abstractions/ConnectionBuilderExtensions.cs index 8cd38d1..3f1b0d1 100644 --- a/src/RabbitMQ.Next.Abstractions/ConnectionBuilderExtensions.cs +++ b/src/RabbitMQ.Next.Abstractions/ConnectionBuilderExtensions.cs @@ -18,18 +18,18 @@ public static IConnectionBuilder PlainAuth(this IConnectionBuilder builder, stri return builder; } - public static IConnectionBuilder Endpoint(this IConnectionBuilder builder, string endpoint) + public static IConnectionBuilder UseConnectionString(this IConnectionBuilder builder, string endpoint) { endpoint = WebUtility.UrlDecode(endpoint); if (Uri.TryCreate(endpoint, UriKind.Absolute, out var uri)) { - return builder.Endpoint(uri); + return builder.UseConnectionString(uri); } throw new ArgumentException("Cannot parse endpoint as Uri.",nameof(endpoint)); } - public static IConnectionBuilder Endpoint(this IConnectionBuilder builder, Uri endpoint) + public static IConnectionBuilder UseConnectionString(this IConnectionBuilder builder, Uri endpoint) { var parsed = ParseAmqpUri(endpoint); diff --git a/src/RabbitMQ.Next.DependencyInjection/RabbitMQ.Next.DependencyInjection.csproj b/src/RabbitMQ.Next.DependencyInjection/RabbitMQ.Next.DependencyInjection.csproj new file mode 100644 index 0000000..f1a7d2f --- /dev/null +++ b/src/RabbitMQ.Next.DependencyInjection/RabbitMQ.Next.DependencyInjection.csproj @@ -0,0 +1,16 @@ + + + + net6.0;net8.0 + + + + + + + + + + + + diff --git a/src/RabbitMQ.Next.DependencyInjection/ServiceCollectionExtensions.cs b/src/RabbitMQ.Next.DependencyInjection/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..8354476 --- /dev/null +++ b/src/RabbitMQ.Next.DependencyInjection/ServiceCollectionExtensions.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace RabbitMQ.Next.DependencyInjection; + +public static class ServiceCollectionExtensions +{ +#if NET8_0_OR_GREATER + public static IServiceCollection AddRabbitMQConnection( + this IServiceCollection serviceCollection, + Action connectionBuilder, + ServiceLifetime lifetime = ServiceLifetime.Singleton, + object serviceKey = null) + { + serviceCollection.TryAdd( + new ServiceDescriptor( + typeof(IConnection), + serviceKey, + (sp, _) => + { + var builder = ConnectionBuilder.Default; + connectionBuilder(builder); + return builder.Build(); + }, + lifetime)); + + return serviceCollection; + } +#else + public static IServiceCollection AddRabbitMQConnection( + this IServiceCollection serviceCollection, + Action connectionBuilder, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + serviceCollection.TryAdd( + new ServiceDescriptor( + typeof(IConnection), + sp => + { + var builder = ConnectionBuilder.Default; + connectionBuilder(builder); + return builder.Build(); + }, + lifetime)); + + return serviceCollection; + } +#endif +} diff --git a/src/RabbitMQ.Next.Publisher/Publisher.cs b/src/RabbitMQ.Next.Publisher/Publisher.cs index 1c6c214..f8ed60b 100644 --- a/src/RabbitMQ.Next.Publisher/Publisher.cs +++ b/src/RabbitMQ.Next.Publisher/Publisher.cs @@ -130,8 +130,6 @@ private async Task InternalPublishAsync(IMessageBuilder message, TCont } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckDisposed() { @@ -185,7 +183,13 @@ private async ValueTask InitializeChannelAsync() { if (ch != null) { - await ch.CloseAsync().ConfigureAwait(false); + try + { + await ch.CloseAsync().ConfigureAwait(false); + } + catch (Exception e) + { + } } throw; diff --git a/src/RabbitMQ.Next/Channels/Channel.cs b/src/RabbitMQ.Next/Channels/Channel.cs index ac96462..7dec71d 100644 --- a/src/RabbitMQ.Next/Channels/Channel.cs +++ b/src/RabbitMQ.Next/Channels/Channel.cs @@ -104,6 +104,11 @@ public async ValueTask PublishAsync( CancellationToken cancellation = default) { this.ValidateState(); + + if (this.serializer == null) + { + throw new InvalidOperationException("Cannot publish content when serializer was not set. Make sure serializer is configured correctly."); + } var publishMethod = new PublishMethod(exchange, routingKey, (byte)flags); diff --git a/tests/RabbitMQ.Next.Benchmarks/Consumer/ConsumerBenchmarks.cs b/tests/RabbitMQ.Next.Benchmarks/Consumer/ConsumerBenchmarks.cs index 0c029cd..105150f 100644 --- a/tests/RabbitMQ.Next.Benchmarks/Consumer/ConsumerBenchmarks.cs +++ b/tests/RabbitMQ.Next.Benchmarks/Consumer/ConsumerBenchmarks.cs @@ -26,7 +26,7 @@ public class ConsumerBenchmarks public async Task Setup() { this.connection = ConnectionBuilder.Default - .Endpoint(Helper.RabbitMqConnection) + .UseConnectionString(Helper.RabbitMqConnection) .UsePlainTextSerializer() .Build(); diff --git a/tests/RabbitMQ.Next.Benchmarks/Publisher/PublishNoConfirmBenchmarks.cs b/tests/RabbitMQ.Next.Benchmarks/Publisher/PublishNoConfirmBenchmarks.cs index 8f47ab1..98b8238 100644 --- a/tests/RabbitMQ.Next.Benchmarks/Publisher/PublishNoConfirmBenchmarks.cs +++ b/tests/RabbitMQ.Next.Benchmarks/Publisher/PublishNoConfirmBenchmarks.cs @@ -18,7 +18,7 @@ public class PublishNoConfirmBenchmarks public PublishNoConfirmBenchmarks() { this.connection = ConnectionBuilder.Default - .Endpoint(Helper.RabbitMqConnection) + .UseConnectionString(Helper.RabbitMqConnection) .UsePlainTextSerializer() .Build(); diff --git a/tests/RabbitMQ.Next.Benchmarks/Publisher/PublisherBenchmarks.cs b/tests/RabbitMQ.Next.Benchmarks/Publisher/PublisherBenchmarks.cs index bc46873..1745ff9 100644 --- a/tests/RabbitMQ.Next.Benchmarks/Publisher/PublisherBenchmarks.cs +++ b/tests/RabbitMQ.Next.Benchmarks/Publisher/PublisherBenchmarks.cs @@ -24,7 +24,7 @@ public PublisherBenchmarks() this.theirConnection = factory.CreateConnection(); this.connection = ConnectionBuilder.Default - .Endpoint(Helper.RabbitMqConnection) + .UseConnectionString(Helper.RabbitMqConnection) .UsePlainTextSerializer() .Build(); } diff --git a/tests/RabbitMQ.Next.Tests/Abstractions/ConnectionBuilderExtensionsTests.cs b/tests/RabbitMQ.Next.Tests/Abstractions/ConnectionBuilderExtensionsTests.cs index 10236fc..fc7e104 100644 --- a/tests/RabbitMQ.Next.Tests/Abstractions/ConnectionBuilderExtensionsTests.cs +++ b/tests/RabbitMQ.Next.Tests/Abstractions/ConnectionBuilderExtensionsTests.cs @@ -25,7 +25,7 @@ public void AddEndpointStringThrowsOnNonUri() { var builder = Substitute.For(); - Assert.Throws(() => builder.Endpoint("some random text")); + Assert.Throws(() => builder.UseConnectionString("some random text")); } [Fact] @@ -33,7 +33,7 @@ public void AddEndpointStringThrowsOnNoAmqp() { var builder = Substitute.For(); - Assert.Throws(() => builder.Endpoint("http://rabbitmq.com")); + Assert.Throws(() => builder.UseConnectionString("http://rabbitmq.com")); } [Fact] @@ -41,7 +41,7 @@ public void AddEndpointUriThrowsOnNoAmqp() { var builder = Substitute.For(); - Assert.Throws(() => builder.Endpoint(new Uri("http://rabbitmq.com"))); + Assert.Throws(() => builder.UseConnectionString(new Uri("http://rabbitmq.com"))); } [Theory] @@ -50,7 +50,7 @@ public void AddEndpointCanParseValidUri(string endpoint, bool ssl, string host, { var builder = Substitute.For(); - builder.Endpoint(endpoint); + builder.UseConnectionString(endpoint); builder.Received().Endpoint(host, port, ssl); builder.Received().VirtualHost(vhost);