Use factory to resolve dependencies - c#

We have a websolution with autofac. Now we want to reuse things in a windows service/console app where things are only available when a message comes in from an enterprise bus.
I have the following service to reuse
public SettingsService : ISettingsService {
public readonly ITenantIdentifer _tenantIdentifier
public SettingsService (ITenantIdentifier tenantIdentifier) {
this._tenantIdentifier =tenantIdentifier;
}
// do other stuff
}
The current working setup
the ITenantIdentifier for the webcontext is simply registered for the webapplication using builder.RegisterType<WebTenantIdentifier>().As<ITenantIdentifier>();.
Evething works fine.
Our enterprise bus
The enterprise bus can not resolve the ITenantIdentifier until the message is available. So we created a MessageTenantIdentifier and registered a factory.
public class MessageTenantIdentifier : ITenantIdentifier
{
public delegate MessageTenantIdentifier Factory(int tenantId);
public MessageTenantIdentifier(int tenantId, IOtherDependency stuff)
{
_tenantId = tenantId;
// ...
}
}
// somewhere else the this is registered
builder.RegisterType<MessageTenantIdentifier >().As<ITenantIdentifier>().AsSelf();
builder.RegisterGeneratedFactory<MessageTenantIdentifier.Factory>();
The problem
The factory can only be used when the message is being handled in a
public class MsgTypeHandler : IHandleMessages<MsgType>
{
public MsgTypeHandler(ISettingsService settingsService, MessageTenantIdentifier factory) { ...}
public async Task Handle(MsgType message)
{
var tenantId = message.TenantId;
// THIS IS THE MOMENT I CAN CONFIGURE THE MessageTenantIdentifier
var tenantIdentifier = factory.Invoke(tenantId);
// but this factory is not used against the ISettingsService. The service to be reused. <== THE REAL PROBLEM
}
}
The question
So, how can I solve this issue? E.g. how should I setup the registration of the MessageTenantIdentifier in the servicebus?
Or is my dependency setup just plain wrong?

If the MsgTypeHandler class needs an ISettingsService, but the entire object graph can't be resolved until the tenant ID is available, that means that the MsgTypeHandler is the Composition Root. That's OK, but that means that this is where you resolve your entire object graph, so don't inject individual services here; instead, inject the factory you need:
public class MsgTypeHandler : IHandleMessages<MsgType>
{
public MsgTypeHandler(ISettingsServiceFactory factory) {...}
public async Task Handle(MsgType message)
{
var tenantId = message.TenantId;
ISettingsService svc = this.factory.Create(tenantId);
// User svc here...
}
}

Related

How to inject dependencies from IHostedService before creating scope

I have a multi tenant system with background job. The tenancy details are stored in database and based on the tenant adding request in service bus, I want to resolve the dependencies based on tenant.
For this I would have to add dependencies to service collection before creating scope. When trying to inject IServiceCollection, it gives me error.
I am looking for the best way to inject dependencies from HostedService
public async Task MessageHandler(object sender, Message message)
{
// Inject dependencies
services.AddScoped<IMyService,Myservice>(); // No way to get services here
using (var scope = serviceProvider.CreateScope())
{
var ... = scope.ServiceProvider.GetService<...>();
//...
}
}
I had a similar need a while back. I created my own service bus handler.
You could try something like the below, where you inject a service (here as an example I'm using IMessageService) to the ServiceeBusHandler that itself has a dbcontext injected.
Then where ever you implement IServiceBusHandler you can specify for which tenant (and their queues) you want the connection built.
public class ServiceBusHandler : IServiceBusHandler
{
private readonly ServiceBusSender _serviceBusSender;
private readonly IMessageService _messageService;
public ServiceBusHandler(
ServiceBusSender serviceBusSender,
IMessageService messageService)
{
_serviceBusSender = serviceBusSender;
_messageService = messageService;
}
public async Task PublishMessageAsync<T>(T message)
{
var jsonString = JsonConvert.SerializeObject(message);
var serviceBusMessage = new ServiceBusMessage(jsonString);
await _serviceBusSender.SendMessageAsync(serviceBusMessage);
}
internal static IServiceBusHandler Create(ServiceBusSender sender)
{
return new ServiceBusHandler(sender);
}
}
public class ServiceBusHandlerFactory : IServiceBusHandlerFactory
{
private readonly IAzureClientFactory<ServiceBusClient> _serviceBusClientFactory;
public ServiceBusHandlerFactory(
IAzureClientFactory<ServiceBusClient> serviceBusClientFactory)
{
_serviceBusClientFactory = serviceBusClientFactory;
}
public IServiceBusHandler GetClient(string tenantId)
{
var tenantDetails = _messageService.GetTenantDetails(tenantId); // Call to your DB to get details about the Tenant
var client = GetServiceBusClient(tenantDetails.QueueName);
var sender = client.CreateSender(tenantDetails.QueueName);
return ServiceBusHandler.Create(sender);
}
protected virtual ServiceBusClient GetServiceBusClient(string queueName)
{
var client = _serviceBusClientFactory.CreateClient(queueName);
return client;
}
}
What you are trying to achieve is to change the set of registrations after the Container was built. MS.DI does not support this, and while historically, more mature DI Containers tended to support this behavior, most modern DI Containers stopped supporting this, because there are too many negative consequences in allowing this. Autofac, for instance, obsoleted its Update method in 2016 and described the issues with updating the Container in details. Ninject has gone through a similar process, although development stopped before the final release that removed the possibility to update the Container. The Simple Injector DI Container never supported updating, and its documentation has some clear texts that describe what the issue is.
You might find a DI Container that supports this, but I would urge you to abbondon this path, because of the negative consequences that it can (and probably will) cause, as the previous links described.
Instead, you will have to find a different way to get tenant-specific behavior, with one single set of registrations. The trick here, typically lies in creating a Proxy implementation of your IMyService that can forward the call to the correct tenant implementation.
This might look something like this:
public class ProxyMyService : IMyService
{
public IMyService Service { get; set; }
// IMyService methods
public void SomeMethod() => this.Service.SomeMethod();
}
This proxy class can be registered at startup, together with other IMyService implementations, as follows:
services.AddScoped<IMyService, ProxyMyService>();
services.AddTransient<MyServiceTenant1>();
services.AddTransient<DefaultMyServiceTenant>();
With this, your hosted service can become the following:
private ProxyMyService service;
public MyHostedService(IMyService service)
{
this.service = (ProxyMyService)service;
}
public async Task MessageHandler(object sender, Message message)
{
using (var scope = serviceProvider.CreateScope())
{
var p = scope.ServiceProvider;
var proxy = (ProxyMyService)p.GetRequiredService<IMyService>();
proxy.Service = IsTentant1
? p.GetRequiredService<MyServiceTenant1>()
: p.GetRequiredService<DefaultMyServiceTenant>();
var ... = p.GetRequiredService<...>();
//...
}
}
A more evolved solution would entail a Proxy implementation that allows to switch between tenant-specific implementations internally. That would likely mean moving part of the logic that's currently inside MessageHandler into the ProxyMyService.
Do notice that the solutions I suggested do not require an abstract factory. Abstract factories are typically not needed.

How do I use Dependency Injection in C# .NET 6 application to pass in different instances of the same object with same interface?

I am creating an Azure Function App in Visual Studio with C# and .NET 6.
I have a service I created (CosmosDBService) that implements the interface ICosmosDBService:
public class CosmosDbService : ICosmosDbService
{
private Container? _container = null;
public CosmosDbService(
CosmosClient cosmosDbClient,
string databaseName,
string containerName)
{
_container = cosmosDbClient.GetContainer(databaseName, containerName);
}
I want to pass two different instances of this service into the Function App. Each service instance would represent a different container.
How would I set this up in Startup:FunctionsApp class using the FunctionsHostBuilder?
Default DI container does not support named such scenarios so you have next options - either create separate interfaces and implementations (and register/resolve them) for each databaseName-containerName pair or create a factory and use it to generate desired CosmosDbService instance:
public interface ICosmosDbServiceFactory
{
ICosmosDbService Create(string databaseName, string containerName);
}
class CosmosDbServiceFactory : ICosmosDbServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public CosmosDbServiceFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ICosmosDbService Create(string databaseName, string containerName) => new CosmosDbService(
_serviceProvider.GetRequiredService<CosmosClient>(),
databaseName,
containerName
);
}
Register it with appropriate lifetime and inject it into corresponding class and use it in the constructor to resolve required ICosmosDbService instances.
You can do this, but I wouldn't recommend it. For instance in your start up if you had the following code:
services.AddSingleton<ICosmosDbService, CosmosDbService>();
services.AddSingleton<ICosmosDbService, OtherCosmosDbService>();
both instances would be registered in the Di container. If you had a class that depends on this interface, the following constructor would result in OtherCosmosDbService being injected:
public class SomeClass {
private readonly ICosmosDbService _service;
public SomeClass(ICosmosDbService service){
_service = service; // This would be OtherCosmosDbService
}
}
Both would be registered and in this instance, the last one registered "wins". If you wanted to get both then you could change the constructor to this:
public SomeClass(IEnumerable<ICosmosDbService> services){
// Write logic to handle finding which one you want
}
Honestly, I would go with Guru Stron's suggestion of creating separate interfaces for each container and registering them separately.
I think your design needs to be more granular - trying to access multiple containers (possibly in multiple COSMOS databases) with one interface/class goes against the SOLID principle of single responsibilty. Think about your domain and work from there 'downwards'
public interface ICustomers
{
public IEnumerable<Customer> GetCustomers();
}
public interface IProducts
{
public IEnumerable<Product> GetProducts();
}
public class CustomersInCosmosDatabase : ICustomers
{
private readonly CosmosClient cosmosClient;
public CustomersInCosmosDatabase(CosmosClient cosmosClient)
{
this.cosmosClient = cosmosClient;
}
public IEnumerable<Customer> GetCustomers()
{
var container = cosmosClient.GetContainer("customerDatabaseId", "customerContainerId");
return container.GetItemLinqQueryable<Customer>();
}
}
public class ProductsInCosmosDatabase : IProducts
{
private readonly CosmosClient cosmosClient;
public ProductsInCosmosDatabase(CosmosClient cosmosClient)
{
this.cosmosClient = cosmosClient;
}
public IEnumerable<Product> GetProducts()
{
var container = cosmosClient.GetContainer("productDatabaseId", "prodcutContainerId");
return container.GetItemLinqQueryable<Product>();
}
}
and your registrations become:
serviceCollection.AddAzureClients(clientBuilder =>
{
clientBuilder.AddClient<CosmosClient, CosmosClientOptions>((o) =>
new CosmosClient("connectionString", new DefaultAzureCredential()));
});
serviceCollection.AddTransient<ICustomers, CustomersInCosmosDatabase>();
serviceCollection.AddTransient<IProducts, ProductsInCosmosDatabase>();
You are then in the business of injecting Customer collections and Product collections everywhere NOT CosmosDBServices.

How to handle event invocation between injected classes in .NET Core Console App

I've a .Net Core(3.1) Console App, that has 2 service classes, one has an event and other listens to it with a handler to that event. I've setup getting the DI containers but the event field is always null, so not able to call its Invoke(). Any pointers on what am I missing in setting up the services in ConfigureServices() that involves event handling. Below is the complete test code:
public class RefreshEventArgs : EventArgs
{
public string RefreshEventData { get; set; }
}
public interface INotifierService
{
event EventHandler<RefreshEventArgs> RefreshEventHandler;
}
public class NotifierService : INotifierService
{
public event EventHandler<RefreshEventArgs> RefreshEventHandler;
public RefreshEventArgs RefreshEventData { get; set; }
// GeneralAppSettings is a POCO class to read all appsettings.json key values.
private readonly IOptions<GeneralAppSettings> myAppSettings;
public NotifierService(IOptions<GeneralAppSettings> appSettings)
{
myAppSettings = appSettings;
}
public void RunInvokingRefreshEvent()
{
RefreshEventData = new RefreshEventArgs();
RefreshEventData.RefreshEventData = "somedata";
// Main problem! In the below line, RefreshEventHandler is null all the time
RefreshEventHandler?.Invoke(this, RefreshEventData);
}
public void SomeBackgroundThreadMonitorsExternalEvents()
{
// Some external events triggers below method
RunInvokingRefreshEvent();
}
}
Refresh Service
public interface IRefreshService
{
void Refresh(RefreshEventArgs eventData = null);
}
public class RefresherService : IRefreshService
{
private readonly IOptions<GeneralAppSettings> myAppSettings;
private readonly INotifierService notify;
public RefresherService(IOptions<GeneralAppSettings> _appSettings, INotifierService _notifyService)
{
myAppSettings = _appSettings;
notify = _notifyService;
notify.RefreshEventHandler += _notify_RefreshEventHandler;
}
private void _notify_RefreshEventHandler(object sender, RefreshEventArgs e)
{
// Call Refresh() based say based on a config value from myAppSettings
Refresh(e);
}
public void Refresh(RefreshEventArgs eventData = null)
{
// final business logic processing based on eventData
}
}
public class GeneralAppSettings // POCO
{
public string SomeConfigKeyInAppSettingsJson { get; set; }
}
Program
class Program
{
public static IConfiguration Configuration { get; set; }
static void Main(string[] args)
{
// read appsettings
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
// Host builder, setting up container
var host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddConfiguration(Configuration);
})
.ConfigureServices((context, services) =>
{
services.Configure<GeneralAppSettings>(Configuration.GetSection("GeneralAppSettings"));
services.AddSingleton<INotifierService, NotifierService>();
services.AddSingleton<IRefreshService, RefresherService>();
})
.Build();
// Need to get NotifierService instance to run some initial logic, so using ActivatorUtilities
var svc = ActivatorUtilities.GetServiceOrCreateInstance<NotifierService>(host.Services);
svc.SomeBackgroundThreadMonitorsExternalEvents();
// Need to get RefresherService instance to have initial Refresh logic so using ActivatorUtilities
var refresh = ActivatorUtilities.GetServiceOrCreateInstance<RefresherService>(host.Services);
refresh.Refresh(null);
// need to keep this main thread alive
Thread.Sleep(Timeout.Infinite);
}
}
When you request something from the DI container, you must request the "Service" type (the interface or first/only generic argument). If you request a type that's not been registered and you use ActivatorUtilities, it will create an instance if and only if all the types required to construct it are available. What's happening to you is you are getting two distinct objects (one registered as the interface and one pseudo-registered as the concrete type)! It doesn't matter that your class implements the interface and you've used it as the "Implementation" type in the registration. DI is always based on the service type and you've not registered any services of type NotifierService directly.
Your problem is that you have a weird coupling between your classes and the method you want to call on NotifierService isn't actually part of the interface. The usual trick would be to just register and request the concrete type as the service type:
services.AddSingleton<NotiferService>();
//...
var notifier = services.GetService<NotifierService>();
That would work, except now you haven't registered INotifierService for injection into the RefresherService.
Never fear, we have a work around. Register the concrete type as a singleton and then use a factory to register the interface:
// register the concrete type directly
services.AddSingleton<NotifierService>();
// use a factory to register the interface
services.AddSingleton<INotifierService>(sp => sp.GetRequiredService<NotifierService>());
Now, the same instance will be returned whether you are requesting the interface or the concrete type. You no longer need to use ActivatorUtilities either (in fact you shouldn't)--you can now use the host's services directly:
var notifier = host.Services.GetRequiredService<NotifierService>();
notifier.SomeBackgroundThreadMonitorsExternalEvents();
All that said, you're project is a perfect candidate for an IHostedService/BackgroundService. You can restructure it a bit (splitting NotifierService into two classes: one with just the event and the other for the background service) such that you'll then only be dealing with interfaces and you'd be able to actually call Host.Run() which will in turn wait for shutdown. This is the standard pattern for things like this, rather than abusing the Host simply for the DI container and including the weird Thread.Sleep.

Blazor: Unable to resolve service for type xx while attempting to activate yyy

I have read many other SO questions on the same topic, but none of the answers that I found applies to my case.
I have successfully added 4 services in my Startup.cs, and it was working fine before. I then added the 5th, and now I realize that something is broken - none of the services work. Even if I remove the 5th completely, the other ones are now also broken with the same error.
Unable to resolve service for type xx while attempting to activate
This is my Startup.cs ConfigureServices.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddStorage();
services.AddSingleton<IMyLocalStorage, MyLocalStorage>();
services.AddSingleton<IFrontEndService, FrontEndService>();
services.AddSingleton<ISystemProvider, SystemProviderService>();
services.AddSingleton<IAuthenticationService, AuthenticationService>();
}
It's the last AuthenticationService that I noticed the error, but even the older previously working services fails now.
public interface IAuthenticationService
{
// ...
}
public class AuthenticationService : IAuthenticationService
{
private readonly FrontEndService frontEndService;
private readonly MyLocalStorage myLocalStorage;
public AuthenticationService(FrontEndService frontEndService, MyLocalStorage myLocalStorage)
{
this.frontEndService = frontEndService;
this.myLocalStorage = myLocalStorage;
}
// ...
}
The services are simple; one interface, one implementation of that interface, and then adding in Startup.cs. I can't figure out why it stopped working.
So if I remove IAuthenticationService, then the error instead shows up in FrontEndService, then complaining on the MyLocalStorage:
public interface IFrontEndService
{
Task<T> GetAsync<T>(string requestUri);
}
public class FrontEndService : IFrontEndService
{
private readonly HttpClient client;
private readonly MyLocalStorage myLocalStorage;
public FrontEndService(HttpClient client, MyLocalStorage myLocalStorage)
{
// ...
}
}
and
public class MyLocalStorage : IMyLocalStorage
{
public MyLocalStorage(LocalStorage storage)
{
this.storage = storage;
}
}
What am I missing here?
When you call methods on IServiceCollection such as .AddSingleton<IFrontEndService, FrontEndService>(), you're saying to the container, "Whenever you see an IFrontEndService dependency, inject an instance of FrontEndService." Now if you take a look at your AuthenticationService:
public class AuthenticationService : IAuthenticationService
{
private readonly FrontEndService frontEndService;
private readonly MyLocalStorage myLocalStorage;
public AuthenticationService(FrontEndService frontEndService, MyLocalStorage myLocalStorage)
{
this.frontEndService = frontEndService;
this.myLocalStorage = myLocalStorage;
}
// ...
}
Notice how you're passing in dependencies of FrontEndService and MyLocalStorage, rather than the interfaces you registered. That means the container doesn't recognise them, so it doesn't know how to fulfil the dependency graph.
You need to change the service to depend on the interfaces, as those are what you've registered with the container:
public class AuthenticationService : IAuthenticationService
{
private readonly IFrontEndService frontEndService;
private readonly IMyLocalStorage myLocalStorage;
public AuthenticationService(IFrontEndService frontEndService, IMyLocalStorage myLocalStorage)
{
this.frontEndService = frontEndService;
this.myLocalStorage = myLocalStorage;
}
// ...
}
#Ted,
Do you remember a question of yours from a couple of weeks ago, in which you used LocalStorage in a service ? At that service you had a constructor with IStorage parameter, but this caused an error, the reason of which was that though the LocalStorage class implements the IStorage interface, the creators of this library added the LocalStorage to the DI container as a concrete class like this:
public static IServiceCollection AddStorage(this IServiceCollection services)
{
return services.AddSingleton<SessionStorage>()
.AddSingleton<LocalStorage>();
}
And therefore, you had to use
(LocalStorage storage)
instead of
(IStorage storage)
The extension method above, could be rewritten thus:
public static IServiceCollection AddStorage(this IServiceCollection services)
{
return services.AddSingleton<IStorage, SessionStorage>()
.AddSingleton<IStorage, LocalStorage>();
}
In which case, you could use the IStorage interface in your constructor.
Now you may form a general rule, and act accordingly.
Ted says:
Thats odd, cause I have used exactly this approach before, and it
worked fine. If you read the docs, Microsoft also uses the concrete
class, not the interface
HttpClient derives from HttpMessageInvoker. It does not implement any interface.
This code-snippet shows how the HttpClient is added to the service container, and made available for injection in your client-side Blazor:
services.AddSingleton<HttpClient>(s =>
{
// Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
var uriHelper = s.GetRequiredService<IUriHelper>();
return new HttpClient
{
BaseAddress = new Uri(WebAssemblyUriHelper.Instance.GetBaseUri())
};
});
Hope this helps...

IoC / Dependency Injection - How to handle contextual dependencies (using Structuremap)

After introducing messaging in my application it seems I've found a bit of a smell.
In my multi tenant application, the file system is abstracted and scoped for each tenant. So if a service needs to create files, then we inject an instance of IFileSystem which will be scoped to the tenants directory/container.
This is achieved by configuring structuremap to construct the IFileSystem implementation by getting of a contextual object that has the current users site.
Now we need to use the filesystem when there is no context and no current user (on a background thread). Here's a simple example:
public class SiteContext
{
public string SiteId { get { return "Site123"; } }
}
public class FileSystemSettings
{
public string BaseDirectory { get; set; }
}
public interface IFileSystem { }
public class DefaultFileSystem : IFileSystem
{
public DefaultFileSystem(FileSystemSettings settings)
{
}
}
public interface ISomeService { }
public class SomeService : ISomeService
{
public SomeService(IFileSystem fileSystem)
{
}
}
public class TestMessageHandler : IMessageHandler<TestMessage>
{
public TestMessageHandler(ISomeService someService)
{
// oO we don't have access to site context here :(
}
}
I suppose I could change my FileSystem implementation to expose the FileSystemSettings as a property so it can be set afterwards.
However, even doing this would still require me to construct my ISomeService object manually, which is a pain as some of my services have a number of dependencies = lots of calls to ObjectFactory.GetInstance...
Ideas?
You could use nested containers and configure the nested container to have a dummy implementation of your context.
The code would approximately be:
using (var container = ObjectFactory.Container.GetNestedContainer())
{
container.Configure(config => {
config.For<ISiteContext>().Use<DummyContext>();
});
return container.GetInstance<TestMessageHandler>();
}
This should set a custom (dummy) implementation of ISiteContext without overwriting the global container (ObjectFactory.Container). Of course, I can't give you an appropriate implementation of DummyContext without more information. But this should get you started.

Categories

Resources