The application I'm trying to get to work uses MVVM. The biggest part of this post is an explanation of what I tried and what I got working. The question is near the bottom of the post. The used Localizer
class is only used here as an example and can easily be replaced with another class.
I have a class library
with a Localizer
class. This purpose of this class is to change the language of the application on the fly, without having to restart the application. The `Localizer has to be instantiated before it can be used but once instantiated, should be usable in the entire application. (The class uses the application resources to localize the application.)
My first approach I could think of is making the Localizer
a public static class
with a public static void Initialize
method. This way I could initialize the Localizer
like this
Localizer.Initialize(/* Needed arguments here */);
on the application level and use it wherever I want in either my class library or application like this
string example = Localizer.GetString(/* A key in the resource dictionary */);
Considering the class library is written by me (only I have the source code) and used by other people who have no clue about the source code (they only know what the class library can do), I would have to explicitly state in some sort of "How to use this class library" that they need to call Localizer.Initialize
on the application level in order to use it everywhere in their application.
After doing some research a lot of people state that this is a bad practice and suggest investigating what Dependency Injection (DI) and Inversion of Control (IoC), so I did. I learned that DI is doing about the same as my first approach but remove the static stuff, use Localizer.Initialize
as the constructor and inject the instantiated class in my other classes.
So the second approach is dependency injection and this is where I'm stuck. I managed to let my application compile with a single MainWindowView
and MainWindowViewModel
with the following code:
protected override void OnStartup(StartupEventArgs e)
{
ILocalizer localizer = new Localizer(Current.Resources, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name, "Languages", "Language", "en");
var mainWindowViewModel = new MainWindowViewModel(localizer);
var mainWindowView = new MainWindowView { DataContext = mainWindowViewModel };
mainWindowView.Show();
base.OnStartup(e);
}
What the above code does is, is inject the localizer
into MainWindowViewModel
. This way no additional code is added to the MainWindowView
code behind and has the view a view model bound to.
In MainWindowViewModel
the constructor is like this (note that the message box is called somewhere else but was moved here to minimize the code):
ILocalizer _localizer;
public MainWindowViewModel( ILocalizer localizer)
{
_localizer = localizer;
MessageBox.Show(_localizer.GetString(/* A key in the resource dictionary */));
}
The above code is still compiling and running fine without exceptions. The problem occurs when I have either UserControls
in my class library
with a view and view model that also require the localizer
instance.
I think I have a solution for when I have a UserControl
in my application assembly but it feels like it is more 'complex' then when I would use a static class
. I usually just bind the view model of a UserControl
to the view in its code behind. This way I can simply add the UserControl
to my .xaml code like this <local:UserControl1 />
without a lot of extra hustle. This way the view model parent view model doesn't have to be concerned about child view models.
With DI I would do something like this in my parent (the child would be the same as in the previous block of code):
View
<n:UserControl1 DataContext="{Binding UC1ViewModel}" />
ViewModel
public UserControl1ViewModel UC1ViewModel { get; set; }
ILocalizer _localizer;
public MainWindowViewModel(ILocalizer localizer)
{
_localizer = localizer;
UC1ViewModel = new UserControl1ViewModel(localizer);
}
The above is still all working fine, no problems so far. The only thing that changed is that the DataContext
is set in the parents view and the content of the DataContext
is set in the view model of the parent.
The question
I also have several UserControls
in my class library
. These can be used by the users of the class library
but they can't alter them. Most of these UserControls
are some fixed pages
that display information about a person, car, etc. The intention is that, for example the label with the name of the person is "Name" in English, "Naam" in Dutch, etc. (which are all declared in the view and are working fine) but there is also text in the code behind that has to be localized and this is where I'm stuck.
Should I approach the problem the same way as I'm doing with a UserControl
in my applications assembly? This feels really counterproductive if let say 20+ of these UserControls
are used in a single parent view.
I also feel that I'm not implementing DI 100% correctly.
You can use dependency injection in a static class using method or property injection. However, you cannot use constructor injection in a static class because the constructor of a static class cannot accept any parameters.
Constructor Injection—Dependencies are statically defined as list of parameters to the class's instance constructor. Property Injection—Dependencies are injected into the consumer via writable instance properties. Method Injection—Dependencies are injected into the consumer as method parameters.
A static web injection, also known as a redirect attack, is a technique used to redirect an infected victims' traffic to a malicious server that hosts a replica of the original website that was requested.
Static classes are sealed and therefore cannot be inherited. They cannot inherit from any class except Object. Static classes cannot contain an instance constructor. However, they can contain a static constructor.
So, today we will see how we can handle these kinds of operations, so we can achieve Dependency Injection with static classes in .NET Core.
Thank you. .NET supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. Dependency injection in .NET is a first-class citizen, along with configuration, logging, and the options pattern.
This article provides general guidelines and best practices for implementing dependency injection in .NET applications. When designing services for dependency injection: Avoid stateful, static classes and members. Avoid creating global state by designing apps to use singleton services instead.
Even if you need lazy loading capabilities or have components that need runtime data to be instantiated, you can still do proper dependency injection without resorting to the evil static or singleton container. Useful or Interesting? If you liked the article, I would really appreciate it if you could share it with your Twitter followers.
Problem
DI is not as simple as you've made it look. There are DI frameworks out there which take care of the DI concern, and they are mature pieces of software.
You can't really do DI yourself without designing a DI container because of the way DI should work
DI solves a few problems, a couple of the main ones are:
IoC - ensuring that components aren't tightly coupled by moving the resolution and provision of dependencies outside of the component classes
Lifetime scope - ensures that components have a well defined lifetime/lifecycle and that they are correctly instantiated and disposed of at key points in your application
How does it look?
You shouldn't even see the container! - you should only see components dependencies and the rest should look like magic...
DI containers should be very transparent. Your components and services should require their dependencies simply by specifying what the dependencies are (in their constructors)
What's my current problem?
You don't want to be having to manually wire up sub-dependencies with code like this:
public MainWindowViewModel(ILocalizer localizer)
{
_localizer = localizer;
UC1ViewModel = new UserControl1ViewModel(localizer); // <-- ouch
}
There are a number of problems with the above:
You are making the MainWindowViewModel
responsible for creating the UC1ViewModel
and managing the lifetime of the object (this isn't always a bad thing as sometimes you want to manage the lifetime of an object in a particular component)
You are coupling the implementation of the MainWindowViewModel
to the constructor implementation of UserControl1ViewModel
- if you require another dependency in UserControl1ViewModel
, suddenly you have to update MainWindowViewModel
to inject that dependency, cue a lot of refactoring. This is because you are instantiating the type yourself instead of letting a container do it.
How do containers prevent code like the above?
With any container you should be registering components
The container will track the list of possible components and services and use this registry to resolve dependencies.
It also tracks the dependencies lifecycle (singleton, instanced etc)
Ok I've registered everything, what next?
Once you have registered all your dependencies, you then resolve your root component from the container. This is known as the composition root and should be the 'entry point' for your application (usually the main view or main method).
The container should take care of wiring up and creating the dependencies for everything that stems from that composition root.
Example:
(Pseudo code)
public class ApplicationBootstrapper
{
private IContainer _container;
public ApplicationBootstrapper() {
_container = new SomeDIContainer();
_container.Register<SomeComponent>().AsSingleton(); // Singleton instance, same instance for every resolve
_container.Register<SomeOtherComponent>().AsTransient(); // New instance per resolve
// ... more registration code for all your components
// most containers have a convention based registration
// system e.g. _container.Register().Classes().BasedOn<ViewModelBase> etc
var appRoot = _container.Resolve<MainWindowViewModel>();
appRoot.ShowWindow();
}
}
Now when your application runs, all dependencies are injected into the root and all dependencies of the root and so on
Your MainWindowViewModel
could then specify a dependency on the UC as such:
public MainWindowViewModel(UC1ViewModel vm)
{
}
Notice how the MainWindowViewModel
no longer needs an ILocalizer
instance, it will be resolved and injected into the UC1ViewModel
for you (unless of course you need it).
Couple of points to note
You should not pass an instance of the container around. If you are referencing the container in your application code anywhere other than during application startup you are probably doing something wrong
Deferred resolution of dependencies is usually achieved with factories (types that are designed specifically to resolve from the container on behalf of your components). The factory should be injected into the component and the component can then call the factory to get the instance it needs. This also allows you to pass arguments to the dependency.
Use SOLID principles, depend on abstractions not concrete classes. This way it's much easier to swap out components if you decide to change the way something works (you just change the registration code to use a different concrete class that implements the same interface, et voila, no refactoring the app)
Anything else
This is by no means a concise view of DI, there is a lot to consider, but hopefully it will get you started. As Steven mentioned, if you are planning on redistributing the library you should read up on best practices.
The original post on dos/dont's is here:
Dependency Inject (DI) "friendly" library
Which DI container should you use?
The world is your oyster. I'm a fan of Castle Windsor - it's not the fastest (I can't think of an app I've written where I've ever needed component resolution to be ninja fast...), but it's certainly fully featured.
Update: couple of non queries I didn't really address
Plugins
Castle Windsor has plugin capabilities built in - so you can drop a DLL into your application directory which adds functionality to your application by registering components with the container. Not sure if this applies to your UC class library or not (you could just make the app depend on it unless it needs to actually be a plugin)
Other stuff
There are also quite a lot of MVVM frameworks with several different approaches on view/viewmodel resolution (viewmodel-first, view-first, hybrid approaches).
You may want to consider using one of these to help guide you in structuring your application if you are not already using one (it doesn't sound like you are).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With