Core issue
Every example I see for dependency injection is paired with MVC for some reason as if nothing else exists outside of web projects. I take issue with this because there is a contradiction going on with MVC utilizing dependency injection but it delivers those dependencies through a Dependency Resolver which to me is just another name for a Service Locator.
DI in a simple console application
With all that being said, how do you use DI with a simple console application?
When there isn't a convenient Dependency Resolver ready to use. How do I actually perform the injection part of DI?
I see the disapproving tone around Service Locators, but what else can you do?
You cannot pass the container because that's also bad practice, but again what else can be done?
Common confusion/frustration
I see a lot of programmers making these mistakes and honestly I can't blame them for it. There isn't a clear solution outside of MVC which is clearly using the dreaded Service Locator.
DI introduces its own problems
Something I don't feel good about doing is pass a dependency through a chain of objects to use it in a deeply nested piece of code. This just feels wrong.
Example
This is a watered down example of something I am working on to demonstrate my concern. I don't like passing the SMTP client dependency through a class, just to give it to another class. You might be compelled to say "Inject the SmtpClient into ServiceClass then into EntryPoint". In my example I cannot inject ServiceClass because it actually comes from a Factory pattern.
public static void Main(string[] args)
{
var smtpClient = _container.GetDependency<ISmtpClient>();
//When I do this manually I feel like it defeats the purpose of DI
var ep = new EntryPoint(smtpClient);
ep.RunAProcess();
}
public class EntryPoint
{
private readonly ISmtpClient _smtpClient;
public EntryPoint(ISmtpClient smtpClient)
{
//EntryPoint doesn't use this dependency
_smtpClient = smtpClient;
}
public void RunAProcess()
{
/* More code here */
//ServiceClass actually comes from a Factory, but I didn't
//want to make this example too long
var svc = new ServiceClass(_smtpClient);
svc.Send();
}
}
public class ServiceClass
{
private readonly ISmtpClient _smtpClient;
public ServiceClass(ISmtpClient smtpClient)
{
//ServiceClass uses this dependency
_smtpClient = smtpClient;
}
public void Send()
{
using (var mail = CreateMailMessage(message))
{
_smtpClient.Send(mail);
}
}
}
Almost related existing question
This is the closest SO question I found in relation to my query:
DbContext Dependency Injection outside of MVC project
Outside of MVC you can use HostBuilder see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2
The general idea is that it works pretty much like the web version ( and will support console, windows services, and linux daemons etc )
public static async Task Main(string[] args)
{
var host = new HostBuilder() .
.ConfigureServices(ConfigureServices)
.UseConsoleLifetime()
.Build();
await host.RunAsync();
}
private static void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services
.AddTransient<IThing, Thingy>()
.AddTransient<Stuff>()
.AddHostedService<MyService>();
}
Your Hosted Service is like your main entry point and things from there will be injected....
internal class MyService : IHostedService
{
public MyService(Stuff stuff) // injected stuff
{
}
public Task StartAsync(CancellationToken cancellationToken)
{
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
This is more a matter of misunderstanding the design principles.
Something I don't feel good about passing a dependency through a chain of objects to use it in a deeply nested piece of code. This just feels wrong.
The core of your issue is about understanding how to apply a clean design which allows loose coupling and high cohesion. Whether it is Asp.Net MVC or console application is an implementation detail.
The watered down example in this case is not following a clean design as EntryPoint is tightly coupling to ServiceClass and also violates the Explicit Dependencies Principle.
EntryPoint in this example is not being genuine about its dependencies. If it does not use ISmtpClient directly then it should not explicitly depend on it just to pass it on.
And if ServiceClass is coming from a factory then the factory should be applied at the composition root and then explicitly injected into EntryPoint
Review the following refactoring to see what I am referring to
public static void Main(string[] args) {
//ISmtpClient should be injected into ServiceClass
//when resolved by the container or factoty
IService service = _container.GetDependency<IService>();
var ep = new EntryPoint(service);
ep.RunAProcess();
}
public class EntryPoint {
private readonly IService service;
public EntryPoint(IService service) {
this.service = service;
}
public void RunAProcess() {
/* More code here */
service.Send(message);
}
}
public class ServiceClass : IService {
private readonly ISmtpClient _smtpClient;
public ServiceClass(ISmtpClient smtpClient) {
//ServiceClass uses this dependency
_smtpClient = smtpClient;
}
public void Send(Message message) {
using (var mail = CreateMailMessage(message)) {
_smtpClient.Send(mail);
}
}
}
So even if you apply pure dependency injection at the composition root, only the actual dependencies are injected into the target dependent.
Related
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.
i'm trying to develop from scratch a WPF app with the use of Simpleinjector as a IOC container.
I'm new on this topic and i have some issue regards lifetime of object and hot use them correctly.
I started the app by following the WPF integration guide on simpleinjector manual.
But i don't understand how to receive a new instance every time a service needed it
As i ask in my previous post i need to receive a new unitOfWork every time a service need it.
as #Steven say on my previous post
Do note that transient means "allways a new instance is resolved when it is requested from the container." If you're not requesting it again, you will be operating on the same instance, which might explain the ObjectDisposedException.
In the other post i found a solutin but i think it's a little bit over-complicated and it's to create a factory and inject this instead of the instance because i want to call the container.getInstance only on the startup method and not on the service by passing the container as a dependency
It's the only way i have to achieve this or there is something that i don't understand on how to develop in DI way?
Example of code:
public class HeaderViewModelFactory : IWpfRadDispenserViewModelFactory<HeaderviewModel>
{
private readonly ProductionService _service;
public HeaderViewModelFactory(ProductionService service)
{
_service = service;
}
public HeaderviewModel CreateViewModel()
{
return new HeaderviewModel(_service);
}
}
public class HeaderviewModel : ViewModelBase
{
private readonly ProductionService _service;
public HeaderviewModel(ProductionService service)
{
_service = service;
CreateData();
}
private void CreateData()
{
_service.CreateTestCycle();
}
}
public class CycleService : GenericDataService<Cycle>
{
private readonly IUnitOfWork<WpfRadDispenserDbContext> _uowContext;
public CycleService(IUnitOfWork<WpfRadDispenserDbContext> uowContext)
: base(uowContext)
{
_uowContext = uowContext;
}
public void CreateTestCycle()
{
var cycleDataService = new GenericDataService<Cycle>(_uowContext);
var vialDataService = new GenericDataService<Vial>(_uowContext);
Cycle c = new Cycle();
c.BatchName = "test";
Vial v = new Vial();
v.Name = "Test Vial";
c.Vials.Add(v);
_uowContext.CreateTransaction(IsolationLevel.ReadCommitted);
try
{
vialDataService.Create(v);
_uowContext.Persist();
var list = vialDataService.GetAll();
cycleDataService.Create(c);
_uowContext.Persist();
_uowContext.Commit();
}
catch (Exception e)
{
Console.WriteLine(e);
_uowContext.RollBack();
throw;
}
finally
{
_uowContext.Dispose();
}
}
}
private static Container Bootstrap()
{
// Create the container as usual.
var container = new Container();
// Register your types:
// Register your windows and view models:
container.Register<WpfRadDispenserDbContextFactory>(Lifestyle.Transient);
container.Register<IUnitOfWork<WpfRadDispenserDbContext>,WpfRadDispenserUOW>();
container.Register(typeof(CycleService));
container.Register<IWpfRadDispenserViewModelFactory<ProductionViewModel>,
ProductionViewModelFactory>(Lifestyle.Transient);
container.Register<IWpfRadDispenserViewModelFactory<AnagraphicViewModel>,
AnagraphicsViewModelFactory>(Lifestyle.Transient);
container.Register<IWpfRadDispenserViewModelFactory<HeaderviewModel>,
HeaderViewModelFactory>(Lifestyle.Transient);
container.Register<IViewModelAbstractFactory,
ViewModelAbstractFactory>(Lifestyle.Transient);
container.Register<INavigator, Navigator>(Lifestyle.Transient);
container.Register<MainWindowViewModel>();
container.Register<MainWindow>();
//container.Options.EnableAutoVerification = false;
//container.Verify();
return container;
}
in this way every time i create a new viewmodel i receive the same service and ovviously the dbcontext it's not present anymore because disposed.
This is not the rela code but only an example that i made to understand how DI works.
Using Abstract Factory pattern is the most common and recommended approach. Using the container in your application directly is widely considered an anti-pattern, like the Service Locator (Service Locator is an Anti-Pattern) for a very good reason.
Abstract factory allows instantiation of objects without introducing a tight coupling to the actual implementation that knows how to create specific instances.
Most IoC frameworks support this pattern natively. Most of the time they provide the generic interface for the factory. You register the instance (the product) with the container and the framework will export a ready-to use factory for you. You add the dependency to this framework interface to your object e.g. constructor. Then you register the generic factory interface. The framework will automatically create the instance of the factory and inject it into the relevant instances e.g., via constructor.
I am not too familiar with Simple Injector, but the framework really keeps things simple. There is no such code generation.
But the pattern is very simple (that's why this is so easy to automate) and in no way complicated.
Example
The interface required to dynamically create the instances of type TInstance:
interface IFactory<TInstance>
{
TInstance Create();
}
The implementation of this factory:
class SaveItemFactory : IFactory<ISaveItem>
{
ISaveItem Create() => new SaveItem();
}
The type that needs to create a dependency dynamically:
interface IItemManager {}
class ItemManager : IItemManager
{
IFactory<ISaveItem> SaveItemFactory { get; }
public ItemManager(IFactory<ISaveItem> itemFactory) => this.SaveItemFactory = itemFactory;
public void SaveData(object data)
{
ISaveItem saveItem = this.SaveItemFactory.Create();
saveItem.SetData(data);
}
}
Configure the container:
public void Run()
{
var container = new SimpleInjector.Container();
container.Register<IFactory<ISaveItem>, SaveItemFactory>(Lifestyle.Singleton);
container.Register<IItemManager, ItemManager>(Lifestyle.Singleton);
IItemManager itemManager = container.GetInstance<IItemManager>();
itemManager.SaveData("Some Data");
}
I have an MVC web app, and I'm using Simple Injector for DI. Almost all my code is covered by unit tests. However, now that I've added some telemetry calls in some controllers, I'm having trouble setting up the dependencies.
The telemetry calls are for sending metrics to the Microsoft Azure-hosted Application Insights service. The app is not running in Azure, just a server with ISS. The AI portal tells you all kinds of things about your application, including any custom events you send using the telemetry library. As a result, the controller requires an instance of Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors. I tried registering it like so (the hybrid lifestyle is unrelated to this question, I just included it for completeness):
// hybrid lifestyle that gives precedence to web api request scope
var requestOrTransientLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
Lifestyle.Transient);
container.Register<TelemetryClient>(requestOrTransientLifestyle);
The problem is that since TelemetryClient has 2 constructors, SI complains and fails validation. I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated. First I wanted to back up and ask this question:
If I don't make the TelemetryClient an injected dependency (just create a New one in the class), will that telemetry get sent to Azure on every run of the unit test, creating lots of false data? Or is Application Insights smart enough to know it is running in a unit test, and not send the data?
Any "Insights" into this issue would be much appreciated!
Thanks
Application Insights has an example of unit testing the TelemetryClient by mocking TelemetryChannel.
TelemetryChannel implements ITelemetryChannel so is pretty easy to mock and inject. In this example you can log messages, and then collect them later from Items for assertions.
public class MockTelemetryChannel : ITelemetryChannel
{
public IList<ITelemetry> Items
{
get;
private set;
}
...
public void Send(ITelemetry item)
{
Items.Add(item);
}
}
...
MockTelemetryChannel = new MockTelemetryChannel();
TelemetryConfiguration configuration = new TelemetryConfiguration
{
TelemetryChannel = MockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString()
};
configuration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
TelemetryClient telemetryClient = new TelemetryClient(configuration);
container.Register<TelemetryClient>(telemetryClient);
Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors.
This TelemetryClient is a framework type and framework types should not be auto-wired by your container.
I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated.
Yep, this complexity is deliberate, because we want to discourage people from creating components with multiple constructors, because this is an anti-pattern.
Instead of using auto-wiring, you can, as #qujck already pointed out, simply make the following registration:
container.Register<TelemetryClient>(() =>
new TelemetryClient(/*whatever values you need*/),
requestOrTransientLifestyle);
Or is Application Insights smart enough to know it is running in a unit test, and not send the data?
Very unlikely. If you want to test the class that depends on this TelemetryClient, you better use a fake implementation instead, to prevent your unit test to either become fragile, slow, or to pollute your Insight data. But even if testing isn't a concern, according to the Dependency Inversion Principle you should depend on (1) abstractions that are (2) defined by your own application. You fail both points when using the TelemetryClient.
What you should do instead is define one (or perhaps even multiple) abstractions over the TelemetryClient that are especially tailored for your application. So don't try to mimic the TelemetryClient's API with its possible 100 methods, but only define methods on the interface that your controller actually uses, and make them as simple as possible so you can make both the controller's code simpler -and- your unit tests simpler.
After you defined a good abstraction, you can create an adapter implementation that uses the TelemetryClient internally. I image you register this adapter as follows:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(new TelemetryClient(...)));
Here I assume that the TelemetryClient is thread-safe and can work as a singleton. Otherwise, you can do something like this:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(() => new TelemetryClient(...)));
Here the adapter is still a singleton, but is provided with a delegate that allows creation of the TelemetryClient. Another option is to let the adapter create (and perhaps dispose) the TelemetryClient internally. That would perhaps make the registration even simpler:
container.RegisterSingleton<ITelemetryLogger>(new TelemetryClientAdapter());
I had a lot of success with using Josh Rostad's article for writing my mock TelemetryChannel and injecting it into my tests. Here's the mock object:
public class MockTelemetryChannel : ITelemetryChannel
{
public ConcurrentBag<ITelemetry> SentTelemtries = new ConcurrentBag<ITelemetry>();
public bool IsFlushed { get; private set; }
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; }
public void Send(ITelemetry item)
{
this.SentTelemtries.Add(item);
}
public void Flush()
{
this.IsFlushed = true;
}
public void Dispose()
{
}
}
And then in my tests, a local method to spin-up the mock:
private TelemetryClient InitializeMockTelemetryChannel()
{
// Application Insights TelemetryClient doesn't have an interface (and is sealed)
// Spin -up our own homebrew mock object
MockTelemetryChannel mockTelemetryChannel = new MockTelemetryChannel();
TelemetryConfiguration mockTelemetryConfig = new TelemetryConfiguration
{
TelemetryChannel = mockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString(),
};
TelemetryClient mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
return mockTelemetryClient;
}
Finally, run the tests!
[TestMethod]
public void TestWidgetDoSomething()
{
//arrange
TelemetryClient mockTelemetryClient = this.InitializeMockTelemetryChannel();
MyWidget widget = new MyWidget(mockTelemetryClient);
//act
var result = widget.DoSomething();
//assert
Assert.IsTrue(result != null);
Assert.IsTrue(result.IsSuccess);
}
If you don't want to go down the abstraction / wrapper path. In your tests you could simply direct the AppInsights endpoint to a mock lightweight http server (which is trivial in ASP.NET Core).
appInsightsSettings.json
"ApplicationInsights": {
"Endpoint": "http://localhost:8888/v2/track"
}
How to set up "TestServer" in ASP.NET Core http://josephwoodward.co.uk/2016/07/integration-testing-asp-net-core-middleware
Another option without going the abstraction route is to disable telemetry before doing running your tests:
TelemetryConfiguration.Active.DisableTelemetry = true;
Based on other work here;
Create the channel - you can use this for testing telemetries if needed
public class MockTelemetryChannel : ITelemetryChannel
{
public ConcurrentBag<ITelemetry> SentTelemtries = new();
public bool IsFlushed { get; private set; }
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; }
public void Send(ITelemetry item)
{
this.SentTelemtries.Add(item);
}
public void Flush()
{
this.IsFlushed = true;
}
public void Dispose()
{
}
}
Use a nice little static factory class
public static class MockTelemetryClient
{
public static TelemetryClient Create()
{
var mockTelemetryChannel = new MockTelemetryChannel();
var mockTelemetryConfig = new TelemetryConfiguration
{
TelemetryChannel = mockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString()
};
var mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
return mockTelemetryClient;
}
}
Call MockTelemetryClient.Create() to get your TelemetryClient
Profit
A colleague of mine wrote this useful library that introduces abstractions for some of these core telemetry types (e.g. ITelemetryClient and IMetric).
https://github.com/thomhurst/ApplicationInsights.TelemetryLogger
Very easy to implement. You'll barely have to change anything in your production code, and mocking in tests becomes a breeze. Here's an extract from the README:
Dependency Injection
Call AddApplicationInsightsTelemetry() as normal, and then call AddApplicationInsightsTelemetryClientInterfaces()
public void ConfigureServices(IServiceCollection services)
{
services
.AddApplicationInsightsTelemetry()
.AddApplicationInsightsTelemetryClientInterfaces();
}
ITelemetryClient
Want the same usage as TelemetryClient? Inject ITelemetryClient into your classes. It has all the available methods of TelemetryClient (apart from any methods which shouldn't be called. e.g. internal or deprecated).
public class MyClass
{
private readonly ITelemetryClient _telemetryClient;
public MyClass(ITelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public void DoSomething()
{
_telemetryClient.TrackTrace("Something happened");
}
}
One of my dependencies (DbContext) is registered using the WebApiRequestLifestyle scope.
Now, my background job uses IoC and depends on the service that was registered above using the WebApiRequestLifestyle. I'm wondering how this works when Hangfire calls the method i registered for the background job. Will the DbContext be treated like a transistent object since the web api is not involved?
Any guidance would be great!
Here is my initialize code that occurs during start up:
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
var container = SimpleInjectorWebApiInitializer.Initialize(httpConfig);
var config = (IConfigurationProvider)httpConfig.DependencyResolver
.GetService(typeof(IConfigurationProvider));
ConfigureJwt(app, config);
ConfigureWebApi(app, httpConfig, config);
ConfigureHangfire(app, container);
}
private void ConfigureHangfire(IAppBuilder app, Container container)
{
Hangfire.GlobalConfiguration.Configuration
.UseSqlServerStorage("Hangfire");
Hangfire.GlobalConfiguration.Configuration
.UseActivator(new SimpleInjectorJobActivator(container));
app.UseHangfireDashboard();
app.UseHangfireServer();
}
public static Container Initialize(HttpConfiguration config)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterWebApiControllers(config);
container.RegisterMvcIntegratedFilterProvider();
container.Register<Mailer>(Lifestyle.Scoped);
container.Register<PortalContext>(Lifestyle.Scoped);
container.RegisterSingleton<TemplateProvider, TemplateProvider>();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
return container;
}
Here is my code that kicks off the background job:
public class MailNotificationHandler : IAsyncNotificationHandler<FeedbackCreated>
{
private readonly Mailer mailer;
public MailNotificationHandler(Mailer mailer)
{
this.mailer = mailer;
}
public Task Handle(FeedbackCreated notification)
{
BackgroundJob.Enqueue<Mailer>(x => x.SendFeedbackToSender(notification.FeedbackId));
BackgroundJob.Enqueue<Mailer>(x => x.SendFeedbackToManagement(notification.FeedbackId));
return Task.FromResult(0);
}
}
Finally here is the code that runs on the background thread:
public class Mailer
{
private readonly PortalContext dbContext;
private readonly TemplateProvider templateProvider;
public Mailer(PortalContext dbContext, TemplateProvider templateProvider)
{
this.dbContext = dbContext;
this.templateProvider = templateProvider;
}
public void SendFeedbackToSender(int feedbackId)
{
Feedback feedback = dbContext.Feedbacks.Find(feedbackId);
Send(TemplateType.FeedbackSender, new { Name = feedback.CreateUserId });
}
public void SendFeedbackToManagement(int feedbackId)
{
Feedback feedback = dbContext.Feedbacks.Find(feedbackId);
Send(TemplateType.FeedbackManagement, new { Name = feedback.CreateUserId });
}
public void Send(TemplateType templateType, object model)
{
MailMessage msg = templateProvider.Get(templateType, model).ToMailMessage();
using (var client = new SmtpClient())
{
client.Send(msg);
}
}
}
I'm wondering how this works when Hangfire calls the method i registered for the background job. Will the DbContext be treated like a transistent object since the web api is not involved?
As the design decisions describe, Simple Injector will never allow you to resolve an instance outside an active scope. So that DbContext will neither be resolved as transient or singleton; Simple Injector will throw an exception when there's no scope.
Every application type requires its own type of scoped lifestyle. Web API requires the AsyncScopedLifestyle (in previous versions WebApiRequestLifestyle), WCF an WcfOperationLifestyle and MVC the WebRequestLifestyle. For Windows Services you will typically use an AsyncScopedLifestyle.
If your Hangfire jobs run in a Windows Service, you will have to use either a ThreadScopedLifestyle or the AsyncScopedLifestyle. Those scopes require explicit starting.
When running the jobs on a background thread in a web (or Web API) application, there is no access to the required context and this means that Simple Injector will throw an exception if you try to do so.
You however are using the Hangfire.SimpleInjector integration library. This library implements a custom JobActivator implementation called SimpleInjectorJobActivator and this implementation will create start a Scope for you on the background thread. Hangfire will actually resolve your Mailer within the context of this execution context scope. So the Mailer constructor argument in your MailNotificationHandler is actually never used; Hangfire will resolve this type for you.
The WebApiRequestLifestyle and AsyncScopedLifestyle are interchangeable; the WebApiRequestLifestyle uses an execution context scope in the background and the SimpleInjectorWebApiDependencyResolver actually starts an execution context scope. So the funny thing is that your WebApiRequestLifestyle can be used for background operations as well (although it can be a bit confusing). So your solution works and works correctly.
When running in MVC, however, this will not work, and in that case you would have to create a Hybrid lifestyle, for instance:
var container = new Container();
container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
new AsyncScopedLifestyle(),
new WebRequestLifestyle());
You can register your DbContext as follows:
container.Register<DbContext>(() => new DbContext(...), Lifestyle.Scoped);
Here's some feedback on your application's design, if you don't mind.
Prevent letting application code, such as your MailNotificationHandler, from taking a direct dependency on an external library such as Hangfire. This is a direct violation of the Dependency Inversion Principle and makes your application code very hard to test and maintain. Instead, let solely your Composition Root (the place where you wire your dependencies) take a dependency on Hangfire. In your case, the solution is really straightforward and I would even say pleasant, and it would look as follows:
public interface IMailer
{
void SendFeedbackToSender(int feedbackId);
void SendFeedbackToManagement(int feedbackId);
}
public class MailNotificationHandler : IAsyncNotificationHandler<FeedbackCreated>
{
private readonly IMailer mailer;
public MailNotificationHandler(IMailer mailer)
{
this.mailer = mailer;
}
public Task Handle(FeedbackCreated notification)
{
this.mailer.SendFeedbackToSender(notification.FeedbackId));
this.mailer.SendFeedbackToManagement(notification.FeedbackId));
return Task.FromResult(0);
}
}
Here we added a new IMailer abstraction and made the MailNotificationHandler dependent on this new abstraction; unaware of the existence of any background processing. Now close to the part where you configure your services, define an IMailer proxy that forwards the calls to Hangfire:
// Part of your composition root
private sealed class HangfireBackgroundMailer : IMailer
{
public void SendFeedbackToSender(int feedbackId) {
BackgroundJob.Enqueue<Mailer>(m => m.SendFeedbackToSender(feedbackId));
}
public void SendFeedbackToManagement(int feedbackId) {
BackgroundJob.Enqueue<Mailer>(m => m.SendFeedbackToManagement(feedbackId));
}
}
This requires the following registrations:
container.Register<IMailer, HangfireBackgroundMailer>(Lifestyle.Singleton);
container.Register<Mailer>(Lifestyle.Transient);
Here we map the new HangfireBackgroundMailer to the IMailer abstraction. This ensures that the BackgroundMailer is injected into your MailNotificationHandler, while the Mailer class is resolved by Hangfire when the background thread is started. The registration of the Mailer is optional, but advisable, since it has become a root object, and since it has dependencies, we want Simple Injector to be aware of this type to allow it to verify and diagnose this registration.
I hope you agree that from perspective of the MailNotificationHandler, the application is much cleaner now.
Getting more and more familiar with DI but I still have few niggles.
Read few articles where it says "Injection must be done at the entry point"
Suppose I have a situation where we have wcf Services and these are used both by internal win/web application and external third parties uses those wcf services.
Now where do you inject the Services and repositories?
Above to me seems to be a common scenarios!
Also i pass all those interfaces around.(Very good for mocking) how do I stop somebody from calling EG my repository from a layer that should NOT be calling the repository.
EG only the business Layer should call DAL.
Now by injecting a IRepository into a controller nothing stops a developer from calling the DAL.
Any suggestion? Links that clear all this
Noddy example of my poor man DI. How do I do the same using unity and Injecting all at the entryPoint?
[TestFixture]
public class Class1
{
[Test]
public void GetAll_when_called_is_invoked()
{
var mockRepository = new Mock<ICustomerRepository>();
mockRepository.Setup(x => x.GetAll()).Verifiable();
new CustomerService(mockRepository.Object);
ICustomerBiz customerBiz = new CustomerBizImp(mockRepository.Object);
customerBiz.GetAll();
mockRepository.Verify(x=>x.GetAll(),Times.AtLeastOnce());
}
}
public class CustomerService : ICustomerService //For brevity (in real will be a wcf service)
{
private readonly ICustomerRepository _customerRepository;
public CustomerService(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
public IEnumerable<Customer> GetAll()
{
return _customerRepository.GetAll();
}
}
public class CustomerBizImp : ICustomerBiz
{
private readonly ICustomerRepository _customerRepository;
public CustomerBizImp(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
public IEnumerable<Customer> GetAll()
{
return _customerRepository.GetAll();
}
}
public class CustomerRepository : ICustomerRepository
{
public IEnumerable<Customer> GetAll()
{
throw new NotImplementedException();
}
}
public interface ICustomerRepository
{
IEnumerable<Customer> GetAll();
}
public interface ICustomerService
{
IEnumerable<Customer> GetAll();
}
public interface ICustomerBiz
{
IEnumerable<Customer> GetAll();
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
thanks
This is a blog post on Composition roots or what you call entry points. Its from Mark Seemann the author of Dependency Injection in .NET. If you are looking for a deep understanding of DI this book is a must read.
There are a lot of samples out there on how to combine WCF and DI. If you are hosting your services in IIS you would need to write a custom ServiceHostFactory where you initialize you DI container. This is a sample for Microsoft's Unity.
As to
how do I stop somebody from calling EG my repository from a layer that should NOT be calling the repository
Do you use poor man's DI and pass all your references around through all your layers? Then you should definitely consider using a DI/IoC container like StructureMap, Castle Windsor, AutoFac or Unity.
If you are asking "how can I in general avoid the situation that someone does not follow my layer boundaries": Write tests that fail if an assembly references another one it should not reference (e.g. UI should not reference DAL).
UPDATE
I assume you wanted the service to use ICustomerBiz instead of the ICustomerRepository. If that is right the setup for Unity would look like this:
[TestMethod]
public void GetAll_with_Unity()
{
var container = new UnityContainer();
container.RegisterType<ICustomerRepository, CustomerRepository>();
container.RegisterType<ICustomerBiz, CustomerBizImp>();
container.RegisterType<ICustomerService, CustomerService>();
var svc = container.Resolve<ICustomerService>();
var all = svc.GetAll();
Assert.AreEqual(1, all.Count());
}
DI is much more about injecting a dependency inside your dipendency architecture, that's why it can not resolve, as is, layers isolation problem you face.
Production code can and should contain DI code, if it needed.
If we are talking about plugin-based architectureDI is one of most natural choices out there.
if we are talking about app behaviour change, like for example Logging system choice: save on remote server if connection present if not injject local logger for future sync with the server.
There are plenty of usages of DI in production, but all that is up to Architect to decide when, how and if use it.
In other words, there is no single rule of it use, it's not a hummer for any nail, so use it where you think it's approriate and use it wisely.