locked
Dependency Injection RRS feed

  • Question

  • User-305496339 posted

    Hi Friends,

    I have a question concerning why is the Interface and the Class with the Implementation in separate files when we use Dependency Injection. I normally put the Class with the Implementation in the same file as the Interface.  Thanks !!!

    Friday, January 8, 2021 5:48 AM

Answers

  • User-1545767719 posted

    Although I do not know why you think that the interface and class need to be put in a separate file, it is not required for the Dependency Injection to work. As a matter of fact, the following console application which includes everything in a same file works as expected. The same can apply to the ASP.NET Core application.

    using System;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace ConsoleAppDependencyInjection
    {
        class Program
        {
            static void Main(string[] args)
            {
                IServiceCollection services = new ServiceCollection();
    
                services.AddTransient<IOrderRepository, SqlOrderRepository>();
                services.AddSingleton<ILogger, Logger>();
                services.AddScoped<IEventPublisher, EventPublisher>();
                services.AddTransient<CancelOrderHandler>();
    
                var provider = services.BuildServiceProvider();
                var handler = provider.GetRequiredService<CancelOrderHandler>();
    
                var orderId = Guid.NewGuid();
                var command = new Order { OrderId = orderId };
                handler.Handle(command);
            }
        }
    
        public class CancelOrderHandler
        {
            private readonly IOrderRepository repository;
            private readonly ILogger logger;
            private readonly IEventPublisher publisher;
    
            // Use constructor injection for the dependencies
            public CancelOrderHandler(IOrderRepository repository, 
                                      ILogger logger, 
                                      IEventPublisher publisher)
            {
                this.repository = repository;
                this.logger = logger;
                this.publisher = publisher;
            }
    
            public void Handle(Order command)
            {
                this.logger.Log($"Cancelling order {command.OrderId}");
                var order = this.repository.GetById(command.OrderId);
                order.OrderStatus = "Cancelled";
                this.repository.Save(order);
                this.publisher.Publish(order);
            }
        }
    
        public interface IOrderRepository
        {
            public Order GetById(Guid orderId);
    
            public void Save(Order order);
        }
    
        public class SqlOrderRepository : IOrderRepository
        {
            private readonly ILogger logger;
    
            // Use constructor injection for the dependencies
            public SqlOrderRepository(ILogger logger)
            {
                this.logger = logger;
            }
    
            public Order GetById(Guid orderId)
            {
                this.logger.Log($"Getting Order {orderId}");
    
                // Retrieve from db.・・・のつもり
                var order = new Order
                {
                    OrderId = orderId,
                    ProductName = "911-GT3",
                    OrderStatus = "Ordered"
                };
    
                return order;
            }
    
            public void Save(Order order)
            {
                this.logger.Log($"Saving order {order.OrderId}");
                // Save to db.
            }
        }
    
        public interface ILogger
        {
            void Log(string log);
        }
    
        public class Logger : ILogger
        {
            public void Log(string log)
            {
                Console.WriteLine(log);
            }
        }
    
        public interface IEventPublisher
        {
            public void Publish(Order order);
        }
    
        public class EventPublisher : IEventPublisher
        {
            public void Publish(Order order)
            {
                Console.WriteLine($"Publish order {order.OrderId}, " +
                    $"{order.ProductName}, {order.OrderStatus}");
            }
        }
    
        public class Order
        {
            public Guid OrderId { get; set; }
    
            public string ProductName { get; set; }
    
            public string OrderStatus { get; set; }
        }
    }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 8, 2021 7:23 AM
  • User1686398519 posted

    Hi rkrex, 

    You can put them in the same file.

    But generally speaking, putting them in a separate file has the following advantages:

    1. The project structure is clear.
    2. This provides convenience for future maintenance and modification.
      • Relatively speaking, the interface file is rarely modified, and is usually used to modify the implementation class of the interface.

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 8, 2021 8:03 AM

All replies

  • User-1545767719 posted

    Although I do not know why you think that the interface and class need to be put in a separate file, it is not required for the Dependency Injection to work. As a matter of fact, the following console application which includes everything in a same file works as expected. The same can apply to the ASP.NET Core application.

    using System;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace ConsoleAppDependencyInjection
    {
        class Program
        {
            static void Main(string[] args)
            {
                IServiceCollection services = new ServiceCollection();
    
                services.AddTransient<IOrderRepository, SqlOrderRepository>();
                services.AddSingleton<ILogger, Logger>();
                services.AddScoped<IEventPublisher, EventPublisher>();
                services.AddTransient<CancelOrderHandler>();
    
                var provider = services.BuildServiceProvider();
                var handler = provider.GetRequiredService<CancelOrderHandler>();
    
                var orderId = Guid.NewGuid();
                var command = new Order { OrderId = orderId };
                handler.Handle(command);
            }
        }
    
        public class CancelOrderHandler
        {
            private readonly IOrderRepository repository;
            private readonly ILogger logger;
            private readonly IEventPublisher publisher;
    
            // Use constructor injection for the dependencies
            public CancelOrderHandler(IOrderRepository repository, 
                                      ILogger logger, 
                                      IEventPublisher publisher)
            {
                this.repository = repository;
                this.logger = logger;
                this.publisher = publisher;
            }
    
            public void Handle(Order command)
            {
                this.logger.Log($"Cancelling order {command.OrderId}");
                var order = this.repository.GetById(command.OrderId);
                order.OrderStatus = "Cancelled";
                this.repository.Save(order);
                this.publisher.Publish(order);
            }
        }
    
        public interface IOrderRepository
        {
            public Order GetById(Guid orderId);
    
            public void Save(Order order);
        }
    
        public class SqlOrderRepository : IOrderRepository
        {
            private readonly ILogger logger;
    
            // Use constructor injection for the dependencies
            public SqlOrderRepository(ILogger logger)
            {
                this.logger = logger;
            }
    
            public Order GetById(Guid orderId)
            {
                this.logger.Log($"Getting Order {orderId}");
    
                // Retrieve from db.・・・のつもり
                var order = new Order
                {
                    OrderId = orderId,
                    ProductName = "911-GT3",
                    OrderStatus = "Ordered"
                };
    
                return order;
            }
    
            public void Save(Order order)
            {
                this.logger.Log($"Saving order {order.OrderId}");
                // Save to db.
            }
        }
    
        public interface ILogger
        {
            void Log(string log);
        }
    
        public class Logger : ILogger
        {
            public void Log(string log)
            {
                Console.WriteLine(log);
            }
        }
    
        public interface IEventPublisher
        {
            public void Publish(Order order);
        }
    
        public class EventPublisher : IEventPublisher
        {
            public void Publish(Order order)
            {
                Console.WriteLine($"Publish order {order.OrderId}, " +
                    $"{order.ProductName}, {order.OrderStatus}");
            }
        }
    
        public class Order
        {
            public Guid OrderId { get; set; }
    
            public string ProductName { get; set; }
    
            public string OrderStatus { get; set; }
        }
    }

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 8, 2021 7:23 AM
  • User1686398519 posted

    Hi rkrex, 

    You can put them in the same file.

    But generally speaking, putting them in a separate file has the following advantages:

    1. The project structure is clear.
    2. This provides convenience for future maintenance and modification.
      • Relatively speaking, the interface file is rarely modified, and is usually used to modify the implementation class of the interface.

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 8, 2021 8:03 AM