Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct use of Autofac in C# console application [duplicate]

Tags:

I'm new using Autofac so my apologies for the noob question. I read every manual in Internet explaining the basics when using Autofac (or any other tool like Structuremap, Unity, etc). But all the examples that I found are basics. I need to know how to implement Autofac deeper in my code. Let me try to explain what I need to know with this example, a console application.

class Program {     static void Main(string[] args)     {         var container = BuildContainer();         var employeeService = container.Resolve<EmployeeService>();         Employee employee = new Employee         {             EmployeeId = 1,             FirstName = "Peter",             LastName = "Parker",             Designation = "Photographer"         };          employeeService.Print(employee);     }      static IContainer BuildContainer()     {         var builder = new ContainerBuilder();         builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>();         builder.RegisterType<EmployeeService>();         return builder.Build();     } } 

This is simple and easy. What I'm trying to figure out is how do you implement this when you go deeper in the code. In this example, when you execute this line

employeeService.Print(employee); 

Let's assume the "Print" method is a little bit complex and need to use another dependencies/classes to accomplish his task. We are still using Autofac so I suppose we need to do something like the example above to create that dependencies. Is that correct? Inside my "print" method, when I need to use another class, I must create another container, fill it, use it with Resolve() and so on? There is an easier way to do that? A static class with all the dependencies needed can be consumed across all the solution? How? I hope to be clear. Maybe neither I can express what I need. :( Sorry for my poor English. I'm still learning it while I learn Autofac.

like image 364
Sebastian Garcia Avatar asked Jan 24 '18 00:01

Sebastian Garcia


People also ask

How do I register with Autofac?

First thing you need to do in order to use a container like Autofac is register all your dependencies. In Autofac you can do that a few ways but all of them rely on using the a ContainerBuilder . The ContainerBuilder relies on extension methods so make sure you have a using statement for the Autofac namespace.

What is the use of Autofac in MVC?

AutoFac provides better integration for the ASP.NET MVC framework and is developed using Google code. AutoFac manages the dependencies of classes so that the application may be easy to change when it is scaled up in size and complexity.


2 Answers

Static is the problem

The main issue with a console program is that the main Program class is mostly static. This is not good for unit testing and it's not good for IoC; a static class is never constructed, for example, so there is no chance for constructor injection. As a result, you end up using new in the main code base, or pull instances from the IoC container, which is a violation of the pattern (it's more of a service locator pattern at that point). We can get out of this mess by returning to practice of putting our code in instance methods, which means we need an object instance of something. But what something?

A two-class pattern

I follow a particular, lightweight pattern when writing a console app. You're welcome to follow this pattern which works pretty well for me.

The pattern involves two classes:

  1. The original Program class, which is static, very brief, and excluded from code coverage. This class acts as a "pass through" from O/S invocation to invocation of the application proper.
  2. An instanced Application class, which is fully injected and unit-testable. This is where your real code should live.

The Program Class

The O/S requires a Main entry point, and it has to be static. The Program class exists only to meet this requirement.

Keep your static program very clean; it should contain (1) the composition root and (2) a simple, "pass-through" entry point that calls the real application (which is instanced, as we will see).

None of the code in Program is worthy of unit testing, since all it does is compose the object graph (which would be different when under test anyway) and call the main entry point for the application. And by sequestering the non-unit-testable code, you can now exclude the entire class from code coverage (using the ExcludeFromCodeCoverageAttribute).

Here is an example:

[ExcludeFromCodeCoverage] static class Program {     private static IContainer CompositionRoot()     {         var builder = new ContainerBuilder();         builder.RegisterType<Application>();         builder.RegisterType<EmployeeService>().As<IEmployeeService>();         builder.RegisterType<PrintService>().As<IPrintService>();         return builder.Build();     }      public static void Main()  //Main entry point     {         CompositionRoot().Resolve<Application>().Run();     } } 

As you can see, extremely simple.

The Application class

Now to implement your Application class as if it were the One and Only Program. Only now, because it is instanced, you can inject dependencies per the usual pattern.

class Application {     protected readonly IEmployeeService _employeeService;     protected readonly IPrintService _printService;      public Application(IEmployeeService employeeService, IPrintService printService)     {         _employeeService = employeeService; //Injected         _printService = printService; //Injected     }      public void Run()     {         var employee = _employeeService.GetEmployee();         _printService.Print(employee);     } } 

This approach keeps separation of concerns, avoids too much static "stuff," and lets you follow the IoC pattern without too much bother. And you'll notice-- my code example doesn't contain a single instance of the new keyword, except to instantiate a ContainerBuilder.

What if the dependencies have dependencies of their own?

Because we follow this pattern, if PrintService or EmployeeService have their own dependencies, the container will now take care of it all. You don't have to instantiate or write any code to get those services injected, as long as you register them under the appropriate interface in the composition root.

class EmployeeService : IEmployeeService {     protected readonly IPrintService _printService;      public EmployeeService(IPrintService printService)     {         _printService = printService; //injected     }      public void Print(Employee employee)     {         _printService.Print(employee.ToString());     } } 

This way the container takes care of everything and you don't have to write any code, just register your types and interfaces.

like image 83
John Wu Avatar answered Oct 24 '22 02:10

John Wu


You can use inject dependencies via constructor (Autofac also supports property and method injection).

Usually when dependency registration is done, you should not use container inside classes, as it makes your class to be coupled to container, there could be some cases that you want to use child container (inner scope) in which you can define a specific class which does that and make your code independent of container.

In your example you just need to resolve IEmployeeService and all its dependencies will be resolved by container automatically.

Here is an example to demonstrate how you can achieve this:

using Autofac; using System; using System.Collections.Generic; using System.Linq;  namespace AutofacExample {     public class Employee     {         public int Id { get; set; }         public string Name { get; set; }     }      public interface IEmployeeRepository     {         Employee FindById(int id);     }      public interface IEmployeeService     {         void Print(int employeeId);     }      public class EmployeeRepository : IEmployeeRepository     {         private readonly List<Employee> _data = new List<Employee>()         {             new Employee { Id = 1, Name = "Employee 1"},             new Employee { Id = 2, Name = "Employee 2"},         };         public Employee FindById(int id)         {             return _data.SingleOrDefault(e => e.Id == id);         }     }      public class EmployeeService : IEmployeeService     {         private readonly IEmployeeRepository _repository;         public EmployeeService(IEmployeeRepository repository)         {             _repository = repository;         }         public void Print(int employeeId)         {             var employee = _repository.FindById(employeeId);             if (employee != null)             {                 Console.WriteLine($"Id:{employee.Id}, Name:{employee.Name}");             }             else             {                 Console.WriteLine($"Employee with Id:{employeeId} not found.");             }         }     }      class Program     {         static void Main(string[] args)         {             var container = BuildContainer();             var employeeSerive = container.Resolve<IEmployeeService>();             employeeSerive.Print(1);             employeeSerive.Print(2);             employeeSerive.Print(3);             Console.ReadLine();         }          static IContainer BuildContainer()         {             var builder = new ContainerBuilder();             builder.RegisterType<EmployeeRepository>()                    .As<IEmployeeRepository>()                    .InstancePerDependency();             builder.RegisterType<EmployeeService>()                    .As<IEmployeeService>()                    .InstancePerDependency();             return builder.Build();         }     } } 
like image 23
HojjatK Avatar answered Oct 24 '22 01:10

HojjatK