Does .net core dependency injection support Lazy<T> - c#

I am trying to use the generic Lazy class to instantiate a costly class with .net core dependency injection extension. I have registered the IRepo type, but I'm not sure what the registration of the Lazy class would look like or if it is even supported. As a workaround I have used this method http://mark-dot-net.blogspot.com/2009/08/lazy-loading-of-dependencies-in-unity.html
config:
public void ConfigureService(IServiceCollection services)
{
services.AddTransient<IRepo, Repo>();
//register lazy
}
controller:
public class ValuesController : Controller
{
private Lazy<IRepo> _repo;
public ValuesController (Lazy<IRepo> repo)
{
_repo = repo;
}
[HttpGet()]
public IActionResult Get()
{
//Do something cheap
if(something)
return Ok(something);
else
return Ok(repo.Value.Get());
}
}

Here's another approach which supports generic registration of Lazy<T> so that any type can be resolved lazily.
services.AddTransient(typeof(Lazy<>), typeof(Lazier<>));
internal class Lazier<T> : Lazy<T> where T : class
{
public Lazier(IServiceProvider provider)
: base(() => provider.GetRequiredService<T>())
{
}
}

You only need to add a registration for a factory method that creates the Lazy<IRepo> object.
public void ConfigureService(IServiceCollection services)
{
services.AddTransient<IRepo, Repo>();
services.AddTransient<Lazy<IRepo>>(provider => new Lazy<IRepo>(provider.GetService<IRepo>));
}

Services that are to be fetched in Lazy will be re-introduced by the factory registration method with the new Lazy of the intended service type and provided for its implementation using serviceProvider.GetRequiredService.
services.AddTransient<IRepo, Repo>()
.AddTransient(serviceProvider => new Lazy<IRepo>(() => serviceProvider.GetRequiredService<IRepo>()));

To my opinion, the code below should do the work(.net core 3.1)
services.AddTransient<IRepo, Repo>();
services.AddTransient(typeof(Lazy<>), typeof(Lazy<>));

To register services as lazy
services.AddScoped<Lazy<AService>>();
services.AddScoped<Lazy<BService>>();
Or by creating an extension
static class LazyServiceCollection
{
public static void AddLazyScoped<T>(this IServiceCollection services)
{
services.AddScoped<Lazy<T>>();
}
}
...
services.AddLazyScoped<AService>();
services.AddLazyScoped<BService>();
And use it
[ApiController, Route("lazy")]
public class LazyController : ControllerBase
{
private readonly Lazy<AService> _aService;
private readonly Lazy<BService> _bService;
public LazyController(Lazy<AService> aService, Lazy<BService> bService)
{
_aService = aService;
_bService = bService;
}
[HttpGet("a")]
public ActionResult GetA()
{
_aService.Value.DoWork();
return new OkResult();
}
[HttpGet("b")]
public ActionResult GetB()
{
_bService.Value.DoWork();
return new OkResult();
}
}
Result
Init AService
AService Work

I use this form, I hope resolve your problem, this code for Scoped and Transient Life-Cycle you can write for other Life-Cycle look like this.
My Dot-Net-Core vertion is 6
public static class Lazier
{
public static IServiceCollection AddScopedLazier<T>(this IServiceCollection services)
{
return services.AddScoped(provider => new Lazy<T>(provider.GetService<T>));
}
public static IServiceCollection AddTransientLazier<T>(this IServiceCollection services)
{
return services.AddTransient(provider => new Lazy<T>(provider.GetService<T>));
}
}
Usage for Scoped Life-Cycle:
services.AddScoped<ISideDal, EfSideDal>().AddScopedLazier<ISideDal>();
Usage for Transient Life-Cycle:
services.AddTransient<ISideDal, EfSideDal>().AddTransientLazier<ISideDal>();

A bit late to the party here, but to throw another solution on to the pile... I wanted to selectively allow Lazy instantiation of services, so I created this extension method:
public static IServiceCollection AllowLazy(this IServiceCollection services)
{
var lastRegistration = services.Last();
var lazyServiceType = typeof(Lazy<>).MakeGenericType(
lastRegistration.ServiceType);
services.Add(new ServiceDescriptor(
lazyServiceType,
serviceLocator => Activator.CreateInstance(
lazyServiceType,
() => serviceLocator.GetRequiredService(
lastRegistration.ImplementationType ??
lastRegistration.ServiceType
))!,
lastRegistration.Lifetime
));
return services;
}
That way you can just tack .AllowLazy() on to your registration without having to re-specify the types or the scope -
public void ConfigureService(IServiceCollection services)
{
services.AddTransient<IRepo, Repo>().AllowLazy();
}

Related

Dependency injection for Decorator pattern with multiple implementation via .Net Core 2.2, Azure Functions

I need to inject dependency in Startup.cs
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<IAppService, AppService>();
//how to inject for the rest
}
}
to achieve the line below:
new AppService(new CacheRepository(new ConfigRepository()))
instead of below or others
new AppService(new ConfigRepository())
Decorator pattern with multiple implementation below:
public class ConfigRepository : IRepository
{
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
public class CacheRepository : IRepository
{
private readonly IRepository _pository;
public CacheConfigRepository(IRepository repository)
{
_pository = repository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
Environment:
.Net Core 2.2, Azure Functions
Update
Answer:
Thanks #Timo for providing the link below
https://github.com/khellang/Scrutor
How to overwrite a scoped service with a decorated implementation?
For decorating a service with a decorated cached service you can use the Scuter.
Example:
public class MyService : IMyService
{
private readonly IRepository _repository;
public MyService(IRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
The decorated cache service:
public class MyCacheService : IMyService
{
private readonly IMyService _myService;
private readonly ICacheRepository _cacheRepository;
public MyCacheService(IMyService myService, ICacheRepository cacheRepository)
{
_myService = myService;
_cacheRepository = cacheRepository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
var cachedKey = "SomeKey";
(isCached,value) = await _cacheRepository.ReadDataAsync();
if (isCached)
retrun value;
var result = await _myService.ReadDataAsync();
return result;
}
}
Startup.cs:
services.AddSingelton<IMyService, MyService>();
services.Decorate<IMyService, MyCacheService>();
Note that in this example I've added a different interface ICacheRepository.
Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.
By using the extension methods in the linked answer, registering decorators becomes as simple as this:
public void ConfigureServices(IServiceCollection services)
{
// First add the regular implementation
services.AddTransient<IRepository, ConfigRepository>();
// Wouldn't it be nice if we could do this...
services.AddDecorator<IRepository>(
(serviceProvider, wrappedRepository) => new CacheConfigRepository(wrappedRepository));
// ...or even this?
services.AddDecorator<IRepository, CacheConfigRepository>();
}

Create option class for my own class library

I'm working in a class library that does something according to the configuration set by the user in Setup.cs (I still dont konw which method fits better, Configure or ConfigureServices).
Soon, my library will be in nuget, users will can install it and configure it. Question is, how can create that options/config class, instantiate that class in Startup.cs (Configure or ConfigureServices) and pass that options to my class/lib/package?
Here goes my doubt in practice:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMyLib(s => s.Value = 1);
}
Inside my class library/nuget package
public class CalculationHelper
{
public bool GetSomething()
{
if (Options.Value == 1)
return true;
return false;
}
}
In extension method (DI)
public static void AddMyLib(this IServiceCollection app, Action<Options> options = null)
{
// Here in this Extension method, I need save this options that I can retrieve for my class library (CalculationHelper).
}
I have seen much libraries using this method of configuration, like, Swagger, AutoMapper, Serilog, etc.
This is as much as I can specify, I hope you understand.
Assuming
public class YourOptions {
public int Value { get; set; } = SomeDefaultValue;
}
public class YourService : IYourService {
private readonly YourOptions options;
public YourService (YourOptions options) {
this.options = options;
}
public bool GetSomething() {
if (options.Value == 1)
return true;
return false;
}
}
Create your extension method that allows for the option to be configured while adding you services.
public static class MyLibServiceCollectionExtensions {
public static IServiceCollection AddMyLib(this IServiceCollection services,
Action<YourOptions> configure = null) {
//add custom options and allow for it to be configured
if (configure == null) configure = o => { };
services.AddOptions<YourOptions>().Configure(configure);
services.AddScoped(sp => sp.GetRequiredService<IOptions<YourOptions>>().Value);
//...add other custom service for example
services.AddScoped<IYourService, YourService>();
return services;
}
}
Users of your library will then configure as needed
public void ConfigureServices(IServiceCollection services) {
services.AddMyLib(options => options.Value = 1);
//...
}
And when using your service
public SomeClass(IYourService service) {
bool result = service.GetSomething();
}
Yes, the standard practice is to use IOptions<T>. I personally am not a fan of injecting that and tend to use the pattern modeled above. I do still register it for those who would still like to use it.

How can I inject dependencies into a custom ILogger in asp.net core 2.0?

In asp.net core 1.1 I could inject the IServiceProvider into the logger provider and resolve my logger when CreateLogger was called, but it all changed in asp.net core 2.0
My ILogger implementation needs dependencies injected.
How can I achieve this?
ASP.NET core provides possibility to replace built-in DI container with custom one (see this article for details). You could use this possibility to obtain instance of IServiceProvider earlier for logging bootstrapping while still using standard .Net core DI container.
To do this you should change return value of Startup.ConfigureServices(IServiceCollection services) method from void to IServiceProvider. You can use this possibility to build instance of IServiceProvider in ConfigureServices, use it for logging bootstrapping and then return from the method.
Sample code:
public interface ISomeDependency
{
}
public class SomeDependency : ISomeDependency
{
}
public class CustomLogger : ILogger
{
public CustomLogger(ISomeDependency dependency)
{
}
// ...
}
public class CustomLoggerProvider : ILoggerProvider
{
private readonly IServiceProvider serviceProvider;
public CustomLoggerProvider(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public ILogger CreateLogger(string categoryName)
{
return serviceProvider.GetRequiredService<ILogger>();
}
// ...
}
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
return ConfigureLogging(services);
}
private IServiceProvider ConfigureLogging(IServiceCollection services)
{
services.AddTransient<ISomeDependency, SomeDependency>();
services.AddSingleton<ILogger, CustomLogger>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
var loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(new CustomLoggerProvider(serviceProvider));
return serviceProvider;
}
// ...
}
Starting of with that dependency thing you need in various places
public class SomeDependency : ISomeDependency
{
}
An extension file so we can configure logging on the ServiceCollection as per MSDN
Pretty standard stuff you can find on various sources
public static class ApplicationLoggerFactoryExtensions
{
public static ILoggingBuilder CustomLogger(this ILoggingBuilder builder)
{
builder.Services.AddSingleton<ILoggerProvider, CustomLoggerProvider>();
//Be careful here. Singleton may not be OK for multi tenant applications - You can try and use Transient instead.
return builder;
}
}
The logger provider is the part that gets called AFTER services are built when you are working in your business code and need to log stuff.
So in the context of application the DI is built and available here. And it probably makes sense now why ILoggerProvider exists now.
public class CustomLoggerProvider : ILoggerProvider
{
private ISomeDependency someDependency;
public CustomLoggerProvider(ISomeDependency someDependency)
{
this.someDependency = someDependency;
}
public ILogger CreateLogger(string categoryName)
{
return new CustomeLogger(someDependency);
}
}
The concrete custom logger pretty simple stuff
public class CustomLogger : ILogger
{
public CustomLogger(ISomeDependency dependency)
{
}
}
And in the place where you are configuring your ServiceCollection.. as in the OP's question in Startup.cs
private void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeDependency, SomeDependency>();
//services.AddSingleton<ILogger, CustomLogger>(); <== NO
var loggerFactory = new LoggerFactory(); //??? newer DotNet gives you LoggerFactory in startup this may be unnecessary.
//Add some console printer
services.AddLogging(configure => configure.AddConsole())
.Configure<LoggerFilterOptions>(options => options.MinLevel = LogLevel.Trace);
//Add our custom logger
services.AddLogging(configure => configure.CustomLogger()); // <== our extension helping out!
}
So just a note for usage of ILogger
✘ DO NOT - Do not add any ILogger to your services
The whole point of LoggerFactory and LoggerProvider configuration is to simplify using ILogger
public MyBusinessService(ILogger<BusinessServiceClass> log)
{
log.Information("Please tell all registered loggers I am logging!);
}
In my example it will print out message to console if available and the CustomLogger that took a Dependency we injected. If you register more.. it will go to all of them
If you are configuring logging in program.cs you can create a function to configure logging and get an instance of logging provider like this:
private static void ConfigureApplicationLogging(WebHostBuilderContext context, ILoggingBuilder loggingBuilder)
{
loggingBuilder.AddConfiguration(context.Configuration.GetSection("Logging"));
loggingBuilder.AddDebug();
loggingBuilder.AddConsole();
var serviceProvider = loggingBuilder.Services.BuildServiceProvider();
loggingBuilder.AddProvider(new DoxErrorLoggerProvider(serviceProvider, null));
}
Then in BuildWebHost you will configure logging as follows:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(ConfigureApplicationLogging)
.UseNLog()
.UseStartup<Startup>()
.Build();

How can I instantiate Services in MVC

I have an ASP.Net MVC 5 project using an Onion Architecture where I have repositories and services and I use Services from my controller. In my controller, I need to use the IGenericService variables I created, but how can I instantiate these variables? The problem being that my Service needs a IRepository for its constructor, and in turn IRepositoryneeds to be initialized too.
What I tried was AddSingleton(IGenericService<MyClass>, GenericService<MyClass>) in the method ConfigureServices(IServiceCollection services) in the Startup.cs file but it doesn't seem to help.
Edit As suggested my #Nkosi I am trying to resolve dependencies and followed this tutorial to do so : http://scottdorman.github.io/2016/03/17/integrating-asp.net-core-dependency-injection-in-mvc-4/ . My problem now is that I get an invalid operation exception :
Unable to resolve service for type 'Repository.PrincipalServerContext' while attempting to activate 'WebExploitv2.Controllers.NavigationController'
My startup.cs looks like this now:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var services = new ServiceCollection();
ConfigureAuth(app);
ConfigureServices(services);
var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());
DependencyResolver.SetResolver(resolver);
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllerAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
services.AddSingleton<IGenericRepository<Web_Documents>, GenericRepository<Web_Documents>>();
services.AddSingleton<IGenericService<Web_Documents>, GenericService<Web_Documents>>();
services.AddSingleton<IGenericRepository<Web_Categories>, GenericRepository<Web_Categories>>();
services.AddSingleton<IGenericService<Web_Categories>, GenericService<Web_Categories>>();
services.AddSingleton<IGenericService<Web_User_joint_Profils>, GenericService<Web_User_joint_Profils>>();
services.AddSingleton<IGenericRepository<Web_User_joint_Profils>, GenericRepository<Web_User_joint_Profils>>();
services.AddSingleton<IGenericRepository<Web_Group_joint_Profils>, GenericRepository<Web_Group_joint_Profils>>();
services.AddSingleton<IGenericService<Web_Group_joint_Profils>, GenericService<Web_Group_joint_Profils>>();
services.AddSingleton<IMenuService, MenuService>();
services.AddSingleton<IMenuRepository, MenuRepository>();
}
}
I also added a DefaultDependencyResolver class :
public class DefaultDependencyResolver : IDependencyResolver
{
protected IServiceProvider serviceProvider;
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
Next I have the ServiceProviderExtension class:
public static class ServiceProviderExtensions
{
public static IServiceCollection AddControllerAsServices(this IServiceCollection services, IEnumerable<Type> controllerTypes)
{
foreach(var type in controllerTypes)
{
services.AddTransient(type);
}
return services;
}
}
Finally in my controller, I have Interfaces of GenericService which allows me to access Repository and in turn access my DB. I use the followed interfaces for instantiation
private IGenericService<Web_User_joint_Profils> _userProfileService;
private IGenericService<Web_Group_joint_Profils> _groupProfileService;
private IGenericService<Web_Categories> _categoryService;
PrincipalServerContext context;
private NavigationController(PrincipalServerContext context, IGenericService<Web_User_joint_Profils> userProfileService, IGenericService<Web_Group_joint_Profils> groupProfileService, IGenericService<Web_Categories> categoryService)
{
_context = context;
_userProfileService = userProfileService;
_groupProfileService = groupProfileService;
_categoryService = categoryService;
}
Note that My GenericService takes POCOs as generics in order to know where to look in Database. So for each of these in Startup.ConfigureServices(IServiceCollection services) I added an AddSingleton method to register these services and repositories with the DI container.
Any ideas why I get this exception?
I wouldn't call services inside a startup.
Instance your IGenericService as a private readonly, then create the constructor to call in startup.cs or where ever you decide to use it.
private readonly IGenericService _genericService = new GenericService();
public IGenericService GenericService
{
get{ return _genericService; }
set{ _genericService = value; }
}
Now you call your classes like:
GenericService.Method();
It is rather simple, using IServiceCollection instance that is being passed to ConfigureServices method by the run time you do:
services.AddSingleton<IAbstraction, ConcreteImplementation>();
or, for a transient lifetime scope:
services.AddTransient<IAbstraction, ConcreteImplementation>();
or, in your case:
services.AddSingleton<IGenericService<MyClass>, GenericService<MyClass>>();

web api self host custom ioc inject data to controllers

I have a property in my web api self hosted app that I would like to inject to my controllers, which is loaded via reflection using my custom IoC framework, here is my startup code:
public CustomClass StuffInstance { get; set; }
// This method is required by Katana:
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var webApiConfiguration = ConfigureWebApi();
// Use the extension method provided by the WebApi.Owin library:
app.UseWebApi(webApiConfiguration);
}
my controllers are mostly scaffolded and some like:
// PUT: api/EventTypeDescriptions/5
[ResponseType(typeof(void))]
public IHttpActionResult PutStuff(int id, int something)
{
//do stuff
//here i would like to use StuffInstance like a singleton
return StatusCode(HttpStatusCode.NoContent);
}
how can a inject StuffInstance to my controllers? this information would be relevant to anyone making an IoC framework btw
I found the information to inject instances to my controllers in this link:
http://www.asp.net/web-api/overview/advanced/dependency-injection
basically i implemented a dependency resolver for my custom IoC Library
in case someone has the same problem, here is the code, maybe for other IoC frameworks it needs more work
public class CustomIocDependencyResolver : IDependencyResolver
{
private readonly CustomIoc container;
public ComponentLoaderWebApiDependencyResolver(CustomIoc container)
{
this.container = container;
}
IDependencyScope IDependencyResolver.BeginScope()
{
return new CustomIocDependencyResolver(container);
}
Object IDependencyScope.GetService(Type serviceType)
{
return container.GetInstance(serviceType);
}
IEnumerable<Object> IDependencyScope.GetServices(Type serviceType)
{
return container.GetAllInstances(serviceType);
}
public void Dispose()
{
}
}
now my katana Configuration looks like:
// This method is required by Katana:
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var config = ConfigureWebApi();
config.DependencyResolver = CustomIocDependencyResolver(container);
// Use the extension method provided by the WebApi.Owin library:
app.UseWebApi(config);
}
being container the instance of my custom IoC
Since you mentioned AutoFac as a potential candidate, I recommend you follow their tutorial on WebAPI integration. You'll need to define an interface on CustomClass so that you can properly inject it.
You'll need to inject your instance that you've created (since you want to treat it as a singleton) by registering it as an instance component.
public interface ICustomClass {}
public class CustomClass : ICustomClass {}
public CustomClass _stuffInstance = new CustomClass();
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterInstance(_stuffInstance).As<ICustomClass>();
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
Then, in each controller's constructor, inject your instance that's been bound to the appropriate interface.
public class CustomController : ApiController
{
private readonly ICustomClass _customClass;
public CustomController(ICustomClass customClass)
{
_customClass = customClass;
}
}
With ASP.NET Core 6 you can now register a service provider:
builder.Services.AddScoped<ICustomClass, CustomClass>(sp => new CustomClass()/* or your already existing instance */);
builder.Services.AddSingleton<ICustomClass>(sp => new CustomClass()/* or your already existing singleton instance */);
it will be injected to your controllers:
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private readonly ICustomClass _customClass;
private readonly ILogger _logger;
public MyController(ICustomClass customClass, ILogger<MyController> logger)
{
_customClass = customClass;
_logger = logger;
}

Categories

Resources