Accessing AppConfig settings via Injection - c#

I have values in my AppConfig like this...
<appSettings>
<add key="id" value="1234" />
</appSettings>
In the IAppConfig class I have...
public interface IAppConfig
{
string id{ get; }
}
[ConfigurationProperty("id")]
public string id
{
get
{
return (string)this["id"];
}
set
{
this["id"] = value;
}
}
In a startup class I have
using SimpleInjector;
public static Container Container;
public static void Start()
{
Container = new Container();
IAppConfig appConfig = (IAppConfig)ConfigurationManager.GetSection("appSettings");
Container.Register<IAppConfig, AppConfig>(Lifestyle.Singleton);
}
And I inject it into another class...
private readonly IAppConfig config;
public ClassName(IAppConfig config)
{
this.config = config;
}
However the id (and other values) come up as empty strings. This happens in the Getter. I am calling the Start method before trying to access them. What am I doing wrong?

You register type AppConfig as singleton implementation of type IAppConfig, so when you request IAppConfig from container, you'll get an instance created by container, and not 'appConfig' you created in Start.
You need to register instance like:
Container.Instance<IAppConfig>(appConfig);

You have created an appconfig object but have not registered that for injection.
Try the below:
Simplify you AppConfig class
public class AppConfig
{
public string id{ get; set; }
}
var appconfig = new AppConfig{ id = ConfigurationManager.AppSettings["id"] };
container.Register<AppConfig>(() => appconfig, Lifestyle.Singleton);
You can keep the Interface pattern as well

Related

How do I use Dependency Injection in C# .NET 6 application to pass in different instances of the same object with same interface?

I am creating an Azure Function App in Visual Studio with C# and .NET 6.
I have a service I created (CosmosDBService) that implements the interface ICosmosDBService:
public class CosmosDbService : ICosmosDbService
{
private Container? _container = null;
public CosmosDbService(
CosmosClient cosmosDbClient,
string databaseName,
string containerName)
{
_container = cosmosDbClient.GetContainer(databaseName, containerName);
}
I want to pass two different instances of this service into the Function App. Each service instance would represent a different container.
How would I set this up in Startup:FunctionsApp class using the FunctionsHostBuilder?
Default DI container does not support named such scenarios so you have next options - either create separate interfaces and implementations (and register/resolve them) for each databaseName-containerName pair or create a factory and use it to generate desired CosmosDbService instance:
public interface ICosmosDbServiceFactory
{
ICosmosDbService Create(string databaseName, string containerName);
}
class CosmosDbServiceFactory : ICosmosDbServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public CosmosDbServiceFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ICosmosDbService Create(string databaseName, string containerName) => new CosmosDbService(
_serviceProvider.GetRequiredService<CosmosClient>(),
databaseName,
containerName
);
}
Register it with appropriate lifetime and inject it into corresponding class and use it in the constructor to resolve required ICosmosDbService instances.
You can do this, but I wouldn't recommend it. For instance in your start up if you had the following code:
services.AddSingleton<ICosmosDbService, CosmosDbService>();
services.AddSingleton<ICosmosDbService, OtherCosmosDbService>();
both instances would be registered in the Di container. If you had a class that depends on this interface, the following constructor would result in OtherCosmosDbService being injected:
public class SomeClass {
private readonly ICosmosDbService _service;
public SomeClass(ICosmosDbService service){
_service = service; // This would be OtherCosmosDbService
}
}
Both would be registered and in this instance, the last one registered "wins". If you wanted to get both then you could change the constructor to this:
public SomeClass(IEnumerable<ICosmosDbService> services){
// Write logic to handle finding which one you want
}
Honestly, I would go with Guru Stron's suggestion of creating separate interfaces for each container and registering them separately.
I think your design needs to be more granular - trying to access multiple containers (possibly in multiple COSMOS databases) with one interface/class goes against the SOLID principle of single responsibilty. Think about your domain and work from there 'downwards'
public interface ICustomers
{
public IEnumerable<Customer> GetCustomers();
}
public interface IProducts
{
public IEnumerable<Product> GetProducts();
}
public class CustomersInCosmosDatabase : ICustomers
{
private readonly CosmosClient cosmosClient;
public CustomersInCosmosDatabase(CosmosClient cosmosClient)
{
this.cosmosClient = cosmosClient;
}
public IEnumerable<Customer> GetCustomers()
{
var container = cosmosClient.GetContainer("customerDatabaseId", "customerContainerId");
return container.GetItemLinqQueryable<Customer>();
}
}
public class ProductsInCosmosDatabase : IProducts
{
private readonly CosmosClient cosmosClient;
public ProductsInCosmosDatabase(CosmosClient cosmosClient)
{
this.cosmosClient = cosmosClient;
}
public IEnumerable<Product> GetProducts()
{
var container = cosmosClient.GetContainer("productDatabaseId", "prodcutContainerId");
return container.GetItemLinqQueryable<Product>();
}
}
and your registrations become:
serviceCollection.AddAzureClients(clientBuilder =>
{
clientBuilder.AddClient<CosmosClient, CosmosClientOptions>((o) =>
new CosmosClient("connectionString", new DefaultAzureCredential()));
});
serviceCollection.AddTransient<ICustomers, CustomersInCosmosDatabase>();
serviceCollection.AddTransient<IProducts, ProductsInCosmosDatabase>();
You are then in the business of injecting Customer collections and Product collections everywhere NOT CosmosDBServices.

Is there a way to modify an IOptionsSnapshot<T> programmatically instead of a change in the source file?

I have a transient class registered in ASP.NET Core's DI.
An IOptions<T> is injected into its constructor. For every request and when needed, during runtime and based on a condition, I want to have another instance of IOptions injected.
Since IOptionsSnapshot<T> gets updated for every request when the source file gets updated, is there a way to mimic this behavior but instead of a change in the file, I want to programmatically make a change in IOptions, and before constructor injection, during runtime when a request comes in?
And use IOptionsSnapshot<T> instead of IOptions<T> for that.
Update:
Condition example to be run before injection happens somewhere in the app like maybe a controller or action custom attribute? In the attribute check a value and so:
if (some condition)
Options.cnnectionstring = "string1";
else
Options.cnnectionstring = "string2";
Injected into a class like this:
public class Books
{
private readonly string connectionString;
public Books(IOptions<DBOptions> options)
{
this.connectionString = options.Value.connectionString;
}
public void SomeMethod()
{
.... //uses connectionString
}
}
Registered like this:
services.Configure<DBOptions>(options =>
{
options.connectionString = "some connection string";
});
IOption (like IConfiguration) is registered as singleton, but the request is scoped. Then it isn't possible to use request's information to modify the configuration.
You can use a intermediate scoped service, that retrieve the request's information and generate the desired connection string, like :
public class BooksConnectionString
{
public IConfiguration _configuration;
public IHttpContextAccessor _httpContextAccessor;
public BooksConnectionString(IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
public string ConnectionString
{
get
{
var library = _httpContextAccessor.HttpContext.Request.Query["library"].First();
return _configuration.GetConnectionString(library);
}
}
}
Register the service as scoped :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
services.AddScoped<BooksConnectionString>();
}
...
}
Then you can inject like :
public class Books
{
private readonly string connectionString;
public Books(BooksConnectionString options)
{
this.connectionString = BooksConnectionString.ConnectionString;
}
}
It's suppose you can modify the class where the connection string is injected. If you can't modify this class, then you can register IOption as scoped :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
services.AddScoped<IOptions<DBOptions>>(p =>
{
var configuration = p.GetService<IConfiguration>();
var httpContextAccessor = p.GetService<IHttpContextAccessor>();
var library = httpContextAccessor.HttpContext.Request.Query["library"].First();
var dbOptions = configuration.GetSection("Databases").GetSection(library).Get<DBOptions>();
return Options.Create(dbOptions);
});
}
...
}
Warning, IOption are expected as singleton. Register IOption as scoped would break this expectation. To be used as a last resort.

What is best practice to inject IOptions in to my ICollectionService Extension?

I have a library class that accepts an IOptions parameter in its constructor as below:
public class MyClass{
MySetting _setting;
public MyClass(MySetting setting){
_setting = setting;
}
public void DoSomeThings(){
//...
}
}
now I want to create an IServiceCollection Extension to facilitate the setup of my library in user code but I am not certain about the best way to inject MySetting into the Constructor.
I can Rely on users to use this code in their startup.cs:
services.Configure<MySettings>(Configuration.GetSection("MySetting"));
or I can inject MySetting into my Extention method as a parameter like this:
public static IServiceCollection AddMtService<T>(this IServiceCollection services, MySetting setting)
Or I can read the appsetting.json file in my extension method and create the setting inside it.
Which one is the best approach and is there any other option that can be better of these?
You can check in more detail from: link
Basic usage:
Inject IOptions with your config class in your service:
public class TestService : ITestService
{
private readonly MySettings _config;
public TestService(IOptions<MySettings> config)
{
_config = config.Value; // You have the configuration class hire...
}
public void TestFunction()
{
string name = _config.Name;
bool isTrue = _config.IsTrue;
bool isFalse = _config.IsFalse;
}
}
When your config class looks something like this:
public class MySettings
{
public const string ConfigSectionKey = "MySettings";
public string Name { get; set; }
public bool IsTrue { get; set; }
public bool IsFalse { get; set; }
}
Registration of your config class to IOptions:
builder.Services.Configure<MySettings>(builder.Config.GetSection(MySettings.ConfigSectionKey));
And the properties are present in appsettings.json (or where you hold your configuration)
{...
"MySettings": {
"Name": "Tralalaa",
"IsTrue": true,
"IsFalse": false
}
}

.Net Core DI pass runtime parameter into constructor of service and also into 1-n of its' subservices

I have multiple let's say process services. These services have a property which I need to fill before using particular service. However these process services use also 1-n generator sub-services which also have same property as process service.
public interface IProcess
{
IEnumerable<string> metadata;
//...
}
public class Process1 : IProcess
{
public IEnumerable<string> Metadata {get; set;}
private readonly IGenerator1 Generator1;
private readonly IGenerator2 Generator2;
public Process1(
IGenerator1 generator1,
IGenerator2 generator2,
IEnumerable<string> metadata)
{
Generator1 = generator1;
Generator2 = generator2;
Metadata = metadata;
}
}
public interface IGenerator
{
IEnumerable<string> metadata;
//...
}
public class Generator1 : IGenerator
{
public IEnumerable<string> Metadata {get; set;}
private readonly ILogger Logger;
public Generator1(
ILogger logger,
IEnumerable<string> metadata)
{
Logger = logger;
Metadata = metadata;
}
}
I use DI and resolve dependencies in ServiceBuilder
public class ServiceBuilder
{
public ServiceBuilder()
{
var services = new ServiceCollection();
services.AddTransient<IProcess, Process1>();
services.AddSingleton<IProcessFactory, ProcessFactory>();
services.AddTransient<IDeliverySiteGenerator, DeliverySiteGenerator>();
services.AddTransient<INewConnectionErrandGenerator, NewConnectionErrandGenerator>();
//...
ServiceProvider = services.BuildServiceProvider();
}
}
And use ProcessFactory class and its' method GetProcess for retrieving processes I want to use. I add these processes in List<IProcess> and then use this list for executing particular method from all retrieved IProcess services. configData are provided by user input and it also include our wanted property IEnumerable<string> Metadata.
public class ProcessFactory : IProcessFactory
{
private readonly IServiceProvider serviceProvider;
//...
public IProcess GetProcess(SomeConfigData configData)
{
var processExists = registeredProcesses.TryGetValue(configData, out var processType);
if (!processExists)
throw new InvalidArgumentException($"Process not supported.");
return (IProcess)serviceProvider.GetService(processType);
}
}
For now I added Metadata to IProcess service after retrieving it by GetProcess method while Metadata has public setter. But it doesn't solve problem of how to pass it to generator sub-services. Don't want to go through all generator instances of process service and add Metadata through public setter. Is there any way of achieving this?
One way to do that is creating a scoped service to provide access to the metadata:
public interface IMetadataAccessor
{
IEnumerable<string> Metadata { get; set; }
}
public class MetadataProcessor : IMetadataAccessor
{
public IEnumerable<string> Metadata { get; set; }
}
Register the service as scoped:
serviceCollection.AddScoped<IMetadataAccessor, MetadataProcessor>();
Change your process and generator classes to have IMetadataAccessor injected via constructor, and read metadata like this:
public IEnumerable<string> Metadata => _metadataAccessor.Metadata;
Change your factory to instantiate process within a scope:
public Processor CreateProcessor(IEnumerable<string> metadata)
{
// Resolve services within a child scope
using (var scope = _services.CreateScope())
{
// Resolve the accessor service and set metadata
var accessor = scope.ServiceProvider.GetRequiredService<IMetadataAccessor>();
accessor.Metadata = metadata;
// Within the current scope, there is only one IMetadataAccessor.
// So both process and generator will be getting same accessor instance via the constructor.
// If we set metadata to the instance, all services can get the value.
var process = scope.ServiceProvider.GetRequiredService<Process>();
return process;
}
}

Failing to pass Android Context using Autofac

I've decided to give Xarmarin a try, and I'd like to pass the android context.
I did a similar idea like this with Android and Roboguice back in the day. They had a provider factory, which would allow you to pass that item around to inject via properties.
I'd like to attempt this approach with IoC (Autofac ideally). I've followed this example: http://arteksoftware.com/ioc-containers-with-xamarin/
I'd like to add that I'm finding it difficult with services where I'd like to inject instances via the constructor.
Were you all able to do this?
I've decided to give Xarmarin a try, and I'd like to pass the android context.
Directly pass the android context? No, there is no way to do that. The Interface is defined in PCL, but there is no proper container(type) in PCL to accept the context instance.
But you can define your own Interface and class to leverage the Context instance:
Example based on the demo of the blog you posted (leverage the context to read a txt file in Assets folder):
Add a txt file(abc.txt) in Assets folder and set it's BuildAction as AndroidAssets.
Define a new Interface in PCL:
public interface IContextUtility
{
string GetAssetTxt(string str);
}
Add a variable of IContextUtility in MainViewModel.cs:
public class MainViewModel
{
private readonly IPlatform _platform;
private readonly ISettings _settings;
private readonly IContextUtility _contextUtility;
public MainViewModel (IPlatform platform, ISettings settings,IContextUtility contextUtility)
{
_settings = settings;
_platform = platform;
_contextUtility = contextUtility;
}
public string Abc
{
get {
return _contextUtility.GetAssetTxt("abc.txt");
}
}
...
}
Add IContextUtility implementation in Android Project:
public class MyContextUtility : IContextUtility
{
public string GetAssetTxt(string str)
{
string strToReturn = null;
using (var stream = Application.Context.Assets.Open(str))
{
using (StreamReader reader = new StreamReader(stream))
{
strToReturn=reader.ReadToEnd();
}
}
return strToReturn;
}
}
Register a new instance in App.cs:
[Application(Icon="#drawable/icon", Label="#string/app_name")]
public class App : Application
{
public static IContainer Container { get; set; }
public App(IntPtr h, JniHandleOwnership jho) : base(h, jho)
{
}
public override void OnCreate()
{
var builder = new ContainerBuilder();
builder.RegisterInstance(new MyContextUtility()).As<IContextUtility>();
...
}
}
Now, you can use it in your MainActivity:
var text = viewModel.Abc;

Categories

Resources