Reputation: 193
I have the following scenario:
I got a service ICompanyDocumentsService
with a single implementation CompanyDocumentsService
which I register in my Startup
class:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ICompanyDocumentService, CompanyDocumentService>();
}
I need this service in many places, and it doesn't bother me using DI in Constructor. However, there is one place where I need it injected in a Method (or probably in a property would be even better):
public abstract class CompanyDocumentBase
{
public abstract object GetAllProperties(Employee employee, string properties,
CompanyDocumentReferenceData documentReferenceData);
// blah, blah, lots of Formatting Methods
private CompanyDocumentService CompanyDocumentService { get; set; } // **inject service here**
public string GetFormattedEmployeeIndividualEmploymentAgreementNumber(Employee employee,
ICompanyDocumentService companyDocumentService = null) // **optional because
//inherited class doesn't need to know anything about this service, it concerns only it's result**
{
companyDocumentService = CompanyDocumentService;
var test =
companyDocumentService.GetEmloyeeIndividualEmploymentAgreementNumber(employee.Id);
return string.Empty;
}
}
There are many classes inheriting CompanyDocumentBase
which are only concerned in it's method results, as mentioned above, that's why that parameter is optional, and that's why I don't need injecting DI in constructor, thus the inheriting classes won't be needing that.
public class JobDescriptionCompanyDocument : CompanyDocumentBase
{
public override object GetAllProperties(Employee employee,
string properties, CompanyDocumentReferenceData documentReferenceData)
{
var document = JsonConvert.DeserializeObject<JobDescriptionModel>(properties);
document.IndividualEmploymentAgreementNumber = GetEmployeeIndividualEmploymentAgreementNumber(employee);
return document;
}
}
Is there any simple way to achieve this? Preferable without needing to install a separate library like Unity or Autofac.
Ideal it would be to somehow get the instance of CompanyDocumentsService
directly into that property, something like:
private CompanyDocumentService CompanyDocumentService => Startup.Services.blah that instance
Upvotes: 3
Views: 4075
Reputation: 10035
One hack way (personally I wouldn’t recommend it), is after your container is built, you could resolve an instance of IHttpContextAccessor
and set it to static class, e.g. IoC
Then you could do private CompanyDocumentService CompanyDocumentService => IoC.HttpContextAccessor.HttpContext.RequestServices.GetService()
.
The interface is a singleton, and provides access to scoped services from a static context.
Note you might have to explicitly register HttpContextAccessor: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1
UPDATE
What I'd recommend
If you are open to object factories, and changing the way how DocumentBase
is instantiated, try make a factory, and whenever you need an instance of DocumentBase
, only use the factory to create it:
public abstract class CompanyDocumentBase
{
// Use internal so that access only limited to friendly assembly
internal CompanyDocumentService CompanyDocumentService { get; set; }
}
// Inject this class to where you need to create an instance of CompanyDocumentBase
public class CompanyDocumentFactory<T> where T : CompanyDocumentBase
{
private readonly IServiceProvider _services;
// DI contaiener has implicit IServiceProvider support, when you do this on constructor, it injects service provider of *current* scope
// If this factory is registered as singleton, you need to use IHttpContextAccessor to use request's service provider in Create method
// because your DocumentService is scoped.
public CompanyDocumentFactory(IServiceProvider services)
{
_services = services;
}
public T Create()
{
// Create an instance of document use your current method.
var instance = new T();
instance.CompanyDocumentService = _services.GetRequiredService<ICompanyDocumentService>();
return instance;
}
}
Upvotes: 3