Resolve .net core injection - c#

I have an interface IRabbitSender and the implementation RabbitSender
public class RabbitSender : IRabbitSender(){
public RabbitSender(string connection, string queue){
}
public void Send (object info){ // send message to specific queue }
}
I need different instances of the RabbitSender that will send information for different queues/connections.
But I only know which instance choose in runtime.
How can I do the DI? Actually, I have this, but I don't know how to distinct both and how to resolve the dependency in runtime.
services.AddTransient<IRabbitSender>(s => new RabbitSender(connection1, queueName1));
services.AddTransient<IRabbitSender>(s => new RabbitSender(connection2, queueName1));

There are several approaches to take here. Here are two to consider.
Consider making the interface generic:
IRabbitSender<TMessage>
This allows the consumer specifying which message to send, and in your configuration you can map message types to queues (tip: try keeping message names and queue names in sync as a convention; that drastically simplifies your life):
// Handy extension method
public static AddSender<TMessage>(
this IServiceCollection services, string con, string queue)
{
services.AddSingleton<IRabbitSender<TMessage>(new RabbitSender(con, queue);
}
// Registrations
services.AddSender<ShipOrder>(connection1, queueName1);
services.AddSender<CancelOrder>(connection2, queueName1);
Inject the full list queues into RabbitSender
Another option is to inject the mapping of messages types to queue information into RabbitSender. For instance:
public class RabbitSender : IRabbitSender {
private Dictionary<Type, (string connection, string queue)> senders;
public RabbitSender(Dictionary<Type, (string connection, string queue)> senders){
this.senders = senders;
}
public void Send(object info) {
var queueInfo = this.senders[info.GetType];
// TODO: Use queue info to send message to a queue
}
}
// Registration
servies.AddSingleton<IRabbitSender>(new RabbitSender(new Dictionary
{
{ typeof(ShipOrder), (connection1, queueName1) }
{ typeof(CancelOrder), (connection2, queueName1) }
}

You need to new up your RabbitSender class when you register instance it should be unique to. For example:
services.AddTransient<IRequireUniqueRabbit>(_ => {
return new RequireUniqueRabbit(new RabbitSender(connString1, queue1))
});
services.AddTransient<IRequireUniqueRabbit2>(_ => {
return new RequireUniqueRabbit2(new RabbitSender(connString2, queue2))
});
PS: Think about how you register classes holding the RabbitMQ connection, if you register them as transient or scoped they will dispose rabbitMQ connection each time they are disposed, most likely you want to avoid that

Related

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.

How to recreate a WCF Service as Singleton instance with SimpleInjector (using Tridion)

I'm using the IoC Container SimpleInjector.
I know that Singletons shouldn't be recreated since that's not their purpose but my problem is with WCF and when it enters into Faulted state which according to several readings it cannot be recovered and a new instance needs to be created.
I have a class named CoreServiceService which uses two WCF Services.
CoreService
ECLService
I want those services to be singleton since I'll be making lots of calls to CoreServiceSession and creating those WCF Service is too expensive and takes a lot of times, after the creation, they are much faster.
I'm registering them like this:
container.Register(() => new SessionAwareEclServiceClient(binding, eclServiceRemoteAddress), Lifestyle.Singleton);
container.Register(() => new SessionAwareCoreServiceClient(binding, coreServiceRemoteAddress), Lifestyle.Singleton);
container.Register(typeof(ICoreServiceSession), typeof(CoreServiceSession), Lifestyle.Scoped);
My problem is that while using ECLService if something cannot be retrieved is enters into Faulted connection, In that case, I call .Abort() and close the connection. But the next time I call my service ECLService WCF service keeps being in the Faulted state (since it's a singleton) so I need a way to recreate the connection.
I tried with something like:
coreServiceSession.EclServiceClient = (SessionAwareEclServiceClient)container.GetInstance(typeof(SessionAwareEclServiceClient));
But, of course, it gives me the same instance.
I also tried using this initializer:
container.RegisterInitializer<ICoreServiceSession>(coreServiceSession =>
{
if (coreServiceSession.EclServiceClient.State == CommunicationState.Faulted)
{
coreServiceSession.EclServiceClient.Abort();
coreServiceSession.EclServiceClient = null;
coreServiceSession.EclServiceClient = (SessionAwareEclServiceClient)container.GetInstance(typeof(SessionAwareEclServiceClient));
}
}
Same thing and I tried to use instead of container.GetInstance, this:
coreServiceSession.EclServiceClient = new SessionAwareEclServiceClient(binding, eclServiceRemoteAddress);
Same things. Any ideas/options?
It there any way to force to get a new instance in this case?
UPDATE
This is part of the class CoreServiceSession:
public class CoreServiceSession : ICoreServiceSession
{
public CoreServiceSession(ISessionAwareCoreService sessionAwareEclServiceClient, SessionAwareCoreServiceClient sessionAwareCoreServiceClient)
{
EclServiceClient = sessionAwareEclServiceClient;
CoreServiceClient = sessionAwareCoreServiceClient;
}
public ISessionAwareCoreService EclServiceClient { get; set; }
public SessionAwareCoreServiceClient CoreServiceClient { get; set; }
public string CreateOrGetStubUris(string eclItemUri)
{
var stubInfo = EclServiceClient.CreateOrGetStubUris(new List<string> { eclItemUri }).FirstOrDefault();
}
}
Thanks in advance. Guillermo
#ScottHannen already gave the answer in his comment: do not register channels as singletons: they are not expensive to create, only the channel factories are.
As a matter of fact, you shouldn't inject your WCF client objects into constructors at all. Injecting them into a constructor implies that they are a useful abstraction that can be used to intercept, mock or replace, while the class using such client is typically strongly coupled to WCF.
So instead of injecting them into a constructor, let the consumer create them internally using the ChannelFactory. Even such ChannelFactory typically doesn't have to be injected, you can just new it up in a private static field.
This is how your CoreServiceSession might look like:
public class CoreServiceSession : ICoreServiceSession
{
private static readonly ChannelFactory factory =
new ChannelFactory<ISessionAwareCoreService>(myBinding, myEndpoint);
public string CreateOrGetStubUris(string eclItemUri)
{
var client = factory.CreateChannel();
try
{
return EclServiceClient.CreateOrGetStubUris(
new List<string> { eclItemUri }).FirstOrDefault();
}
finally
{
try
{
((IDisposable)client).Dispose();
}
catch
{
// We need to swallow exceptions thrown by Dispose.
// See: https://marcgravell.blogspot.com/2008/11/dontdontuse-using.html
}
}
}
}

Masstransit Filter with IoC injection/Database

I have to validate incoming message before passing it to my consumer.
To do it, I need to request some data from the database.
Following the tutorials, I created extension method to apply my specification + filter to the consumer pipe. Something like this:
public static void UseArticlesImportValidation(this IConsumePipeConfigurator configurator){}
public class ArticlesImportValidationSpecification : IPipeSpecification<ConsumeContext<ArticlesImportNotification>>
and the Filter
public class ArticlesImportValidationFilter : IFilter<ConsumeContext<ArticlesImportNotification>>
Everything looks good, but I want to injection some business services in my Filter to reuse some functionality + DAL services. This works completely fine for my Consumer using Autofac extension method builder.RegisterConsumers(Assembly.GetExecutingAssembly());.
Should I use middleware for this at all? Any suggestions?
You need to have the built container in your specification (it is easy to pass as a parameter when you call AddPipeSpecification and then in the specification:
public void Apply(IPipeBuilder<T> builder)
{
builder.AddFilter(new ArticlesImportValidationFilter(container.Resolve<IDataAccessSomethingUseful>()));
}
But I would validate in the consumer or, if you want to keep them separate, have one consumer to validate and send the next message to do the actual job.
You should use Scoped Filters in this situation
Let's say you have a filter with dependency
public class MyConsumeFilter<T> :
IFilter<ConsumeContext<T>>
where T : class
{
public MyConsumeFilter(IMyDependency dependency) { }
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next) { }
public void Probe(ProbeContext context) { }
}
you should register this dependency in your DI container
services.AddScoped<IMyDependency, MyDependency>(); //register dependency
And now you are ready to add this filter to the consumer pipe by calling UseConsumeFilter method:
services.AddConsumer<MyConsumer>();
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.ReceiveEndpoint("input-queue", e =>
{
e.UseConsumeFilter(typeof(MyConsumeFilter<>), context); //generic filter
e.ConfigureConsumer<MyConsumer>();
});
});
});

Configuring MassTransit's Ninject bindings after initialisation

I want to configure MassTransit at one point in my code (using WebActivator) and configure the message handlers in another (a Ninject module). Is there a way I can achieve this? The documentation here shows how to perform what I need in one step, but to do anyhting else, it looks like I need to get an instance of a ServiceBusConfigurator, which doesn't seem to be available from the preexisting IServiceBus
Configuration and Creation of the IServiceBus cannot be separated.
That means, the only option you have is to gather the configuration information some more time before creating the bus.
As the doc you linked states, the meta data information made available by ninject is not sufficient to create the subscriptions. This basically means that you've got to create your own metadata model. Let's make an example, which can be used with single registrations but also with convention based registrations:
Hint: You should treat the following code snippets as psuedo code as i've written them from memory. It's highly likely that it won't compile.
Metadata Model
public class SubscriptionMetadata
{
public SubscriptionMetadata(Type consumer)
{
if(!typeof(IConsumer).IsAssignableFrom(consumer))
{
string message = string.Format(
"{0} does not implement {1}",
typeof(IConsumer).Name,
consumer.Name);
throw new ArgumentOutOfRangeException("consumer", message);
}
this.ConsumerType = consumer;
}
public Type ConsumerType { get; private set; }
}
Registration of Metadata
Now this can be used like this in a Ninject module:
Bind<SubscriptionMetadata>()
.ToConstant(new SubscriptionMetadata(typeof(FooConsumer));
If you're going to use it a lot i'd recommend writing an extension method:
public static class SubscriptionBindingExtensions
{
public static void BindConsumer<T>(this IBindingRoot bindingRoot)
where T : IConsumer
{
Bind<SubscriptionMetadata>()
.ToConstant(new SubscriptionMetadata(typeof(T));
}
}
and usage (#Module):
BindConsumer<FooConsumer>();
IServiceBus creation
Now you would adapt the IServiceBus creation as follows:
var kernel = new StandardKernel();
// 2nd Step left out: load all IModule`s ..
var bus = ServiceBusFactory.New(sbc =>
{
//other configuration options
foreach(var metadata in kernel.GetAll<SubscriptionMetadata>())
{
sbc.Subscribe(subs =>
{
subs.Consumer(metadata.ConsumerType, kernel)
});
}
});
Convention based binding of Consumers
It can also be used in conjunction with conventions by leveraging the IBindingCreator interface. If you wish, i can post an example.

Azure Queue client Dependency injection

I have an azure application developed using MVC Web API, and it uses Ninject as the dependency injection framework, here there are number of queues used to communicate with the other worker roles responsible for the background processing.
To be able to unit test I decided to wrap the QueueClient with class called QueueClientWrapper and use an interface named IQueueClientWrapper
the class and interface looks like follows,
public interface IQueueClientWrapper
{
void Send<T>(T message);
}
public class QueueClientWrapper : IQueueClientWrapper
{
private QueueClient _queueClient;
public QueueClientWrapper(QueueClient queueClient)
{
_queueClient = queueClient;
}
public void Send<T>(T message)
{
_queueClient.Send(new BrokeredMessage(message));
}
}
However the limitation of this approach is I need to pass the QueueClient into the constructor, which is not possible with the Ninject.
So I was thinking changing the interface like,
public interface IQueueClientWrapper
{
void Send<T>(string connectionString,string queueName,T message);
}
so that I can use Ninject to inject the QueueClientWrapper without needing to pass constructor argument,
The issue I have now is, usually it's not recommended to create multiple queue client objects and we have to re-use same instance. So what's the best way to address this (I thought of using a static Hashtable and use to store queue name, queue client pairs, but I'm not very happy with that approach either)?
Dealing with Dependency Injection and Azure Queue
This question is from 2014, but here is a 2022 answer
You will need these 2 official azure extensions :
Azure.Storage.Queues (Contains the queue service)
Microsoft.Azure.Functions.Extensions (Contains helpers for the Startup.cs)
Add this line to your Startup.cs in the Configure method :
builder.Services.AddAzureClients(builder =>
{
builder.AddQueueServiceClient(configuration.GetValue<string>("AzureWebJobsStorage"));
});
And the constructor where you want the service :
public QueueHandlingService(QueueServiceClient queueClient)
{
_queueClient = queueClient;
//Or _queueClient = queueClient.GetQueueClient("myqueue-items");
}
Here are the docs for :
AddAzureClients
AddQueueServiceClient

Categories

Resources