I have two classes: ImportBase.cs (Parent) and ImportFleet.cs (Child) which will in the future import a CSV file. Each child of ImportBase will implement a different implementation of the actual import code.
The approriate child class to use is determined in a Service class where the correct class is instantiated and the import method called. All is going well up until this point.
The problem is that I also want to Dependency Inject some repository classes into ImportBase and it’s inherited classes (as I have attempted in the code below):
ImportBase.cs
namespace WebApi.Services.Import.Investments
{
interface IImport
{
public void Import(IFormFile file, int UserId);
}
public abstract class ImportBase : IImport
{
public abstract void Import(IFormFile file, int UserId);
protected List<InvestmentTransactionType> transactionTypes = new();
protected IInvestmentEntityRepository _investmentEntityRepository;
public ImportBase(IInvestmentEntityRepository investmentEntityRepository)
{
_investmentEntityRepository = investmentEntityRepository;
}
}
}
ImportFleet.cs
namespace WebApi.Services.Import.Investments
{
public class ImportFleet : ImportBase
{
public ImportFleet(IInvestmentEntityRepository investmentEntityRepository) : base(investmentEntityRepository)
{
}
public override void Import(IFormFile file, int UserId)
{
}
}
}
InvestmentService.cs
namespace WebApi.Services
{
public interface IInvestmentService
{
public void Import(IFormFile file, int UserId, int InvestmentEntityId);
}
public class InvestmentService: IInvestmentService
{
public void Import(IFormFile file, int UserId, int InvestmentEntityId)
{
IImport importService = null;
string investmentEntity = ImportBase.determineInvestmentEntityFromCsv(file);
switch(investmentEntity)
{
case "fleet":
importService = new ImportFleet(); // problem is here
break;
}
if (importService != null)
{
importService.Import(file, UserId);
}
}
}
}
The problem is the following line:
importService = new ImportKuflink();
Because I only determine which child class to instantiate at run time, I cannot take advantage of DI here.
Under normal circumstances I would make the Import classes a DI based service so all dependencies are available, however I have to create the instance at run time so this I don’t think is possible.
Is there a way to accomplish the above?
3
Answers
Yes, of course there is a way to accomplish this. But I guess the DI container you are using (like from MS) won’t help you here.
I’ve been fiddling with crap like this for like two years so far and still am busy with it. Two years of creating my own IoC framework.
Usual DI/IoC microkernels follow OCP and other really mandatory concepts and patterns. What I’ve done is leaving one single small door open. I won’t bore you with details. The fundamental idea is that a class must be decorated with the appropriate attributes in code, and then is able to call the microkernel within its constructor (which has been called by a simple "var foo = new Barney();") to let an entity be modified like it had been created by the microkernel.
There is no(t yet a) way to hook into the plain new() code. Some cheer this, some don’t. I’m with the cheerleaders here. Why? Side-effects.
Imagine this:
Okay? Let’s assume you’d modified the new() process by whatever, then another user of your code goes:
and this code throws an exception, because for whatever reason your modifying code set the number to 7.
Now look at this code (from a unit test):
Clean code isn’t always easily readable, but the aspects/attributes will raise coder’s awareness.
PS: I posted the unit test code on purpose to show you what’s happening.
Short version:
You can inject a factory which has the services injected into it.
Then inject the factory as a dependency in your Service class.
Here’s a simplified version of your code that demonstrates how you can populate an instance of an object from a DI service container.
In your
InvestmentService
:IServiceProvider
.ActivatorUtilities
to get a fully DI’d instance of your object.IDisposable
. I’ve included an async version if you use anything that needs aIAsyncDisposable
.