Say I have this interface:
public interface IExample
{
string GetSupportedExtension();
void DoWork(Stream input);
}
a concrete implementation like:
public class PdfExample : IExample
{
public string GetSupportedExtension() => ".pdf";
public void DoWork(Stream input)
{
// Do something with the PDF
}
}
A Factory/Resolver:
public class ExampleResolver(IEnumerable<IExample> examples) : IExampleResolver
{
public IExample? Resolve(string extension)
{
return examples.FirstOrDefault(e => e.GetSupportedExtension() == extension);
}
}
Used in a controller like this:
[ApiController]
public class ExampleController(IExampleResolver resolver) : ControllerBase
{
[HttpPost("/")]
public async Task<IActionResult> DoWork([FromForm] IFormFile file)
{
string extension = Path.GetExtension(file.FileName).ToLower();
IExample? example = resolver.Resolve(extension);
if (example == null)
{
return BadRequest();
}
example.DoWork(file.OpenReadStream());
return Ok();
}
}
Which I thought was a class/interface structure that made sense and let the DI framework construct the objects for me, however there's a problem if I have another concrete implementation:
public class ZipExample(IExampleResolver resolver) : IExample
{
public string GetSupportedExtension() => ".zip";
public void DoWork(Stream input)
{
// Load zip from stream
foreach (var file in zip)
{
IExample example = resolver.Resolve(file.Extension);
example.DoWork(file.GetInputStream());
}
}
}
Now there's a circular reference as the resolver needs the ZipExample to be constructed but the ZipExample also needs the resolver.
I know other questions asked similar to this would mention refactoring to remove the circular dependency but I struggle to see a good way of doing that here with the circular/recursive nature of what I need to do, any suggestions?
I also know I could pass in IServiceProvider instead but read it was bad practice as it hides a class's dependencies so I guess I'm looking to see if there's another solution or what people would recommend? Thanks
ZipExampleas a special case in yourExampleResolver. And while passing aIServiceProvidershould normally be avoided, it might be the better than the alternatives in some cases. While DI is useful, it is sometimes simpler to do something else, like using an explicit factory.