Unity - How to avoid circular reference? - c#

I'm trying to implement dependency injection in my ASP.NET MVC Project using Unity and would like some advice on how to avoid circular references.
At my work we used to implement the service locator pattern, which returned a singleton for each individual service of the application.
public class ServiceWrapper
{
private UserService _userService;
private ProductService _productService;
public UserService User
{
if(_userService == null)
{
_userService = new UserService();
}
return _userService;
}
public ProductService Product
{
if(_productService == null)
{
_productService = new ProductService();
}
return _productService;
}
}
Then in the controller, you could easily access all the services by instantiating the ServiceWrapper and calling methods, like:
private ServiceWrapper _services = new ServiceWrapper();
public ActionResult Index()
{
List<Product> products = _services.Product.GetProducts();
return View(products);
}
Setting up DI using Unity was a breeze. I created a container in Application_Start() (global.asax), like so:
var container = new UnityContainer();
container.RegisterType<IUserService, UserService>(new ContainerControlledLifetimeManager());
container.RegisterType<IProductService, ProductService>(new ContainerControlledLifetimeManager());
container.RegisterType<IServiceWrapper, ServiceWrapper>(new ContainerControlledLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
The ServiceWrapper is registered as a Singleton. And implemented constructor injection as follows:
public class ProductController: Controller
{
private IServiceWrapper _services;
public ProductController(IServiceWrapper services)
{
_services = services;
}
public ActionResult Index()
{
List<Product> products = _services.Products.GetProducts();
return View(products);
}
That worked beautifully. But then I came accross the problem.
We like every service to also have a property containing the ServiceWrapper, so that you can easily access the other services from within another, like so:
public class ProductService
{
private IServiceWrapper _services;
public ProductService(IServiceWrapper services)
{
_services = services;
}
public IServiceWrapper Services { get { return _services; } }
}
But when I implemented constructor injection of the ServiceWrapper in the individual services, it resulted in a stackoverflow exception due to circular referencing.
I read that Unity does not support circular referencing. Is there a (solid) way around this. Or should I implement a different architecture? If so, could you recommend a solution?

The way this is typically done is to declare just the services you need and have them ctor-injected. Don't inject a "ServiceWrapper" which just contains everything. Inject what you need. As the container builds up your type you don't have to worry about providing the services. They will be just there.
The development workflow often goes like this:
Add a new dependency injected field
Delete the existing ctor
Regenerate the ctor using Resharper: alt+ins, generate ctor.

Related

ASP.NET MultiLayer App Migration To ASP.NET Core

I migrate my ASP.NET multi layer project to ASP.NET Core. I was using Ninject for DI in my old project and i could call it as follow in my BLL.
public void IamMethodInBll()
{
...
//i could call ninject like this.
var prodCalcManager = Const.NinjectKernel.Get<IProdCalcService>();
prodSurr.CalculatedPrice = prodCalcManager.GetMyMethod()
..
}
In now i use ASP Net Core's DI system of course. But how can i call the service locator in business layer for ASP.NET core? I need your samples and suggestions.
In Startup.cs
services.AddScoped<IProdCalcService,ProdCalcManager>();
In BLL
public void IamMethodInBll()
{
...
//What's the proper way to call another service in BLL
//How can i get the service provider in BLL
var prodCalcManager = _serviceProvider.GetService<IProdCalcService>();
prodSurr.CalculatedPrice = prodCalcManager.GetMyMethod()
..
}
The correct answer is: Dont use ServiceLocator. Register your services using the services.AddScoped<T> like you are and then add that to the constructor of the class you want to use it in.
Like so:
services.AddScoped<IProdCalcService,ProdCalcManager>();
Then your class looks like this:
public class MyClass()
{
private IProdCalcService calcService;
public MyClass(IProdCalcService calcService)
{
this.calcService = calcService;
}
public void IamMethodInBll()
{
//...
prodSurr.CalculatedPrice = calcService.GetMyMethod();
//...
}
}
It is better to DONT USE Service Locator but in this case you can inject ServiceProvider to your Controller or better solution is wrap and abstract ServiceProvider in a Container that can be injectable simply.
I prefer to use 3rd party DI Containers for use in this situation, specially CastleCore can be good choice.
public interface IContainer
{
T Resolve<T>();
}
public class ServiceProviderContainer : IContainer
{
private IServiceProvider _serviceProvider;
public ServiceProviderContainer(IServiceProvider serviceProvider)
{
this._serivceProvider = serviceProvider;
}
public T Resolve<T>()
{
return _seriveProvider.GetService<T>();
}
}
public class MyController : Controller
{
private IContainer contianer;
public MyController(IContainer container)
{
this._container = container;
}
public IActionResult Get()
{
var service = _container.Resolve<IUserRepository>();
}
}

How to do Caching and Dependency Injection outside of Controller in .NET Core MVC

I've used DI purely for tests within a controller and I'm oddly having a real hard time using it outside of a controller. I had a static caching class called caching engine, but apparently DI and static classes don't mix well, so I decided to make it non-static instead. However, I can't get this to work well and I'm not sure what the best approach is. I have a controller that I need to pass products and send them to the view. However, for speed improvements, I'd like to use memory caching, but I'm really confused on the best design here. I'd like to know the best way to do this.
1) How does instantiating a new class work with DI if you don't pass the dependencies?
2) Should I inject my memorycache and product repository into the controller and then pass them into the cachingengine constructor? That seems like a lot of unnecessary parameter passing, so I didn't like this.
3) Should I just instantiate a MemoryCache class in the caching engine and not worry about DI?
4) Should I just switch the CachingEngine back to a static class?
Thank you for your help and advice. It is much appreciated.
Here's the Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//Add Dependencies
services.AddTransient<IProductRepository, ProductRepository>();
//Extention method that sets up the shared objects used in MVC apps
services.AddMvc();
services.AddMemoryCache();
....
}
}
Here's the Controller
public class MainController : Controller
{
private CachingEngine engine;
public MainController()
{
//This isn't valid, missing parameters
engine = new CachingEngine();
}
public IActionResult Index()
{
var products = CachingEngine.GetProducts();
....
}
}
And here's the caching class:
public class CachingEngine
{
private readonly IMemoryCache memoryCache;
private IProductRepository prodRepo;
public CachingEngine(IMemoryCache memory, IProductRepository rep)
{
memoryCache = memoryCache;
prodRepo = rep;
}
public List<Product> GetProducts()
{
var cacheKey = "Products";
List<Product> prods;
if (memoryCache.TryGetValue(cacheKey, out prods))
{
return prods;
}
else
{
memoryCache.Set(cacheKey, prodRepo.Products);
return prods;
}
}
}
First off, to clarify, a static class cannot be instantiated so how could you inject instantiations into its constructor using a dependency injection framework. It is not that static classes do not work well with DI, they do not work at all and make no sense in the context of dependency injection.
Your Controller needs a CachingEngine, so you need to inject it, a simple rule of setting up DI in your software: do not use the new operator.
Anytime you use the new operator you are tightly coupling your code to a particular type and you have the exact problem that Dependency Inject is trying to solve.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//Add Dependencies
services.AddTransient<IProductRepository, ProductRepository>();
//configure DI for IMemoryCache and CachingEngine
services.AddTransient<IMemoryCache, MyMemoryCacheClass>();
services.AddTransient<MyICachingEngineInterface, CachingEngine>();
//Extention method that sets up the shared objects used in MVC apps
services.AddMvc();
services.AddMemoryCache();
....
}
}
public class MainController : Controller
{
private readonly MyICachingEngineInterface _cachingEngine;
public MainController(MyICachingEngineInterface cachingEngine)
{
_cachingEngine = cachingEngine;
}
public IActionResult Index()
{
var products = _cachingEngine.GetProducts();
....
}
}

Getting IServiceProvider as a Dependency to get Optional Dependencies in ASP.NET Core

Is it a bad practice to get IServiceProvider injected to a service class, as a means to get optional dependencies in ASP.NET Core 2.0? Does this break Explicit Dependency Principal?
I've a class which requires an Optional Service, EventBus. If the EventBus is registered, I want the service class to publish an event, if not simply ignore it.
public class SomeService {
private readonly IServiceProvider _serviceProvider;
public SomeService(IServiceProvider serviceProvider) {
_serviceProvider = serviceProvider;
}
public SomeAction() {
var eventBus = _serviceProvider.GetService(typeof(IEventBus)) as IEventBus;
if (eventBus != null) {
eventBus.publish("SomeAction Happened!");
}
}
}
I can't see how to create optional dependencies with the built in IoC Container of ASP.NET Core 2.0.
EDIT: Any suggestions how to implement optional dependencies in ASP.NET Core? Or any other strategy to get the same effect without the anti-pattern?
It would not be considered optional if it is required directly by the method in order for it to function correctly.
It should be explicitly injected as a dependency
public class SomeService {
private readonly IEventBus eventBus;
public SomeService(IEventBus eventBus) {
this.eventBus = eventBus;
}
public SomeAction() {
if (eventBus != null) {
eventBus.publish("SomeAction Happened!");
}
//...
}
}
otherwise consider passing it explicitly to the method as an optional dependency
public SomeAction(IEventBus eventBus = null) {
if (eventBus != null) {
eventBus.publish("SomeAction Happened!");
}
//...
}
The Explicit Dependencies Principle states:
Methods and classes should explicitly require (typically through method parameters or
constructor parameters) any collaborating objects they need in order
to function correctly.
emphasis mine
Injecting IServiceProvider is debated as an anti-pattern as it follows a service locator pattern.
There are some exceptions for example if the dependent class is being also used as a factory.
Injecting IServiceProvider is an implementation of the Service Locator anti-pattern. Prevent from doing this. Neither should dependencies be optional. This introduces complexity. Instead, use the Null Object pattern. Making the dependency required, simplifies the consumer and its test.
In other words, SomeService should look as follows:
public class SomeService {
private readonly IEventBus _bus;
public SomeService(IEventBus bus) {
_bus = bus ?? throw new ArgumentNullException(nameof(bus));
}
public SomeAction() {
eventBus.publish("SomeAction Happened!");
}
}
In your Composition Root you use a NullEventBus implementation in case no real implementation exists. This should be as easy as this:
public class NullEventBus : IEventBus
{
public void publish(string message) {
// do nothing.
}
}
Since this implementation does nothing, it can be injected into all consumers.

Unity container can't resolve my class

I am trying to implement Unity as an IoC container in a learning project I am working on. I have a layered application, UI->Service->Logic->DataAccess.
It's an MVC Application on the UI side.
In my controller, I have a constructor:
public HomeController()
{
_container = new UnityContainer();
_container.RegisterType<IUserService, UserService>();
_container.RegisterType<IUserLogic, UserLogic>();
}
I then attempt to use the IService in one of my methods:
var service = _container.Resolve<IUserService>();
ReplyDto reply = service.Login(model.Email, model.Password);
But then get this error:
Resolution of the dependency failed, type = "Services.IUserService",
name = "(none)".
I'm not sure why it's saying this. Do I maybe have an issue with MVC and the constructor? Breakpoint on the Resolve line shows that the _container does have the interface I am trying to resolve.
My UserService class has this in the constructor:
private IUserLogic _userlogic;
public UserService(IUserLogic logic)
{
_userlogic = logic;
}
The Logic layer class is defined like this:
public class UserLogic : IUserLogic
{
public ILog _logger;
public IData _data;
public UserLogic(IData data, ILog logger)
{
_data = data;
_logger = logger;
}
I am still in the process of propagating the IoC patter down through all layers.
And finally, the data access layer is defined as:
public class Data :IData
{
Log _logger = new Log();
MyEntities entities;
public Data()
{
entities = new MyEntities();
var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance;
}
My _container has reference to the IUserLogic interfaces and which concrete class to use.
UserLogic(IData data, ILog logger) - neither IData nor ILog shown as registered in container - so if code is exactly like you have in the post it is the reason why IUserLogic can't be resolved when unity tries to pass argument to UserService(IUserLogic) constructor.
Fix: register all dependencies (recursively)
To achieve that consider:
make sure types with no dependencies has constructors without arguments
register instances instead of types - if that works for your system
make constructors depend on concrete types (as all concrete types by default registered with Unity) - not testable choice...
provide parameters for all non-interface/non class arguments like int/string (How resolve a dependency with unity passing arguments in the constructor?)
You should register container in the Global.asax.cs
public class Global : HttpApplication
{
private void Application_Start(object sender, EventArgs e)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IUserService, UserService>();
container.RegisterType<IUserLogic, UserLogic>();
UnityDependencyResolver dependencyResolver = new UnityDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver;
DependencyResolver.SetResolver(dependencyResolver);
}
}

Dependency Injection in Model classes (entities)

I am building an ASP.NET Core MVC application with Entity Framework Code-First.
I implemented a simple repository pattern, providing basic CRUD operations for all the model classes I have created.
I chose to follow all the recommendations provided in docs and DI is one of these.
In ~~.NET 5~~ (6 years later update: .net 5 was the alpha name of .net core 1.0) dependency injection works very well for any class that we do not directly instantiate (e.g.: controllers, data repositories, ...).
We simply inject them via the constructor, and register the mappings in the Startup class of the application :
// Some repository class
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
}
// In startup.cs :
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyRepository, MyRepository>();
The problem is that in some of my model classes, I would like to inject some of the dependencies I have declared.
But I think that I cannot use the constructor injection pattern because model classes are often explicitly instantiated. Therefore, I would need to provide myself with the dependencies, which I can't.
So my question is: is there another way than constructor injection to inject dependencies, and how? I was for example thinking of an attribute pattern or something like that.
As I already explained in a comment, when creating an object using new, there is nothing from the dependency injection framework that is involved in the process. As such, it’s impossible for the DI framework to magically inject things into that object, it simply doesn’t know about it.
Since it does not make any sense to let the DI framework create your model instances (models are not a dependency), you will have to pass in your dependencies explicitly if you want the model to have them. How you do that depends a bit on what your models are used for, and what those dependencies are.
The simple and clear case would be to just have your model expect the dependencies on the constructor. That way, it is a compile time error if you do not provide them, and the model has access to them right away. As such, whatever is above, creating the models, is required to have the dependencies the model type needs. But at that level, it’s likely that this is a service or a controller which has access to DI and can request the dependency itself.
Of course, depending on the number of dependencies, this might become a bit complicated as you need to pass them all to the constructor. So one alternative would be to have some “model factory” that takes care of creating the model object. Another alternative would also be to use the service locator pattern, passing the IServiceCollection to the model which can then request whatever dependencies it needs. Note that is generally a bad practice and not really inversion of control anymore.
Both these ideas have the issue that they modify the way the object is created. And some models, especially those handled by Entity Framework, need an empty constructor in order for EF to be able to create the object. So at that point you will probably end up with some cases where the dependencies of your model are not resolved (and you have no easy way of telling).
A generally better way, which is also a lot more explicit, would be to pass in the dependency where you need it, e.g. if you have some method on the model that calculates some stuff but requires some configuration, let the method require that configuration. This also makes the methods easier to test.
Another solution would be to move the logic out of the model. For example the ASP.NET Identity models are really dumb. They don’t do anything. All the logic is done in the UserStore which is a service and as such can have service dependencies.
The pattern often used in domain driven design (rich domain model to be specific) is to pass the required services into the method you are calling.
For example if you want to calculate the vat, you'd pass the vat service into the CalculateVat method.
In your model
public void CalculateVat(IVatCalculator vatCalc)
{
if(vatCalc == null)
throw new ArgumentNullException(nameof(vatCalc));
decimal vatAmount = vatcalc.Calculate(this.TotalNetPrice, this.Country);
this.VatAmount = new Currency(vatAmount, this.CurrencySymbol);
}
Your service class
// where vatCalculator is an implementation IVatCalculator
order.CalculateVat(vatCalculator);
Finally your service can inject another services, like a repository which will fetch the tax rate for a certain country
public class VatCalculator : IVatCalculator
{
private readonly IVatRepository vatRepository;
public VatCalculator(IVatRepository vatRepository)
{
if(vatRepository == null)
throw new ArgumentNullException(nameof(vatRepository));
this.vatRepository = vatRepository;
}
public decimal Calculate(decimal value, Country country)
{
decimal vatRate = vatRepository.GetVatRateForCountry(country);
return vatAmount = value * vatRate;
}
}
I know my answer is late and may not exactly what you're asking for, but I wanted to share how I do it.
First of all: If you want to have a static class that resolves your dependencies this is a ServiceLocator and it's Antipattern so try not to use it as you can.
In my case I needed it to call MediatR inside of my DomainModel to implement the DomainEvents logic.
Anyway, I had to find a way to call a static class in my DomainModel to get an instance of some registered service from DI.
So I've decided to use the HttpContext to access the IServiceProvider but I needed to access it from a static method without mention it in my domain model.
Let's do it:
1- I've created an interface to wrap the IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- Then I've created a static class to be my ServiceLocator access point
public static class ServiceLocator
{
private static IServiceProviderProxy diProxy;
public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");
public static void Initialize(IServiceProviderProxy proxy)
{
diProxy = proxy;
}
}
3- I've created an implementation for the IServiceProviderProxy which use internally the IHttpContextAccessor
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
private readonly IHttpContextAccessor contextAccessor;
public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public T GetService<T>()
{
return contextAccessor.HttpContext.RequestServices.GetService<T>();
}
public IEnumerable<T> GetServices<T>()
{
return contextAccessor.HttpContext.RequestServices.GetServices<T>();
}
public object GetService(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetService(type);
}
public IEnumerable<object> GetServices(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetServices(type);
}
}
4- I should register the IServiceProviderProxy in the DI like this
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- Final step is to initialize the ServiceLocator with an instance of IServiceProviderProxy at the Application startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
As a result now you can call the ServiceLocator in your DomainModel classes "Or and needed place" and resolve the dependencies that you need.
public class FakeModel
{
public FakeModel(Guid id, string value)
{
Id = id;
Value = value;
}
public Guid Id { get; }
public string Value { get; private set; }
public async Task UpdateAsync(string value)
{
Value = value;
var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
await mediator.Send(new FakeModelUpdated(this));
}
}
The built-in model binders complain that they cannot find a default ctor. Therefore you need a custom one.
You may find a solution to a similar problem here, which inspects the registered services in order to create the model.
It is important to note that the snippets below provide slightly different functionality which, hopefully, satisfies your particular needs. The code below expects models with ctor injections. Of course, these models have the usual properties you might have defined. These properties are filled in exactly as expected, so the bonus is the correct behavior when binding models with ctor injections.
public class DiModelBinder : ComplexTypeModelBinder
{
public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders)
{
}
/// <summary>
/// Creates the model with one (or more) injected service(s).
/// </summary>
/// <param name="bindingContext"></param>
/// <returns></returns>
protected override object CreateModel(ModelBindingContext bindingContext)
{
var services = bindingContext.HttpContext.RequestServices;
var modelType = bindingContext.ModelType;
var ctors = modelType.GetConstructors();
foreach (var ctor in ctors)
{
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
if (parameters.All(p => p != null))
{
var model = ctor.Invoke(parameters);
return model;
}
}
return null;
}
}
This binder will be provided by:
public class DiModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
return new DiModelBinder(propertyBinders);
}
return null;
}
}
Here's how the binder would be registered:
services.AddMvc().AddMvcOptions(options =>
{
// replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider
var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
var binderIndex = options.ModelBinderProviders.IndexOf(provider);
options.ModelBinderProviders.Remove(provider);
options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider());
});
I'm not quite sure if the new binder must be registered exactly at the same index, you can experiment with this.
And, at the end, this is how you can use it:
public class MyModel
{
private readonly IMyRepository repo;
public MyModel(IMyRepository repo)
{
this.repo = repo;
}
... do whatever you want with your repo
public string AProperty { get; set; }
... other properties here
}
Model class is created by the binder which supplies the (already registered) service, and the rest of the model binders provide the property values from their usual sources.
HTH
Is there another way than constructor injection to inject dependencies, and how?
The answer is "no", this cannot be done with "dependency injection". But, "yes" you can use the "service locator pattern" to achieve your end-goal.
You can use the code below to resolve a dependency without the use of constructor injection or the FromServices attribute. Additionally you can new up an instance of the class as you see fit and it will still work -- assuming that you have added the dependency in the Startup.cs.
public class MyRepository : IMyRepository
{
public IMyDependency { get; } =
CallContextServiceLocator.Locator
.ServiceProvider
.GetRequiredService<IMyDependency>();
}
The CallContextServiceLocator.Locator.ServiceProvider is the global service provider, where everything lives. It is not really advised to use this. But if you have no other choice you can. It would be recommended to instead use DI all the way and never manually instantiate an object, i.e.; avoid new.
I'm simply adding some supplemental information here to the answers provided that can help.
IServiceProvider was provided in the accepted answer, but not the important IServiceProvider.CreateScope() method. You can use it to create scopes as necessary that you added through ConfigureServices.
I'm not sure if IServiceProvider is actually a Service Locator pattern behind the scenes or not, but it's how you create scopes as far as I know. At least in the case if it is a Service Locator pattern, it's the official one for today in .NET, and so it's not compounded by the problems of writing your own Service Locator, which I also agree is anti-pattern.
Example, Startup.cs/ConfigureServices and Configure:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SomeDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("Databases").GetSection("SomeDb")["ConnectionString"]);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}, ServiceLifetime.Scoped);
services.AddMvcCore().AddNewtonsoftJson();
services.AddControllersWithViews();
}
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
{
...
IServiceScope scope = provider.CreateScope();
SomeDbContext context = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
SomeModelProxyClass example = new SomeModelProxyClass(context);
await example.BuildDefaults(
Configuration.GetSection("ProfileDefaults").GetSection("Something"),
Configuration.GetSection("ProfileDefaults").GetSection("SomethingSomething"));
scope.Dispose();
}
The above is for doing some default interactions on Startup, maybe if you need to build some default records in your database on a first usage, just as an example.
Ok so let's get to your repository and dependency though, will they work?
Yep!
Here's a test in my own CRUD project, I made a simple minimalist implementation of your IMyDependency and IMyRepository like so, then added them scoped as you did to Startup/ConfigureServices:
public interface IMyRepository
{
string WriteMessage(string input);
}
public interface IMyDependency
{
string GetTimeStamp();
}
public class MyDependency : IMyDependency
{
public MyDependency()
{
}
public string GetTimeStamp()
{
return DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
}
}
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public string WriteMessage(string input)
{
return input + " - " + _myDependency.GetTimeStamp();
}
}
Here ContextCRUD is a Model class from my own project not derived from Scaffold-DbContext tooling like my other database classes, it's a container of logic from those scaffold Model classes, and so I put it in the namespace Models.ProxyModels to hold its own business logic for doing CRUD operations so that the Controllers are not gummed up with logic that should be in the Model:
public ContextCRUD(DbContext context, IServiceProvider provider)
{
Context = context;
Provider = provider;
var scope = provider.CreateScope();
var dep1 = scope.ServiceProvider.GetService<IMyRepository>();
string msg = dep1.WriteMessage("Current Time:");
scope.Dispose();
}
Debugging I get back the expected results in msg, so it all checks out.
The calling code from the Controller for reference, just so you can see how IServiceProvider is passed from upstream by constructor injection in the Controller:
[Route("api/[controller]")]
public class GenericController<T> : Controller where T: DbContext
{
T Context { get; set; }
ContextCRUD CRUD { get; set; }
IConfiguration Configuration { get; set; }
public GenericController(T context, IConfiguration configuration, IServiceProvider provider)
{
Context = context;
CRUD = new ContextCRUD(context, provider);
Configuration = configuration;
}
...
You can do it, check out [InjectionMethod] and container.BuildUp(instance);
Example:
Typical DI constructor (NOT NEEDED IF YOU USE InjectionMethod) public
ClassConstructor(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }
This attribute causes this method to be called to setup DI.
[InjectionMethod] public void Initialize(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }

Categories