Failing to pass Android Context using Autofac - c#

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;

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.

I am trying to inject a service into a constructor using ASP.Net Dependency Injection but getting a message that my blank constructor is not enough

Here is what I have so far. I am trying to create a new ThemeManagementViewModel and inject into that a resource service using:
Microsoft.Extensions.DependencyInjection version 5.0.1 nuget package
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static IServiceProvider Init()
{
var serviceProvider = new ServiceCollection().ConfigureServices()
.BuildServiceProvider();
ServiceProvider = serviceProvider;
return serviceProvider;
}
}
public static class DependencyInjectionContainer
{
public static IServiceCollection ConfigureServices(this IServiceCollection services)
{
services.AddSingleton<IDatabaseService, DatabaseService>();
services.AddSingleton<IResourceService, ResourceService>();
services.AddTransient<ThemeManagementViewModel>();
return services;
}
}
public partial class ThemeManagementViewModel : BaseViewModel
{
private readonly IResourceService _resourceService;
public ThemeManagementViewModel(IResourceService resourceService)
{
_resourceService = resourceService;
}
}
public partial class ResourceService : IResourceService
{
private IDatabaseService _databaseService;
public ResourceService(IDatabaseService databaseService)
{
_databaseService = databaseService;
}
}
public interface IResourceService
{
void SetResourceColors();
}
public class ThemeManagementPage : HeadingView
{
private readonly ThemeManagementViewModel _vm;
public ThemeManagementPage()
{
BindingContext = _vm = new ThemeManagementViewModel();
}
}
When I build my application it gives me a message for this line:
BindingContext = _vm = new ThemeManagementViewModel();
and this is the message that I am getting.
There is no argument given that corresponds to the required
formal parameter 'resourceService' of
'ThemeManagementViewModel.ThemeManagementViewModel(IResourceService)'
I thought that the DI was supposed to insert the service into the constructor of ThemeManagementViewModel but it seems not to be working.
Dependency injection will not simply take place anywhere where you construct an object. You need to go explicitly through your DI framework.
In this case, you need to call GetRequiredService() of your IServiceProvider object.
var _vm = Startup.ServiceProvider.GetRequiredService<ThemeManagementViewModel>();
Also, from your code, we don't see that you use your DependencyInjectionContainer class at all. You must make sure that your ConfigureServices method is called explicitly.
DI cannot do magic. The compiler doesn't know anything about it. You have to use it explicitly. It looks like it could do magic in the context of ASP.net website projects. But that is only because there it is the ASP.net framework that handles the things that you need to do explicitly in other types of projects.
Tutorial on how to use DI in .net applications

.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;
}
}

ASP.NET Core DI in a class library?

I have a ASP.NET Core 2.1 project that references a "Data Access Layer" project of typ .NET Core Class Library.
The Data Access Layger needs connection string from the appsettings.json in the ASP.NET Core project.
I have created a simple container like this :
public class DatabaseConnectionString : IDatabaseConnectionString
{
private readonly string _connectionString;
public DatabaseConnectionString(string connectionString)
{
_connectionString = connectionString;
}
public string ConnectionString {
get { return _connectionString; }
set { }
}
}
In the ASP.NET Core Startup.cs > ConfigureService I have this :
services.AddScoped<IDatabaseConnectionString>(p => new DatabaseConnectionString(Configuration.GetConnectionString("DefaultConnection")));
I know that I can add the IDatabaseConnectionString to a constructor of a controller in ASP.NET to get the container. But How do I get it while in the class library? I dont want to pass it all the way down from the controller and just adding the IDatabaseConnectionString to the constructor of a class in the class library do not work.
I probably need a service where I can ask to create a object of a class and let the service fill in the constructor interfaces with the correct objects?
For example filling in the IDatabasConnectionString in this class :
public class UserFactory : FactoryBase
{
private readonly IDatabaseConnectionString _iDatabaseConnectionString;
public UserFactory(IDatabaseConnectionString connectionString)
{
_iDatabaseConnectionString = connectionString;
}
}
I know that I can add the IDatabaseConnectionString to a constructor of a controller in ASP.NET to get the container.
No, that's not needed and it would be wrong.
just adding the IDatabaseConnectionString to the constructor of a class in the class library do not work.
It doesn't work because you need to create the service that will use the connection string and add it to the services container.
For example:
public class Repository: IRepository
{
public Repository(IDatabaseConnectionString databaseConnectionString)
{
_databaseConnectionString = databaseConnectionString;
}
}
public class ServiceThatRequiresDatabase : IServiceThatRequiresDatabase
{
public ServiceThatRequiresDatabase(IRepository repository)
{
_repository = repository;
}
}
// ...
services.AddScoped<IRepository, Repository>();
services.AddScoped<IServiceThatRequiresDatabase, ServiceThatRequiresDatabase>();
public class HomeController : Controller
{
public HomeController(IServiceThatRequiresDatabase service)
{
_service = service;
}
}
By the way, as #YeldarKurmangaliyev said, your DatabaseConnectionString should be like this if you want to make it read-only:
public class DatabaseConnectionString : IDatabaseConnectionString
{
public string ConnectionString { get; }
public DatabaseConnectionString(string connectionString)
{
ConnectionString = connectionString;
}
}
There is no difference between controller and class from a class library. You need to
Define a class in a class library and inject IDatabaseConnectionString into it. Your UserFactory is the right way.
register the UserFactory for DI
serviceCollection.AddScoped<IUserFactory, UserFactory>();
Resolve the UserFactory by the DI. For example, use the UserFactory as the constructor parameter in some controller. Everything is connected by DI automatically.
public MyController(IUserFactory userFactory)
{
_userFactory = myUserFactory;
}
Here is the good explanation for understanding Composition root.

How do I handle a configuration class that are loaded at runtime with dependency injection?

I'm currently trying to work with dependency injection and so far I love. But it's one thing I can't really get my head around and where my current solution just seems wrong.
I'm working with WPF, MVVM and many of the classes I inject need an instance of a project configuration class that isn't initialized until the user create or open a new project in the application.
So my current solution is to have a "ConfigurationHandler" with load/save method and a property that hold an instance of the configuration class after it's loaded. I inject ConfigurationHandler to the others classes and then they can access the configuration after it's loaded. But it seems weird to let classes that never should save/load configuration handle the whole "ConfigurationHandler" and 100% they would just use it to access the configuration instance likt this:
var configuration = configurationHandler.Configuration;
Another problem is that if they try to access the configuration before it's loaded they will get exception (should not really happen as you can't do anything before a project is created/loaded, but still).
But the only other solution I can think of is to use "intialize" methods after a project is created/open but that seems just as bad.
So how do you usually handle cases like this?
Edit: Should add that this configuration class handle information like project path, project name, etc so have nothing to do with the dependency injection itself.
If your configuration is static (read: It's only read during startup of your application, such as from project.json or Web.Config), you can also set it during app startup/the composition root.
The new ASP.NET 5 uses it heavily and it works very well. Basically you will have an IConfiguration<T> interface and a POCO class, which you set up during the app startup and can resolve/inject it into your services.
public interface IConfiguration<T> where T : class
{
T Configuration { get; }
}
And it's default implementation
public interface DefaultConfiguration<T> where T : class
{
private readonly T configuration;
public T Configuration {
return configuration;
}
public DefaultConfiguration<T>(T config)
{
this.configuration = this.configuration;
}
}
And your POCO class
public class AppConfiguration
{
public string OneOption { get; set; }
public string OtherOption { get; set; }
}
In your composition root, you would then register it, like
// read Web.Config
Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
container.AddSingleton<IConfiguration<AppConfiguration>>(new DefaultConfiguration<AppConfiguration>(
new AppConfiguration
{
OneOption = rootWebConfig.AppSettings.Settings["oneSetting"],
OtherOption = rootWebConfig.AppSettings.Settings["otherSetting"],
})
);
And finally, all you have to declare in your services is
public class MyService : IMyService
{
public MyService(IUserRepository, IConfiguration<AppConfiguration> appConfig)
{
...
if(appConfig.OneOption=="someValue") {
// do something
};
}
}
Finally you can make this a bit easier to configure, if you write an extension method like
public static class MyContainerExtension
{
public static void Configure<T>(this IMyContainer container, Action<T> config) where T : class, new()
{
var t = new T();
config(t);
container.AddSingelton<IConfiguration<T>>(t);
}
}
Then all you need to do is
container.Configure<AppConfiguration>(
config =>
{
config.OneOption = rootWebConfig.AppSettings.Settings["oneSetting"],
config.OtherOption = rootWebConfig.AppSettings.Settings["otherSetting"],
})
);
to set it up
Instead of Constructor Injection, consider using an Ambient Context approach.
The last type of DI we’ll discuss is making dependencies available
through a static accessor. It is also called injection through the
ambient context. It is used when implementing cross-cutting concerns.
This is a good option if the classes that need access to your configuration are of different types in different layers or libraries - i.e. is a true cross-cutting concern.
(Quote source)
Example, based on the classic Time Provider one from [Dependency Injection in .NET][2]
abstract class CustomConfiguration
{
//current dependency stored in static field
private static CustomConfiguration current;
//static property which gives access to dependency
public static CustomConfiguration Current
{
get
{
if (current == null)
{
//Ambient Context can't return null, so we assign a Local Default
current = new DefaultCustomConfiguration();
}
return current;
}
set
{
//allows to set different implementation of abstraction than Local Default
current = (value == null) ? new DefaultCustomConfiguration() : value;
}
}
//service which should be override by subclass
public virtual string SomeSetting { get; }
}
//Local Default
class DefaultCustomConfiguration : CustomConfiguration
{
public override string SomeSetting
{
get { return "setting"; }
}
}
Usage
CustomConfiguration.Current.SomeSetting;
There are other DI Patterns that could be used, but require changes to the class that need it. If Configuration is a cross cutting concern Ambient Context could be the best fit.
Constructor Injection Example
public SomeClass(IConfiguration config)
{
}
Property Injection
public SomeClass()
{
IConfiguration configuration { get; set; }
}
Method Injection
public SomeClass()
{
public void DoSomethingNeedingConfiguation(IConfiguration config)
{
}
}
There is also Service Locator, but Service Locator is (IMO) an anti-pattern.

Categories

Resources