I am writing a simple console application that takes care of connecting to the database, selecting a particular product from it (based on the provided criteria) and doing some processing with this product. I am storing command-line arguments to an instance of this class:
public class Arguments { public string ConnectionString { get; set; } public int ProductId { get; set; } public string ProductName { get; set; } } At some point, I need to fetch the product from the database. I am using the following repository for that:
public interface IProductRepository { Product GetById(int productId, string connectionString); Product GetByName(string productName, string connectionString); } Then, I inject an implementation of the repository to the class that uses it, e.g:
public class ProductProcessor { private readonly IProductRepository productRepository; public ProductProcessor(IProductRepository productRepository) { this.productRepository = productRepository; } public void Process(Arguments arguments) { Product productToProcess; if (!string.IsNullOrEmpty(arguments.ProductName)) { productToProcess = productRepository.GetByName(arguments.ProductName, arguments.ConnectionString); } else { productToProcess = productRepository.GetById(arguments.ProductId, arguments.ConnectionString); } // .... } } This is working, but what I don't like about the design is that every method of the IProductRepository has a connectionString argument. If there was no dependency injection involved, I would probably rewrite it like the following:
public void Process(Arguments arguments) { Product productToProcess; ProductRepository productRepository = new ProductRepository(arguments.ConnectionString); if (!string.IsNullOrEmpty(arguments.ProductName)) { productToProcess = productRepository.GetByName(arguments.ProductName); } else { productToProcess = productRepository.GetById(arguments.ProductId); } // .... } This enables me to have simpler and easier-to-use interface. Of course, now the ProductRepository does not have parameterless constructor and it is difficult to use with DI container. Ideally, I would like to have the best of both worlds, i.e. to initialize the ProductRepository with the connection string from constructor and remove the connection string from its methods. What is the best approach to achieve this?
Some approaches I've already considered:
- Add a method
Initialize(string connectionString)to theIProductRepositorythat would basically serve as a constructor. Obvious drawback is that I now need to check whether theInitializehas been called before doing anything inGetByIdorGetByNamemethods. - Do not use constructor injection and use Service Locator pattern instead to instantiate
ProductRepository. I don't like Service Locator much, but this is probably only possible solution.
Is there any better alternative?
EDIT: From the answers I see that I should have posted a bit more context. I am using Ninject as my DI container. In Main method in my Program.cs, I register all dependencies to the container and instantiate the class that serves as an entry-point to the application:
public static void Main(string[] args) { StandardKernel kernel = new StandardKernel(); kernel.Bind<IArgumentsParser>().To<IArgumentsParser>(); kernel.Bind<IProductProcessor>().To<ProductProcessor>(); kernel.Bind<IProductRepository>().To<ProductRepository>(); MainClass mainClass = kernel.Get<MainClass>(); mainClass.Start(args); } The MainClass looks like the following:
public class MainClass { private readonly IArgumentsParser argumentsParser; private readonly IProductProcessor productProcessor; public MainClass(IArgumentsParser parser, IProductProcessor processor) { argumentsParser = parser; productProcessor = processor; } public void Start(string[] args) { Arguments parsedArguments = argumentsParser.Parse(args); productProcessor.Process(parsedArguments ); } } This enables me to have a dependency to Ninject and creation of the whole graph in one place only (the Main method) and the rest of the application knows nothing about DI and containers.
I'd like to keep it that way, if possible.