I am looking to use a variable Config instantiated in Program.cs to be able to use in other classes I have. As I understand, we need to use dependency injection, but not sure how to achieve.
Program.cs
public class Program
{
static IConfigurationRoot Config = null;
public static void Main(string[] args)
{
Config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
}
}
TestClass.cs
public class TestClass
{
public void DoSomething()
{
// Now I need to use that instantiated Config object here.
}
}
You can either make Config static public, so it can be accessed anywhere in your application, or, if you want to use Dependency Injection, make your TestClass constructor ask for one IConfigurationRoot:
public TestClass(IConfigurationRoot config)
{
// do what you need
// save a reference to it on a local member, for example
}
Now every time you instantiate a new TestClass, you will have to pass by argument to its constructor the IConfigurationRoot object it will use. If that proves to be troublesome (e.g.: you instantiate TestClass a lot, in a lot of different places), then you might use a TestClassFactory:
public class TestClassFactory
{
public TestClass Get()
{
// Do your logic here to get a new TestClass object
// The IConfigurationRoot object that will be used to create TestClasses
// will be chosen here.
}
}
Also, if you don't want to use ASP.NET types directly, you may, as Crowcoder pointed out, make a config repository, as you would do for any database model. That repository would fetch configuration from a json file, for example.
Something like this:
public class ConfigurationRepository : IConfigurationRepository
{
public string GetBasePath()
{
// Read base path from config files
}
public string SetBasePath()
{
// Write base path to config files
}
}
And then you would use DI to pass IConfigurationRepository to your TestClass.
If Config is declared public static, then it can be accessed from other classes as Program.Config
Related
I'm using a service in an assembly with following definition:
internal class BinanceService : IBinanceService
{
private readonly IBinanceClient _binanceClient;
public BinanceService(IBinanceClient binanceClient)
{
_binanceClient = binanceClient;
_binanceClient.SetApiCredentials("u4TX2adUAkpCyRDrStohFNf5otSlw7S7W077X9BQ7KGQbC7C2Y", "uxadyxYyMRPF3caQM0pbzT5naEA9BntqFREJRNIQIBxHNyS");
}
}
the above code lives in an assembly (let's call it A) which has been referenced in assembly B. Now I want to pass different parameters for SetApiCredentials("","") while calling the IBinanceService from assembly B. How can I do that??
thanks
Your class is internal, that means that from inside the assembly, you can create it normally via the constructor. Put the credentials also in the constructor and you can chose them arbitrary:
internal class BinanceService : IBinanceService
{
private readonly IBinanceClient _binanceClient;
public BinanceService(IBinanceClient binanceClient, string credentials)
{
_binanceClient = binanceClient;
_binanceClient.SetApiCredentials(credentials);
}
}
From outside, you won't be able to access the class. Create a factory which returns an instance of IBinanceService (I assume that the interface is public). The caller should not be able to pass its own credentials string. Hence when you call this method, you only the credentials stored in the constant externalCredentials will be used.
public static class BinanceServiceFactory()
{
private const string externalCredentials = "something";
public static IBinanceService Create(IBinanceClient client)
{
return new BinanceService(client, externalCredentials);
}
}
This is a long shot, but if this is about dependency injection this is a pattern I've used:
public class XCredentialsProvider
{
public string ClientId {get;set;}
public string Secret {get;set;
// Constructor here
}
(...)
public BinanceService(IBinanceClient binanceClient, XCredentialsProvider credentialsProvider)
{
_binanceClient = binanceClient;
_binanceClient.SetApiCredentials(credentialsProvider.ClientId, credentialsProvider.Secret);
}
Then in Unity config:
container.RegisterInstance(new XCredentialsProvider(clientId: ..., secret: ...));
Another option is to not to call SetApiCredentials and just accept client that is set up.
Since the class is marked as internal it is not exposed to referencing assembly. You either need to make it public or if that can't be possible, make it a friend assembly.
See this: Friend Assembly in .NET
I have a plugin system here that checks assemblys for implementations of a base class. One base class is supposed to provide an API, but when I hit a Controller, static variables forget their values and always return their default value.
The plugin system creates module instances like this:
var assembly = Assembly.LoadFile(#"absolute\path\to\TestModule.dll");
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(ModuleBase)));
ModuleBase instance = (ModuleBase)Activator.CreateInstance(types.FirstOrDefault());
instance.Execute();
That's working nice so far. The plugin base type is as minimal as it could get:
public abstract class ModuleBase
{
public abstract void Execute();
}
Now I implement a plugin for that in a different project:
public class TestModule : ModuleBase
{
public static int StaticTest;
public override void Execute()
{
StaticTest = 3; // variable is initialized with some non-default value
WebHost.CreateDefaultBuilder()
.UseStartup<Startup>()
.Build()
.RunAsync(cancellationTokenSource.Token);
}
}
with the Startup.cs class looking like this:
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
}
Nothing fancy, really. However, when I now implement a Controller that is accessing my StaticTest variable, it's value is 0:
public class TestController : ControllerBase
{
[Route("Test")]
[HttpGet]
public ActionResult<int> GetStatics()
{
return TestModule.StaticTest;
}
}
If I drop the plugin approach and instead create the WebHost from a console application directly, everything works as expected and the Controller returns 3.
I suspected MVC to launch controllers in a different AppDomain, but that does not seem to be the case, as AppDomain.CurrentDomain.FriendlyName and AppDomain.CurrentDomain.Id do not change and AppDomain.CurrentDomain.DomainUnload is not invoked.
Any ideas are welcome, I am totally clueless now and I feel I missed some essential concept of this whole thing.
You have an ivar named Test, and you're accessing a member named StaticTest, which doesn't seem to be defined. My assumption is that you misnamed the ivar in this question and it's supposed to be StaticTest, not Test.
Based on that assumption, the issue is that you've cast your dynamically created object to ModuleBase, but StaticTest exists on TestModule. Therefore, you cannot access StaticTest unless you cast to TestModule, because that's where it's defined.
EDIT
Sorry, I wasn't paying close enough attention to the controller code. The real issue is similar to the above, but has to do with Execute. You haven't shown where that's called, but I'm assuming you're calling it somewhere or the answer as to why the static has no value would be immensely obvious. However, if your instance is typed as ModuleBase, it's going to call ModuleBase.Execute(), not TestModule.Execute(), even if it's actually a TestModule instance. Only if you're working with something actually typed as TestModule will the correct Execute override be called.
I'm trying to implement Asp Net Core-like static builder for my class. Here's what I'm talking about. In Asp Net Core application in Startup method BuildWebHost class WebHost creates IWebHostBuilder:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
I'm interested in UseStartup<Startup>() method, so I want to know about how does this method 'understands' what Startup class it must use?
The signature for this method is:
public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder)
where TStartup : class;
In my application I want to use the similar approach, for example here I have a simple empty interface ICleaner, which is implemented by some Cleaner classes, and then I have CleanerBuilder class, which is responsible for adding appropriate Cleaner classes to a List, configuring them and runs them all after the Build method. Here's my CleanerBuilder class. Here's my CleanerBuilder class:
public class CleanerBuilder
{
private List<ICleaner> _activeCleaners { get; set; }
private CleanerBuilder() { }
public static CleanerBuilder CreateDefaultBuilder(string[] args)
{
var cleanerBuilder = new CleanerBuilder();
/*
* ...
*/
return cleanerBuilder;
}
public CleanerBuilder UseCleaner<ICleaner>()
{
_activeCleaners.Add(???);
return this;
}
public void Build()
{
foreach(var cleaner in _activeCleaners)
{
cleaner.Run();
}
}
}
And the usage is:
CleanerBuilder.CreateDefaultBuilder(args)
.UseCleaner<TempCleaner>()
.Build();
Right now I don't know what to do in UseCleaner method.
The simplest implementation would probably be this:
public CleanerBuilder UseCleaner<TCleaner>() where TCleaner : ICleaner, class, new
{
_activeCleaners.Add(new TCleaner());
return this;
}
However, there is really nothing you gain here over:
public CleanerBuilder UseCleaner(ICleaner cleaner)
{
_activeCleaners.Add(cleaner);
return this;
}
The generic version is more restricted in this simple version, because it has to have an empty constructor. Not cool, maybe it hasn't. What Microsoft does with it's StartUp is to connect it to it's dependency injection container and try to construct it from there. That solves the problem of constructor parameters.
If you have Visual Studio configured to download sources, just press F12 on the UseStartup method to see what they do in there. Alternatively, you can look it up in the source.
The WebHostBuilder class use reflection to create an instance of the startup class. If you want to do something similar then your List implementation needs to store a Type
private List<Type> _activeCleaners { get; set; }
Then in your Add method you could use:
public CleanerBuilder UseCleaner<TCleaner>() where TCleaner : ICleaner, class, new
{
_activeCleaners.Add(typeof(TCleaner));
return this;
}
But in this scenario you would have to research how to create the ICleaner instance when you require it. I think, in this case, nvoigt's answer might be better for you.
What you're trying to achieve is the builder pattern. The job of a builder is to gather dependencies, requirements and parameters for building a type.
The UseCleaner wouldn't normally, or necessarily do much except just store the type of ICleaner in a list for instantiation later on, likely as a result of the user calling the Build method. The simplest here is to require that ICleaner 's have a parameterless constructor.
private readonly List<Type> _activeCleaners;
public CleanerBuilder UseCleaner<ICleaner>()
where ICleaner : class, new()
// We now require that ICleaners has a parameterless constructor
{
_activeCleaners.Add(typeof(ICleaner));
return this;
}
public Cleaner Build()
{
// Lazily create instances of the cleaners
var cleaners = _activeCleaners.Select(Activator.CreateInstance);
// Pass enumerator for instantiated cleaners to the object
return new Cleaner(cleaners);
}
You can use Activator.CreateInstance with parameters as well, or you could use a dependency injection container if you don't want to require a parameterless constructor.
Below is my sample code. I have this class whose constructor takes an IConfigurationRoot parameter and uses it to set a private field:
public class MyClass
{
private IConfigurationRoot _configuration;
public class MyClass(IConfigurationRoot configuration)
{
// I believe this has been injected
_configuration = configuration;
}
}
Now I want to instantiate that class:
public class AnotherClass
{
private IConfigurationRoot _configuration;
private MyClass myclass;
public SomeMethod()
{
// My _configuration here gets null in the debugger
MyClass myclass = new MyClass(_configuration);
// Rest of method omitted...
}
}
How do I properly instantiated that class so that it doesn't end up being null?
Follow this process:
1.) What I have done is map a section of the AppSettings.json file to an object.
A.) Add the configuration settings to json file.
"MyConfiguration": {
"DBServer": "MyDBServer",
"DBName": "MyDatabase"
}
B.) Create a class to use strongly typed configurations.
public class MyConfiguration
{
public string DBServer { get; set;}
public string DBName { get; set;}
}
C.) Map the JSON configurations to the MyConfiguration object in the Startup class.
public class Startup
{
//Make sure you add this.
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
...
var configSection = Configuration.GetSection("MyConfiguration");
services.Configure<MyConfiguration>(configSection);
//Use one of the following.
Services.AddScoped<MyClass>();
Services.AddTransient<MyClass>();
Services.AddSingleton<MyClass>();
Services.AddInstance<MyClass>();
...
}
}
See this link for an explanation of the differences between
`AddSingleton()`
`AddScopped()`
`AddInstance()`
`AddTransient()`
2.) Change MyClass to have MyConfiguration object as a parameter of the MyClass constructor.
//add this using statement and corresponding package
//"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.1"
using Microsoft.Extensions.Options;
public class MyClass
{
private MyConfiguration _configuration;
public MyClass(IOptions<MyConfiguration> configuration)
{
_configuraiton = configuraiton.Value;
}
}
3.) Add MyClass as a parameter of the AnotherClass constructor. Dependency Injection will create the MyClass or reuse it depending on the scope of the object and how you setup that up in the Startup.ConfifureServices() method.
public class AnotherClass
{
private MyClass _myclass;
public AnoherClass(MyClass myclass)
{
_myclass= myclass;
}
public SomeMethod()
{
//Use myClass here and you don't need to instantiate it.
_myclass
....
}
}
You doing a couple of things.
1 - You are re-declaring your class variable myclass as a local variable in your SomeMethod. You should use the myclass variable you already declared as a class variable. To do that, make this line
MyClass myclass = new MyClass(_configuration);
Like this
myclass = new MyClass(_configuration);
2 - You are not instantiating your IConfigurationRoot, so it is null before inject it at all. You should be able to instantiate it however you want (ConfigurationBuilder like #Sach suggested, or _configuration = ConfigurationBuilder.Build() as #Eldho suggested), but you need to instantiate it before you inject it if you dont want it to be null.
Here, it would be best if you use an IOC container like Autofac or Unity or the built-in container in ASP.NET core(if you are using core). You would need to register your IConfigurationRoot interface and its implemented class with the IOC container. After this, inject your MyClass object using the IOC container(in the constructor, or in the method, as per your best scenario). This way, you would never face a problem.
Short question
Where should I put the abstract factory interface and the actual factory?
Overview
I'm writing a simple video transcoding application and I'm trying to wrap my head around dependency injection.
I have separated my application into several projects in visual studio.
One class library for the transcoder, used by the application engine
One class library for the application engine that will be used by a gui or console interface
One console application that will be the main user interface for now
Without DI
This is what everything looks like before dependency injection
The transcoder lib:
namespace SimpleFFmpeg {
public interface ITranscoder {
void Transcode(String fileName);
}
public class Transcoder:ITranscoder {
// ...
public void Transcode(String fileName) {
// do transcoding stuff
}
// ...
}
}
The PusherEngine lib:
using SimpleFFmpeg;
namespace PusherLib {
public class PusherEngine {
private readonly List<VideoItem> _items;
public PusherEngine() {
_items = new List<VideoItem>();
}
// ...
public void processItems() {
foreach (VideoItem item in _items) {
ITranscoder t = new Transcoder();
t.Transcode(item.FileName);
}
}
// ...
}
}
The actual application:
namespace Pusher {
class Program {
static void Main(string[] args) {
PusherEngine pe = new PusherEngine();
pe.addVideoItem(new VideoItem(...));
pe.processItems();
}
}
}
Refactor to use DI
I create a generic abstract factory interface, like suggested in this question: Creating new instances while still using Dependency Injection
public interface IFactory<T> {
T Get();
}
Next I create a factory that creates ITranscoders
public class TranscoderFactory: IFactory<ITranscoder> {
public ITranscoder Get() {
return new SimpleFFmpeg.Transcoder();
}
}
Then I modify the PusherEngine to require a factory dependence in the constructor:
using SimpleFFmpeg;
namespace PusherLib {
public class PusherEngine {
private readonly IFactory<ITranscoder> _transcoderFactory;
private readonly List<VideoItem> _items;
public PusherEngine(IFactory<ITranscoder> transcoderFactory) {
_items = new List<VideoItem>();
_transcoderFactory = transcoderFactory;
}
// ...
public void processItems() {
foreach (VideoItem item in _items) {
ITranscoder t = _transcoderFactory.Get();
t.Transcode(item.FileName);
}
}
// ...
}
}
Finally, in the Program it looks like this:
namespace Pusher {
class Program {
static void Main(string[] args) {
IFactory<ITranscoder> f = new TranscoderFactory();
PusherEngine pe = new PusherEngine(f);
pe.addVideoItem(new VideoItem(...));
pe.processItems();
}
}
}
Question
In which lib/project should the IFactory interface be defined?
In which lib/project should the TranscoderFactory be defined?
Do they live in the Transcoder lib? In the PusherLib? Or in the actual frontend application?
I'm looking for best practices.
Thanks!
In my opinion, it doesn't matter. For me, the main point of dependency injection is being able to inject something other than the real implementation while testing. I keep my unit tests in a separate project along with the various mock definitions used for testing. The real implementations as well as the 'abstract' logic are all kept in the same assembly/project/namespace.
If you really need a factory (see comment), then this blog post by Mark Seemann addresses this issue.
Briefly, if you use the IoC Container in your factory, you want to use it in the composition root. If not, there is no harm for it to remain in the same assembly as the class it is instantiating.
EDIT
For your particular case, you do not need a factory, since you already have everything you need to solve this dependency.
using SimpleFFmpeg;
namespace PusherLib {
public class PusherEngine {
private readonly ITranscoder _transcoder;
private readonly List<VideoItem> _items;
public PusherEngine(ITranscoder transcoder) {
_items = new List<VideoItem>();
_transcoder = transcoder;
}
// ...
public void processItems() {
foreach (VideoItem item in _items) {
_transcoder.Transcode(item.FileName);
}
}
// ...
}
}
The initialization would then look like this:
namespace Pusher {
class Program {
static void Main(string[] args) {
ITranscoder t = new Transcoder();
PusherEngine pe = new PusherEngine(t);
pe.addVideoItem(new VideoItem(...));
pe.processItems();
}
}
}
The reason why a factory was needed in the answer you linked is that the dependency needed values only known at run-time to be able to be instantiated, while your dependency does not require run-time dependent arguments to be created.
To answer your actual question, and not if this is a good use case for a factory or not:
For this purpose I sometimes split up Interface and Implementation into different projects, and something like your IFactory<> would live in the Common.I project.
This doesn't work in every scenario, but one of the advantages of this approach for me is that I can swap out Implementation dll's with either mocks or a new implementation when there are underlying technology changes.
For example, we recently switched from parsing xml files from a directory to getting data from a service. The only thing I had to update on client machines was this one Implementation dll because the interface did not change at all.
But I guess in the end it doesn't really matter, as mentioned before.