BrandGhost

Fixing Autofac Circular Dependencies in .NET C#

This video is part of a series where we will explore how to create a facade in C#. We will be using a repository as a basis for our facade and then exploring some of the challenges and benefits. Our facade will be composed of multiple plugin sources where we will use Autofac to load them in dynamically at runtime. When you're done with this video, you should follow up with this one: https://www.youtube.com/watch?v=L8KeFxUWaGM You can also find a related blog post here: https://www.devleade...
View Transcript
managing plug-in dependencies with autofack is something that I've had a pattern for for a few years now and before I get into this video I was curious if you use autofack or some other dependency injection framework how have you been trying to manage your plug-in dependencies so what we're going to be looking at today is trying to set up plug-in dependencies working around some circular dependency issues with a little bit of a tweak and then from there we're going to see how we can go build a better solution for this issue so if that sounds interesting please give this video a thumbs up subscribe to the channel because it really helps me out and with that said let's jump right into the code so The Tweak that I'm going to make is actually that we're going to explicitly call the implementation of the repository facade a repository facade so how that's going to look is that I would say something like I repository facade implements an eye repository and I'll explain how this fixes it why I'm not a huge fan so you can see just it's a it's a marker into interface right some people like violently dislike the idea of a marker interface I don't know um it's a tool seems to work for me so I was using it this way this marker interface still has the same requirement that it needs to implement this method that I have highlighted but now the difference is that when I go here I'm going to ask for the IRA repository facade but there's one more trick if I run this it still doesn't fix it okay so we still get a circular component dependency and here's the other trick that's going to happen and that will be that I need one more type of interface and I'm going to put that in the shared spot this is what I've been doing so I would take a similar interface and I would say this is now the discoverable one a discoverable repository yet another marker interface oops I spelled that totally wrong there we go the solution that I have that has been working for me for a long time across many different types of projects we're talking applications with WPF front ends we're talking about console tools we're talking about uh literally monolithic server architecture and my uh microservice architecture if you can write it in C sharp I've been basically writing applications that use these marker interfaces like this now we change these to be high discoverable so the trick that we're implementing is that once these are marked we are going to tell our facade you're not just looking for any repository it needs to be discoverable the way that I thought through this before was like okay well the reason it works technically is because now there isn't a circular dependency right there's a common interface but my object repository facade depends on these guys this actual class does not implement this so circular dependencies now broken that's great but my thought process was it's discoverable because we're requiring that other you know implementations in these dlls they're going to have registered their plugin with autofac that's what makes it discoverable that was my thought process and I guess if I know it's working that way and I'm building applications it's easy to follow the battery if we run this does it work well I think at this point it works but it's not going to do quite what we want yet so it didn't blow up excellent First Step but of course when we ask for all of the objects nothing here yet we need to get our dlls into the output directory okay I'm going to use a bit of a cheater method here this is something that I do in some of my uh my code because of how the applications are packaged and I know they'll be packaged a certain way I'll explain why it's a cheater method what I'm going to do is I'm going to actually add project references to the example program for both the plugins now it's a cheater method because our example program should not have to know about the existence of these plugins I'm just using it this way so that Visual Studio will build these projects and put the dlls into our bin directory for us if you have questions about how um how that's actually working and how I'm not actually able to see the implementations like I can see the implementations from my now this project right so I could go in memory I can see both of these plugins right you can maybe you can see my intellisense there showing up but I'm never going to use them this way it's a trust me bro moment it's just so that I can have them put into the output directory right so I've done build debug you can see I plug in one and plug into dll that's literally the only reason I've done it this way just to save some time it works on a lot of stuff I do but if you need to have um different build systems and other separation and dropping in plug-ins totally understand well you can't use the same little shortcut I just did but that's what it is but that doesn't solve our registration problem we can do um something fancy so generally what I do instead of just this line here is I actually would do look at the directory I'm running from and then I would scan over the executables and dlls in that output directory it's a little bit Overkill so I'm just going to register this module because we only have one in our core executable but now we have to go get the other ones dynamically and we're able to do that if we look at the container Builder we can do register assembly modules and it takes in a set of assemblies so what I will still do is we're going to we'll do a bit of a dynamic scan on the output directory so we might do something like directory dot get files assembly get executing assembly this will get us a reference to the assembly we're currently in I want to I get the directory where we're running from and then I'm going to ask for all the dlls in there you could do just to kind of illustrate what's going on here you could have like your own filtering if you have a naming convention or you could point to a different you have a plugins folder or something anything you want you can do that what oh my apologies uh I only got the file names um this part I always mess up so it's not assembly screen we want load from or load file we'll find out um it will blow up uh one way if it's incorrect so now we have a array of assemblies and we'll register those so this loads plugins dynamically for us not a lot of code pretty cool but we're not quite done so if I run this it's still not going to work but let's just double check what it's actually trying to do I want to see if it's going to get the assemblies when we run this but I'll explain what's missing in just a moment here so assemblies we have two assemblies in there one for plug one and plug into so far so good but when we do that and register them we didn't actually make the auto fact modules for those plugins so that's a really quick step here I'm just going to copy these modules in here again this only works if you're following a plug-in loading pattern with auto fact if you don't want to propagate Auto fact to your plugin system you would have to tweak this maybe wrap it a different way or maybe you can follow a similar pattern with your other favorite di framework so if we open up plug in one module we want to do in memory repository for plugin one but we need to do it a little bit different we're not just going to register the type because our repositories take in some content holding you my object and plug in one let's say is going to have well it can be explicit let's have them labeled like this and then we're going to copy and paste this put it over here give it a quick little rename we will do one two three and this type is of course for plugin two when we run this what we should see is that these modules get called right so plug in one module as you can see when I step over I have now gone into the loading method for plugin one if you remember my cheater shortcut yes we are referencing plugin one and plugin two from example program but nowhere in my example program do I have any code that explicitly says and refers to plugin one here's plugin two same story there I just wanted to show you that it happens dynamically so all the objects what do we expect is inside here all of the plugins so we have all six um plug-in one plug-in one plug-in one and then the three from plugin two this is just a quick example of the pattern that I've been following now that we have this as a good checkpoint you can see that that's how that's been working for me it uses three interfaces two of them are marker interfaces one for the facade and one for um the plugins themselves and then they share a common base interface couple of reasons why I wanted to move away from this are one the marker interfaces feel a little bit weird and two I will show you that if I needed to have another class and it's also done through Auto fact and it needs to use the repository let's say we will actually ask to print out all of the objects from the Repository so how does that look well we would want to do this because like I said we shouldn't have to know or care that we have a facade that's the point of the facade we should not know and what we might do is like for each we might like we would want to do this but this won't work um exactly how you'd expect so I will prove it so I'm just registering it up here now what I'm going to do is actually say this container resolve and we're going to get the thing that does work and we will ask it to print so what do we think is going to show up in the console it's kind of interesting because we only get three of them and they're only from plug-in two perhaps change the order of this stuff right I'm going to register my facade down here what happens now all three of them sorry all six I can't do math all six of them get printed out so okay the order matters and that's because Auto fact when you resolve a single service of something that's registered multiple times it will take the last one and the facade in this case is now the last one but that's kind of sketchy because we really have to care about the plug-in load order I don't like that at all so how I work around that is I end up having to put this and I call the marker interface and I hate it I hate it because I need to know that some some repository I want to access some service I want to access has a facade and to me that defeats the whole point of the facade this is the thing that I'm trying to solve and I have a nice solution for it now finally and we will look at that next in an upcoming video of course so just for a quick recap for what we looked at today was that we were able to break our circular dependencies with respect to our facade and the different repositories that we were passing into it by using marker interfaces so the marker interfaces broke those circular dependencies and then as a result we were able to resolve them properly with autofac so if you use Auto fact or some other dependency injection framework if you have a different solution for overcoming this I'd love to hear about it in the comments so if you thought that was interesting please give the video a thumbs up subscribe to the channel and make sure you watch the next video where I show you my solution for how I've overcome this problem more recently

Frequently Asked Questions

What are marker interfaces and how do they help in breaking circular dependencies?

Marker interfaces are interfaces that do not contain any methods but serve as a way to tag or categorize classes. In my approach, I use marker interfaces to break circular dependencies by creating a common interface that my repository facade can depend on, allowing me to avoid direct circular references.

Why do you consider your method of adding project references a 'cheater method'?

I call it a 'cheater method' because ideally, the main application shouldn't need to know about the plugins. I'm using project references just to ensure that Visual Studio builds the projects and places the DLLs into the output directory, which is a workaround rather than a clean solution.

What is the significance of the order in which services are registered in Autofac?

The order of service registration in Autofac matters because when resolving a service that has multiple registrations, Autofac will return the last registered instance. This can lead to unexpected behavior if you're not careful about the order, which is something I try to manage to avoid confusion.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload