I'm making a class library intended to be used as a nuget package. I want to make it DI-friendly and not associated with any specific container.
Let's say that the project consists of 15 classes, injected to eachother by constructor injection. How can I make the consumer register my library in an easy way? Should my library expose something like IEnumerable<Type> GetTypesToRegister(), or perhaps evenIList<(Type type, Lifestyle lifestyle)> GetTypesToRegister()? How is this usually approached by library designers?
You can use extension methods to allow users of your package to easily register specific services and their dependencies. The IServiceCollection interface will work across various DI containers. Here's a quick example from a small side project I did a while back. The example is an extension method that allows easy registration of a Azure Storage service.
public static class AzureBlobStorageServiceCollectionExtensions
{
public static IServiceCollection AddAzureBlobFileStorageService(this IServiceCollection services,
Action<AzureBlobStorageServiceConfigOptions> options)
{
var configOptions = new AzureBlobStorageServiceConfigOptions();
options(configOptions);
services.AddScoped<IFileStorageService>(sp =>
{
var logger = sp.GetRequiredService<ILogger<AzureBlobStorageService>>();
var connectionString = configOptions.ConnectionString ??
throw new ArgumentNullException(nameof(configOptions.ConnectionString));
var containerName = configOptions.ContainerName ??
throw new ArgumentNullException(nameof(configOptions.ContainerName));
return new AzureBlobStorageService(logger, connectionString, containerName);
});
return services;
}
}
You can then register the service in an application like this
services.AddAzureBlobFileStorageService(options =>
{
options.ConnectionString = Configuration["Storage:ConnectionString"];
options.ContainerName = Configuration["Storage:ContainerName"];
});
So it provides a simple way to register the service and all it's dependencies and configuration data. You can use the service normally by requesting the IFileStorageService.
Not sure if this is what you were asking about but hopefully it helps. You can look up Service Collection Extension methods in the docs and read more about the specifics.
Related
I am learning DI in .Net Core and I do not get the idea about the benefit of using IOptions.
Why do we need IOptions if we can do without it?
With IOptions
interface IService
{
void Print(string str);
}
class Service : IService
{
readonly ServiceOption options;
public Service(IOptions<ServiceOption> options) => this.options = options.Value;
void Print(string str) => Console.WriteLine($"{str} with color : {options.Color}");
}
class ServiceOption
{
public bool Color { get; set; }
}
class Program
{
static void Main()
{
using (ServiceProvider sp = RegisterServices())
{
//
}
}
static ServiceProvider RegisterServices()
{
IServiceCollection isc = new ServiceCollection();
isc.Configure<ServiceOption>(_ => _.Color = true);
isc.AddTransient<IService, Service>();
return isc.BuildServiceProvider();
}
}
Without IOptions
interface IService
{
void Print(string str);
}
class Service : IService
{
readonly ServiceOption options;
public Service(ServiceOption options) => this.options = options;
public void Print(string str) => Console.WriteLine($"{str} with color : {options.Color}");
}
class ServiceOption
{
public bool Color { get; set; }
}
class Program
{
static void Main()
{
using (ServiceProvider sp = RegisterServices())
{
//
}
}
static ServiceProvider RegisterServices()
{
IServiceCollection isc = new ServiceCollection();
isc.AddSingleton(_ => new ServiceOption { Color = true });
isc.AddTransient<IService, Service>();
return isc.BuildServiceProvider();
}
}
In .Net core, it is recommended that all your configurations should be strongly typed based on their use cases. This will help you to achieve separate of concerns.
Practically, you can achieve the same thing without using IOptions as you stated.
So, if I go back one step and if we have a look at all the available options in .net core configuration:
1. Raw Configuration[path:key]
You can directly access IConfiguration instance and provide path of JSON key in the accessor part, and the configuration value would be returned.
This is not good approach because there is no strong typing here while reading the configuration.
2. IOptions binding to a Config Section
You can use IOptions implementation (which you already know).
This is better because you can have a single class with all related configurations. The IOptions interface provides you additional benefits.
As far as I understood, this IOptions interface decouples your configuration from the actors who are reading the configuration and thereby you can use some additional services from .net core framework.
Please refer MSDN article for details about the benefits.
You can also refer to the twitter conversation at this blog. In that blog, Rick also explains that he could not find any practical case on how this approach is different from the 3rd approach below - as generally the configurations are not dynamic and they are done only once before the application startup.
3. Configuration.Bind() to bind to a Config Section
You can use .Bind call to bind a configuration section to a POCO class. You get strongly typed object. Here if multiple actors are using the configurations, they will not get additional services provided by IOptions interface.
I know this is not exactly pointing out the difference. But I am sure this will bring little more clarity on deciding your preference.
Short answer: yes, you can do without it and access your setting directly from ConfigurationManager.AppSettings, like in this answer.
Slightly longer answer: especially when you want to test your (Console) Application, it might be nice to inject services and settings.
ASP.NET Core comes with DI included and it will be set up in your Startup.cs. DI can be used in Console Applications, but it might be hard(er) to set it up, as the default application has no plumbing for it. I wrote a small blog on how to setup DI with IOptions configuration for .NET Core Console Applications.
By itself IOptions<TOptions> doesn't add anything, in your examples. However, it allows you to use the OptionsBuilder API, should you need any of its features:
Configuring your Options objects using other services;
Validate your Options object;
Add post-configuration to your Options object.
From my experience, all of these use cases are quite exotic, though. For the basic use case, where you want to bind a section of your IConfiguration to an Options object, you can just inject the Options object directly, as per your second example. Not using the IOptions<T> interface has the benefit of being less cumbersome to unit test - you don't need to mock it.
However, if you want your Options values to automatically update at runtime as the configuration sources change, you will need to make use of a wrapper interface. But IOptions<T> itself doesn't do that - you'll need to use either IOptionsSnapshot<T> or IOptionsMonitor<T> for that.
I am trying to build a library that has core and extensions packages like Entity Framework and its database providers.
What I am trying to do is when I register that library with dependency injection, I want to give specific implementation as a parameter.
Think EF. In order to use sql provider on EF we need to register it with SQL provider passed as option parameter like the following.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"]);
});
I would like to build similar structure. Lets say my framework will provide film producer. It will have producer.core package for framework related classes and two extensions package called Producer.Extensions.Hollywood and Producer.Extensions.Bollywood.
If I want to use Hollywood provider, I need to install core package and Hollywood extension package. On registration it should look like
services.AddFilmProducer(options =>
{
options.UseHollywoodProducer();
});
I could not find even a keyword that will point me a direction. I tried to read entity framework's source code but it is too complicated for my case.
Is there anyone who could point me a direction?
Thanks in advance.
I'm not sure if I completely understand your requirements, but DI and extensions are an easy thing in .net core.
Let's say you want this in your Startup.cs
services.AddFilmProducer(options =>
{
options.UseHollywoodProducer();
});
To implements this, create your library and add a static extension class
public static class FilmProducerServiceExtensions
{
public static IServiceCollection AddFilmProducer(this IServiceCollection services, Action<ProducerOptions> options)
{
// Create your delegate
var producerOptions = new ProducerOptions();
options(producerOptions);
// Do additional service initialization
return services;
}
}
where your ProducerOptions implementation might look like
public class ProducerOptions
{
public void UseHollywoodProducer()
{
// Initialize hollywood
}
public void UseBollywoodProducer()
{
// Initialize bollywood
}
}
If you wish to use the passed ProducerOptions in your service, there are two ways to do it. Either use dependency injection again, or directly access the service by using service provider in your extension method
var serviceProvider = services.BuildServiceProvider()
IYourService service = sp.GetService<IYourService>();
And now you have the original Use piece of initialization working.
Hope it helps.
Edit:
To clarify. To inject your options in the service, you can use
services.Configure(ProducerOptions);
in your extension method, and pass to your service constructor via
public YourService(IOptions<ProducerOptions>)
You can then simplify or complicate your options as much as you want.
A useful link for this kind of extensions might be the CORS repository for .net core: https://github.com/aspnet/CORS
Edit after comments:
I think I've got it now. You want packages to extend and implement specific options, kind of like what serilog does with different sinks. Piece of cake.
Scrap the ProducerOptions implementation.
Lets say you have a base package with initial empty structures (BaseProducer library) and an interface
public interface IProducerOptions
{
// base method signatures
}
Your service extension now becomes
public static class FilmProducerServiceExtensions
{
public static IServiceCollection AddFilmProducer(this IServiceCollection services, Action<IProducerOptions> options)
{
// Do additional service initialization
return services;
}
}
Now you create a new package with specific "Hollywood producer" options and you want to extend the base option set
public static class HollyWoodExtensions
{
public static void UseHollywoodProducer(this IProducerOptions options)
{
// Add implementation
}
}
Create as many packages and IProducerOptions extensions as you like, and the added methods will start appearing in your Startup.cs
services.AddFilmProducer(options =>
{
options.UseHollywoodProducer();
});
I'm trying to read my configuration from SF configuration using the 'ConfigurationPackage' that is available from any SF service context. My class looks like this:
internal class ServiceFabricDbConfiguration : IDbConnectionConfig
{
private ConfigurationPackage _configurationPackage;
public ServiceFabricDbConfiguration(ServiceContext context)
{
_configurationPackage = context.CodePackageActivationContext.GetConfigurationPackageObject("Config");
}
public string UserName =>
_configurationPackage.Settings.Sections["Db_Configuration"]
.Parameters[
"Username"]
.Value;
}
I'm using autofac as my DI container, and can register the above class by explicitly capturing a reference to the ServiceContext when i register it with the SF runtime:
ServiceRuntime.RegisterServiceAsync("ApiType",
context =>
{
Bootstrapper.SetContext(context);
return new Api(context);
})
.GetAwaiter()
.GetResult();
Is there a way that i can register the ServiceContext with the bootstrapper, ideally within the bootstrapper class?
I'm currently experimenting with using Autofac.ServiceFabric to register my actors/services, but that hides the ServiceContext so makes the above harder to achieve again (though does make it far easier to maintain clean autofac module definitions)
There's the static method GetActivationContext() in FabricRuntime. You could perhaps use this to inject the activation context.
There's also, in development, Autofac.ServiceFabric https://github.com/autofac/Autofac.ServiceFabric which may be of use to you. There's a blog post about it here https://alexmg.com/introducing-the-autofac-integration-for-service-fabric/ which also contains links to sample code! It's in pre-release (beta) at the moment but I have been using it without issue for the past few months.
PR 8 makes the ServiceContext available in Autofac.ServiceFabric
It seems to me that it's a bad idea to have a domain service require an instance of IOptions<T> to pass it configuration. Now I've got to pull additional (unnecessary?) dependencies into the library. I've seen lots of examples of injecting IOptions all over the web, but I fail to see the added benefit of it.
Why not just inject that actual POCO into the service?
services.AddTransient<IConnectionResolver>(x =>
{
var appSettings = x.GetService<IOptions<AppSettings>>();
return new ConnectionResolver(appSettings.Value);
});
Or even use this mechanism:
AppSettings appSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(appSettings);
services.AddTransient<IConnectionResolver>(x =>
{
return new ConnectionResolver(appSettings.SomeValue);
});
Usage of the settings:
public class MyConnectionResolver
{
// Why this?
public MyConnectionResolver(IOptions<AppSettings> appSettings)
{
...
}
// Why not this?
public MyConnectionResolver(AppSettings appSettings)
{
...
}
// Or this
public MyConnectionResolver(IAppSettings appSettings)
{
...
}
}
Why the additional dependencies? What does IOptions buy me instead of the old school way of injecting stuff?
Technically nothing prevents you from registering your POCO classes with ASP.NET Core's Dependency Injection or create a wrapper class and return the IOption<T>.Value from it.
But you will lose the advanced features of the Options package, namely to get them updated automatically when the source changes as you can see in the source here.
As you can see in that code example, if you register your options via services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); it will read and bind the settings from appsettings.json into the model and additionally track it for changes. When appsettings.json is edited, and will rebind the model with the new values as seen here.
Of course you need to decide for yourself, if you want to leak a bit of infrastructure into your domain or pass on the extra features offered by the Microsoft.Extensions.Options package. It's a pretty small package which is not tied to ASP.NET Core, so it can be used independent of it.
The Microsoft.Extensions.Options package is small enough that it only contains abstractions and the concrete services.Configure overload which for IConfiguration (which is closer tied to how the configuration is obtained, command line, json, environment, azure key vault, etc.) is a separate package.
So all in all, its dependencies on "infrastructure" is pretty limited.
In order to avoid constructors pollution of IOptions<>:
With this two simple lines in startup.cs inside ConfigureServices you can inject the IOptions value like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
And then use with:
public MyService(AppSettings appSettings)
{
...
}
credit
While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.
This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:
public static class ConfigurationHelper
{
public static T GetObjectFromConfigSection<T>(
this IConfigurationRoot configurationRoot,
string configSection) where T : new()
{
var result = new T();
foreach (var propInfo in typeof(T).GetProperties())
{
var propertyType = propInfo.PropertyType;
if (propInfo?.CanWrite ?? false)
{
var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
propInfo.SetValue(result, value, null);
}
}
return result;
}
}
There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:
services.AddSingleton<DataStoreConfiguration>((_) =>
Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
The appSettings.json config looked something like the following:
{
"DataStoreConfiguration": {
"ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
"MeaningOfLifeInt" : "42"
},
"AnotherSection" : {
"Prop1" : "etc."
}
}
The DataStoreConfiguration class was defined in my library project and looked like the following:
namespace MyLibrary.DataAccessors
{
public class DataStoreConfiguration
{
public string ConnectionString { get; set; }
public int MeaningOfLifeInt { get; set; }
}
}
With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:
using System.Data.SqlClient;
namespace MyLibrary.DataAccessors
{
public class DatabaseConnectionFactory : IDatabaseConnectionFactory
{
private readonly DataStoreConfiguration dataStoreConfiguration;
public DatabaseConnectionFactory(
DataStoreConfiguration dataStoreConfiguration)
{
// Here we inject a concrete instance of DataStoreConfiguration
// without the `IOption` wrapper.
this.dataStoreConfiguration = dataStoreConfiguration;
}
public SqlConnection NewConnection()
{
return new SqlConnection(dataStoreConfiguration.ConnectionString);
}
}
}
Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.
I can't stand the IOptions recommendation either. It's a crappy design to force this on developers. IOptions should be clearly documented as optional, oh the irony.
This is what I do for my configuraition values
var mySettings = new MySettings();
Configuration.GetSection("Key").Bind(mySettings);
services.AddTransient(p => new MyService(mySettings));
You retain strong typing and don't need need to use IOptions in your services/libraries.
You can do something like this:
services.AddTransient(
o => ConfigurationBinder.Get<AppSettings>(Configuration.GetSection("AppSettings")
);
Using Net.Core v.2.2, it's worked for me.
Or then, use IOption<T>.Value
It would look something like this
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I would recommend avoiding it wherever possible. I used to really like IOptions back when I was working primarily with core but as soon as you're in a hybrid framework scenario it's enough to drive you spare.
I found a similar issue with ILogger - Code that should work across frameworks won't because I just can't get it to bind properly as the code is too dependent on the DI framework.
How can I inject different implementation of object for a specific class?
For example, in Unity, I can define two implementations of IRepository
container.RegisterType<IRepository, TestSuiteRepositor("TestSuiteRepository");
container.RegisterType<IRepository, BaseRepository>();
and call the needed implementation
public BaselineManager([Dependency("TestSuiteRepository")]IRepository repository)
As #Tseng pointed, there is no built-in solution for named binding. However using factory method may be helpful for your case. Example should be something like below:
Create a repository resolver:
public interface IRepositoryResolver
{
IRepository GetRepositoryByName(string name);
}
public class RepositoryResolver : IRepositoryResolver
{
private readonly IServiceProvider _serviceProvider;
public RepositoryResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IRepository GetRepositoryByName(string name)
{
if(name == "TestSuiteRepository")
return _serviceProvider.GetService<TestSuiteRepositor>();
//... other condition
else
return _serviceProvider.GetService<BaseRepositor>();
}
}
Register needed services in ConfigureServices.cs
services.AddSingleton<IRepositoryResolver, RepositoryResolver>();
services.AddTransient<TestSuiteRepository>();
services.AddTransient<BaseRepository>();
Finally use it in any class:
public class BaselineManager
{
private readonly IRepository _repository;
public BaselineManager(IRepositoryResolver repositoryResolver)
{
_repository = repositoryResolver.GetRepositoryByName("TestSuiteRepository");
}
}
In addition to #adem-caglin answer I'd like to post here some reusable code I've created for name-based registrations.
UPDATE Now it's available as nuget package.
In order to register your services you'll need to add following code to your Startup class:
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();
services.AddByName<IService>()
.Add<ServiceA>("key1")
.Add<ServiceB>("key2")
.Add<ServiceC>("key3")
.Build();
Then you can use it via IServiceByNameFactory interface:
public AccountController(IServiceByNameFactory<IService> factory) {
_service = factory.GetByName("key2");
}
Or you can use factory registration to keep the client code clean (which I prefer)
_container.AddScoped<AccountController>(s => new AccountController(s.GetByName<IService>("key2")));
Full code of the extension is in github.
You can't with the built-in ASP.NET Core IoC container.
This is by design. The built-in container is intentionally kept simple and easily extensible, so you can plug third-party containers in if you need more features.
You have to use a third-party container to do this, like Autofac (see docs).
public BaselineManager([WithKey("TestSuiteRepository")]IRepository repository)
After having read the official documentation for dependency injection, I don't think you can do it in this way.
But the question I have is: do you need these two implementations at the same time? Because if you don't, you can create multiple environments through environment variables and have specific functionality in the Startup class based on the current environment, or even create multiple Startup{EnvironmentName} classes.
When an ASP.NET Core application starts, the Startup class is used to bootstrap the application, load its configuration settings, etc. (learn more about ASP.NET startup). However, if a class exists named Startup{EnvironmentName} (for example StartupDevelopment), and the ASPNETCORE_ENVIRONMENT environment variable matches that name, then that Startup class is used instead. Thus, you could configure Startup for development, but have a separate StartupProduction that would be used when the app is run in production. Or vice versa.
I also wrote an article about injecting dependencies from a JSON file so you don't have to recompile the entire application every time you want to switch between implementations. Basically, you keep a JSON array with services like this:
"services": [
{
"serviceType": "ITest",
"implementationType": "Test",
"lifetime": "Transient"
}
]
Then you can modify the desired implementation in this file and not have to recompile or change environment variables.
Hope this helps!
First up, this is probably still a bad idea. What you're trying to achieve is a separation between how the dependencies are used and how they are defined. But you want to work with the dependency injection framework, instead of against it. Avoiding the poor discover-ability of the service locator anti-pattern. Why not use generics in a similar way to ILogger<T> / IOptions<T>?
public BaselineManager(RepositoryMapping<BaselineManager> repository){
_repository = repository.Repository;
}
public class RepositoryMapping<T>{
private IServiceProvider _provider;
private Type _implementationType;
public RepositoryMapping(IServiceProvider provider, Type implementationType){
_provider = provider;
_implementationType = implementationType;
}
public IRepository Repository => (IRepository)_provider.GetService(_implementationType);
}
public static IServiceCollection MapRepository<T,R>(this IServiceCollection services) where R : IRepository =>
services.AddTransient(p => new RepositoryMapping<T>(p, typeof(R)));
services.AddScoped<BaselineManager>();
services.MapRepository<BaselineManager, BaseRepository>();
Since .net core 3, a validation error should be raised if you have failed to define a mapping.