Just used DI containers in MVC projects, via constructor injection, and I'd need to now inject a service into a console app. I'm using Autofac so I followed what I found here.
The code would be something like this:
The service the console app depends on:
public interface IBatchService
{
void WriteInformation(string input);
}
public class BatchService : IBatchService
{
public void WriteInformation(string input)
{
Console.WriteLine(input);
Console.ReadKey();
}
}
The console app main method
public class Program
{
private static IContainer Container { get; set; }
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<BatchService.BatchService>().As<IBatchService>();
Container = builder.Build();
using (var scope = Container.BeginLifetimeScope())
{
var batchService = scope.Resolve<IBatchService>();
batchService.WriteInformation("Injected!");
}
}
It's using the Service Locator if I'm not wrong (I'm trying to get my head around all of this), which I would be likely to avoid in case I could as it's thoroughly discussed in the web. But can I actually avoid it in a console app? Can any see any big flaw with this implementation?
The main method is almost as simple as this one, only one call to one service (although the service doesn't write a console). And that's the only code in the project in fact.
1 Answer 1
Your Main
method has too many responsibilities: it's your application's composition root, and it's executing the program's logic... if printing to Console
can be considered logic.
There's not enough meat in this project to even justify using DI/IoC: you have a service that nothing depends on.
If you had, say, an Application
class:
public interface IApplication
{
void Run();
}
public class Application : IApplication
{
private readonly IBatchService _service;
public Application(IBatchService service)
{
_service = service;
}
public void Run()
{
_service.WriteInformation("Injected!");
}
}
...then composition would refer to resolving all dependencies of an Application
instance, regardless of how complex that might be.
The Main
method would only ask for an Application
instance (let's call it app
), and your IoC container would do its job - the Main
method would only call app.Run();
, and the rest of the application doesn't need to know there's an IoC container involved.
You don't need the static IContainer
field; I've never used Autofac (you should look into Ninject, seems much simpler), but your IoC container should only live (and die) in the Main
method - if you're using it anywhere else, you're doing something wrong (Service Locator).
I'd make it something like this:
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<BatchService.BatchService>().As<IBatchService>();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<IApplication>();
app.Run();
}
}
Then your Main
method is done with. If you're going to expand the application, you just inject new dependencies, and consume them.
Actually, this:
builder.RegisterType<BatchService.BatchService>().As<IBatchService>();
I'd move elsewhere; as your application grows, you'll be registering tons of stuff here, registration is a concern of its own, that deserves its own class IMO. That class can be static
for all you care, it's infrastructure stuff, its only goal is to spit out a configured IContainer
.
The configuration class could look like this:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.RegisterType<BatchService.BatchService>().As<IBatchService>();
// add other registrations here...
return builder.Build();
}
}
Leaving your Main
method like this:
static void Main(string[] args)
{
var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<IApplication>();
app.Run();
}
}
-
\$\begingroup\$ Thanks @Mat'sMug I completely agree that this project doesn't need injection, actually in the solution there are a few of the like this. With just classes of 40 lines calling one service each, so maybe I should raise that to the "owner" of the code who wanted me to use the container in all of them \$\endgroup\$mitomed– mitomed2014年07月05日 17:18:05 +00:00Commented Jul 5, 2014 at 17:18
-
5\$\begingroup\$ One small thing in Mat'sMug code is that we should add builder.RegisterType<Application>().As<IApplication>(); to the container config \$\endgroup\$mitomed– mitomed2014年07月07日 09:54:48 +00:00Commented Jul 7, 2014 at 9:54
-
\$\begingroup\$ I have created sample app code from the above answer as console app with AutoFac DI and No-DI gist.github.com/greatb/1bfd9a5bd579a65e4eee1c4b074dacd0 \$\endgroup\$Bala– Bala2016年11月02日 00:28:16 +00:00Commented Nov 2, 2016 at 0:28