❔ Dependency Injection
Disclaimer: I come from a low-level language, never did Java and very new in C#.
I started to read the Avalonia UI MVVM tutorial, and it mentioned that usually you'd use DI to avoid coupling the model and the viewmodel.
This got me curious about what exactly is DI. My understanding is that you have a service that uses a dependency. Instead of using the dependency directly, you make the service use an interface for that service, and provide an instance of that interface somehow.
So, the "replace concrete type with interface" I get.
My main confusion is about how to provide the instance. From what I read, it's either
1) passing a concrete type instance to a constructor that expects the interface type.
Or
2) Something involving "frameworks and "automatic registration".
Option 1) is pretty intuitive, not much to be confused about here, but option 2) is complete gibberish to me.
25 Replies
You provide the implementation to the DI container
For example,
That tells the DI framework, that wherever an
IProductService
is requested, it should inject an instance of ProductService
and there are 3 different lifetimes by default:
Transient (new every time)
Singleton (same every time)
Scoped (same within the scope, whatever a scope is)
With all due respect, you're like 10 steps ahead.
The implementation of what?
What's a DI container?
What's a DI framework?
What does "requested" and "injected" mean in this context
a scope is defined by your app. for ASP it defaults to request scope
I have no experience with DI so this is all very obscure to me.
The implementation of the interface
The place that stores all instances that are supposed to be injected
A framework that makes DI possible
Requested/injected can vary, but in case of ASP.NET Core, for example, it's the interface you place in the constructor
tl;dr DI is a magic box where you dump a bunch of objects in, and when you tell it to create new objects it figures out which of those objects to pass into the new objects' constructors
this is less the concept of DI itself and more how DI implementations tend to work in C#
yeah, the concept that people often refer to as DI is actually IoC: Inversion of Control
Dependency Injection is what makes IoC possible
IoC is just saying "I'm going to have one master container that's responsible for maintaining object lifetimes".
I.E. reusing certain types of objects across multiple differerent consumers, and then cleaning up/disposing those objects when they're no longer needed
Ok I see, that kind of explains the large variations in answers I found on the subject.
It's unclear to me why I would want that in the first place. What's wrong with simply passing the object instead of relying on a black box to do it?
In the context of the original question, why would you want that for an MVVM pattern?
because in large applications, you might have hundreds or thousands of things. passing them around yourself gets very messy, very fast - not to mention, how do you keep track of their lifetimes etc in that scenario
letting the DI container handle all that for you based on the rules you told it (transient, scoped, singleton) makes life easier
also, DI containers can be unittested (ie, verify that every registered thing can be resolved and that they dont have conflicting lifetimes)
I don't really see what's inherently "messy" about it and how the DI container improves that
at some point you're going to need some construct that manages instances of objects to pass into other objects
you could do it by hand and for a simple application that's probably fine
but DI containers like the standard Microsoft.DependencyInjection one being mentioned here do it for you
To be clear, I'm not debating whether it's useful/needed or not, I simply don't have a reference to understand what things would look like with and without using DI.
i don't think i have a concise example handy, but think about a scenario where you have to instantiate a class that needs an instance of another class, and that one needs other different classes, and so on
eventually having to write all the boilerplate to set up all those dependencies becomes unmanageable
For the record, my goto in a situation like that is to make builder APIs and config datastructures, or something along those lines
but ok yeah I can see how that's a difficult situation that needs a clever solution
i suppose you could consider a dependency injection system as a kind of generic builder
you configure it ahead of time and tell it what services to include as what implementations etc, then it does all the work of resolving the dependency graphs when you want to instantiate something
then say you want to swap out an implementation for something else, now you change a single line in your DI configuration instead of having to go through a bunch of code that specifically references that type
Ok I see where this is going.
I still have to get some actual practice with it, but I understand much better now. Thanks for taking the time to explain @Jimmacle
As a closing question, let's say I'm working on a Avalonia MVVM project and want to setup DI, what would be the go to DI framework?
i personally try to use Microsoft.DependencyInjection everywhere for consistency, iirc avalonia uses splat internally for some things (maybe just with reactiveui)
I use ReactiveUI so might as well use that too 🤷
thanks!
you'll want to double check it, that may be one that's more like a service locator than DI
as in it's more like a bag of objects than a framework that can construct instances for you
i haven't used it myself but i think that's why i avoided going with it for avalonia applications
oh ok interesting 🤔
One good example I find comes from my own codebase. I had to switch to a different transactional email provider in my ASP.NET Core app.
Instead of having to change every
new FooMainer()
to new BarMailer()
everywhere that used it, I just changed
into
i use it to dynamically pick implementations at startup, for example swapping out a real email sending service to a dummy one that just logs the emails for testing
I'm sure the others have done it justice already, but "because management of object lifetimes is just as much of an issue in MVVM as it is in any other OOP architecture"
the go-to example is almost always database access
picture a button that you click to trigger a form or datagrid to load from a database query
your command execute method (or click event handler or whatever) would look something like this
what this accomplishes is that you create a new short-lived database connection to perform your database I/O, and dispose of it when you're done
because your database services, whatever they might look like, are registered with the IoC container as "scoped", so they are created new for each scope, and shared among everything within the scope that wants to do database I/O
maybe that's an Entity Framework DbContext, maybe it's an ADO SqlConnection, whatever
for simple scenarios, sure, you could just create a DbContext or SqlConnection directly, in lieu of
_serviceScopeFactory.CreateScope()
but it shouldn't be hard to imagine more complex scenarios
maybe DataLoadingService
actually calls out to some other services to do multiple database calls, and then assemblebthe results together
maybe it checks a cache first, which would be another service registered with the IoC container, this time as singleton
maybe the operation involves both database I/O and I/O with some HTTP API.Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.