Hi, I’d need to resolve the connection string to be used by outbox feature. We need to provide support for multi-tenant system that provides the client tokens in every incoming message. All the logic to retrieve a client’s connection string is in a service called IClientConnectionService. Is it possible to inject this service in the buildConnectionFromTenantData callback?
Link: Multi-tenant support • Sql Persistence • Particular Docs
var persistence = endpointConfiguration.UsePersistence();
persistence.MultiTenantConnectionBuilder(tenantIdHeaderName: “TenantHeaderName”,
buildConnectionFromTenantData: tenantId =>
{
resolve IClientConnectionService service;
var connection = service.Get(tenantId);
return new SqlConnection(connection);
});
Is the IClientConnectionService
service used by any of the message handlers? Why does it need to be in the NServiceBus container?
You could create an instance of the service outside of NServiceBus and call it directly from inside the factory method.
i.e.
var service = new ClientConnectionService<SqlPersistence>();
var persistence = endpointConfiguration.UsePersistence();
persistence.MultiTenantConnectionBuilder(
tenantIdHeaderName: "TenantHeaderName",
buildConnectionFromTenantData: tenantId => new SqlConnection(service.Get(tenantId))
);
The dependent service has other dependencies. If I need to manually construct the service dependency using new (), I have to pass all the dependencies where each of the dependent classes can have further more dependencies. The same problem that is solved by DI containers.
If I understand your initial code snippet correctly, the IClientConnectionService
is registered once and then decides what connection string to return based on the “tenant id” provided to the IClientConnectionService.Get(string tenantId)
method.
If my understanding is correct, what’s the problem with registering the IClientConnectionService
implementation in the container, considering that the connection string resolution logic is inside the Get
method?
I might be missing something obvious, in which case, forgive me.
Hi @mauroservienti,
I am revisiting a question from a few years ago regarding the configuration of a multi-tenant service using NServiceBus (NSB). Specifically, I am working with the following method:
public static void MultiTenantConnectionBuilder(this PersistenceExtensions configuration, Func<IncomingMessage, string> captureTenantId, Func<string, DbConnection> buildConnectionFromTenantData)
In this setup, the delegates captureTenantId
and buildConnectionFromTenantData
are invoked for each incoming message. I am considering whether it would be advantageous to enable these delegates to resolve dependencies within the scope of an incoming message.
I am curious if this approach would offer any benefits, such as improved resource management or efficiency, by scoping dependencies to the lifecycle of a message. Your expertise and insights on this matter would be greatly appreciated, especially regarding any best practices or potential pitfalls in a message-driven, multi-tenant architecture.
Thank you for your time and guidance.
Thanks for the detailed response, @syaragat.
I believe that what you’d envision is something like:
var persistence = endpointConfiguration.UsePersistence<SqlPersistence>();
persistence.MultiTenantConnectionBuilder( (scopedServiceProvider, incomingMessage) =>
{
// the scopedServiceProvider will be scoped based on the incoming message
});
I don’t think that’s a big deal, we should have the scopedServiceProvider
available internally when invoking the user delegate.
The problem, though, is what you can do with the scoped service.
To be able to resolve scoped dependencies, they must be resolved in the scope, but to resolve the dependency for a specific tenant, the tenant ID is still required, forcing you into the direction of a factory that accepts the tenant ID and decides how to resolve the dependencies.
Another option, with the latest releases of .NET, is to use keyed services. You could be registering a dependency for each one of the tenants. And then resolve the one you need based on the tenant ID.
Thanks for your response. I agree that we may not need dependencies scoped to each message. Instead, we could use dependencies that resolve a connection string based on the message’s tenant ID, and these dependencies can be resolved in a singleton scope.
This resolves one issue but brings up another question I posted in the community forum: Configuring NSB in .NET 8 using Generic Host Model and Web Hosting Model.
The need to resolve dependencies to create an EndpointConfiguration
instance means I have to build the service collection to get a service provider before I can call IHostApplicationBuilder.UseNServiceBus(endpointConfiguration)
. However, when IHostApplicationBuilder.Build()
is called, it will create another service provider for the .NET Core runtime to use.
I would appreciate hearing from the community on best practices for creating hosting endpoints in .NET 8.