BrandGhost

POWER UP Your ASP.NET Core Blazor User Interfaces With Plugins!

Want to introduce flexibility, extensibility, and modularity to your ASP.NET Core Blazor application's user interface? We can leverage a plugin system to load raw HTML content and RenderFragments from plugins. Let's see how we can use Autofac in Blazor to accomplish this! Have you subscribed to my weekly newsletter yet? A 5-minute read every weekend, right to your inbox, so you can start your weekend learning off strong: https://www.devleader.ca/newsletter Check out more Dev Leader content (in...
View Transcript
have you been finding it difficult to add new features to your Blazer application like every time you want to go add a new feature to the user interface you're finding that you got some spaghetti code going on and sometimes you're kind of nervous that adding the new feature is going to break some of the old features that are already there and you're kind of feeling like hey it shouldn't because I'm almost copying and pasting some of this code tweaking it a little bit it really should just be adding new functionality something in the back of your mind is making you think hey I'm kind of touching this core code and modifying it that's where the other features are I'm not totally sure if it's safe but you keep kind of repeating this pattern and even though it feels a little bit gross it seems to be working so you continue on but what if I told you there was a simple pattern that you could follow and if you adopt this in your applications it'll actually minimize a lot of the problems that you're experiencing just like this and guess is it what it is that's right it's going to be a plug-in architecture I love building applications with plug-in architectures because they're really extensible and flexible and you can keep adding features without always risking that you're breaking old ones because the code is entirely separate in this video we're going to look at using blade with autoa so that when we add new plugins to our application we can actually dynamically render HTML from the plugins right in the user interface in a previous video I showed how a lot of this is set up so you can go and watch this one first and then come right back here to watch this one and we can look at some enhancements with that said let's jump over to visual studio and check out some Blazer code all right so in a previous video I was showing how we can load plugins into Blazer using autofac and one of the classes I demonstrated was this plug-in provider in order to go make the plugins work today that I want to show you instead of using this iplugin API from the last video I'm actually going to make iplugin API 2 and I admit I'm really bad with naming here but I just wanted to add a new interface and not clobber the other one because I'm going to push this code up to GitHub so you can check it out there now instead of copying and pasting this plug-in provider I actually just went ahead and made a generic one instead and there's nothing really fancy about this I just decided that for demo functionality because this plugin provider is not really doing anything at all that's unique Purp plugin all that I'm going to do is pass in a t plugin API and that way I can actually go replace the other one as well but we just really have this generic one that we can go use the difference is going to be that when we go to register these with autofac we can pass in a different type parameter for the plugin that we're interested in so I mentioned that the plugins we're going to be using are going to be able to have raw HTML generated from the plugin and we're going to be able to drop that right into the UI of our Blazer application so if we go ahead and look at the new plugin that I've created which in my solution Explorer is HTML fragment plugin in a new project as well we can see that this plugin has a method called get fragment content async and my first dummy implementation that I have is just returning a div here and it's styled to be this uh alert and it has some text on it as well that says it's coming from a plugin and you can see also that it's implementing iplugin 2 which I mentioned and iplugin 2 the only thing that it has defined on it is going to be this method right here so nothing overly special about this plug-in it's very similar to the last one that returned a string as well and it was asynchronous of course in this case I'm just returning a task from result so it's actually going to run synchronous but you have the option to go make a sync code here in this particular case I wanted to explicitly call out that we're going to have HTML content that's going to go into a fragment so we'll look at that in just a moment and if you recall from the last video I said that every plug-in in this style that we have is going to use a module so we also have a module for this plug-in inside of the plug-in Library itself it's for registering the HTML fragment plugin by the end of this video I'm actually going to show you some reflection that can go register these for us and we can actually go ahead and delete the modules from the plug-in classes this is just going to demonstrate that we don't actually have to leverage a whole module inside of a plugin if we don't want to to go register it however if your plug-in implementation actually wanted to leverage autofac to do its own dependency injection then of course your plug-in could have its own modules Okay so we've had a brief look at what our plug-in looks like and we see the module that we have to go register with autofac but how is this actually going to work in the application well I made a plugins 2 razor page and it's very much like the other one we had except the for Loop that we had in the other one was actually iterating over the plugins to call a method on it and instead of that in this case I'm going to be looping over a set of fragments that we're going to be creating from the plugins and all that we're going to do is Drop That fragment content rate in here and it's going to show up in our user interface now something that I want to call out is that the code to go actually iterate over the plugins and generate the fragments is down here so this code here is actually going to ask for all of the plugins that come out of the provider it's going to create a list and then populate that list by asking each plug-in get fragment content async and we'll await it and then we're going to pass that into a fragment Builder and use this code right here to go build that fragment leveraging this pattern allows us to have plugin that generate raw HTML content and we can go drop that right inside of a fragment in our user interface now I've already built my project and drop the dll into the bin directory of Blazer plug-in example which is the main application so when I go run this we should go see this new plugin take effect in our user interface all right with our application launched if we go to plugins 2 which will have the fragment stuff that we just looked at when I click this boom we can see that there are one plugins and no I still haven't corrected this plural s here when we only have one from the first video and I probably never will fix that however the plug-in output that we have we can actually see that we have an alert dropped right into here instead of just a list of items that we had generated from the original set of plugins now this is super cool because now you can go ahead and create plugins that have raw HTML and we can go ahead and populate different areas of the user interface just like this with raw HTML from those plugins okay so you're probably looking at this and going that's really cool cuz I can generate raw HTML but what if I just wanted to drop in other user interface components that I've already made in Blazer and you're right we should be able to do that and if you haven't figured it out from quickly looking at the code that we just saw we can actually go ahead and move some code that we have in the core application out to be a responsibility of the plugins if we change the API of the plugins to actually return that fragment instead of the HTML that goes into the fragment we can actually push that responsibility out to the plugin itself s now I wanted to pause here because this is actually an interesting decision that when you're building applications you need to think about the responsibility of the plugin because plugins give us so much flexibility and we can essentially go do whatever we want with plugins we need to be able to create some boundaries and it's not for me to say in your application what's a good and a bad boundary I can't really tell you that but when you're designing applications and you want to use plugins these are things you're going to want to consider in this case what we're about to compare is should plug just generate raw HTML and they can basically give us any HTML they want and then we're going to go ahead and drop that into our user interface wrapped in a fragment or should we be able to communicate with our plugins by skipping raw HTML altogether and just letting them give us back a fragment that will go drop into the user interface we could be even more picky and say that look plugins can't actually go generate any user interface components but we can go Define a dto so a data transfer object and it might have properties and things on it and we'll go use that dto you might call it a model right and we'll use that dto or model and go populate our own view from the core application that we have full control over and that way you don't give any control to the plugin about what actual view is created on screen that plug-in just gives us information to the core application and we as the core application are responsible for creating that view in the latter case that's a lot more locked down from the plug-in perspective right you can only do so many things like maybe you can define a body of text and a title for a simple control or something like that and in The Other Extreme case we're having just raw HTML that you could go drop in and we're kind of trusting that the plugin itself is giving us valid HTML that's going to make sense and that other sort of Middle Ground that's close to the HTML option was just giving us back a fragment that we could drop into the user interface now there's a million different ways that you can do this and like I said I'm not going to tell you which one is right but in this case we're about to go modify the one we just looked at and we're actually going to go add a third type of plugin into our application to support fragments let's go check that out so essentially what we're going to do is go create a new plug-in type so we'll go make an i plugin API 3 cuz I'm really bad at naming things and that plug-in API 3 is actually going to be responsible for giving us back a fragment specifically what we'll have it do is give us back this render fragment class so that API will change from get fragment content async which is a string so instead of a string we'll actually have this render fragment class instead so it's going to be really simple to start we're just going to copy and paste some code and make a couple of tweaks if we go into this I plugin API class you'll notice I've been a little bit lazy and I've just kind of copied and pasted iplugin API 2 now I'm going to go add three right below it and what we want to do instead is make iplugin API 3 actually be render fragment so we'll change this whole method to be a task that returns a render fragment and we'll give it a name get render fragment async we'll give that a save and then we have to think about how we go and register a plug-in provider that can go return all of the iplugin API 3 instances well I didn't show you this in the core application but where I did that was actually in this autofac module and to me this actually makes sense inside of the core application because we want to be able to say from the perspective of the core application which plugins should this thing know about more specifically what types of plugins actually make sense to load into our application so I'm going to go add I plugin API 3 right here you'll notice I left in the original plug-in provider that was just I plugin API and it can be replaced by this generic one I've just left it in for historical purposes but this will actually go allow us to go resolve all plug-in API 3 instances now we haven't gone and made any implementations of iplugin API 3 so this generic plug-in provider currently won't give us any instances back and we'll get to that in just a moment but we have to go think about the third spot in our code to go change to be able to support this new page so you'll notice under pages in my solution Explorer I have plugins one plugins 2 we will need a plugins three for the new page we're about to go add and I'm going to go show you where I had to go make this available in the navigation so if I go to nav menu this part right here is where the plugins one and plugins 2 menu items are so I can go copy and paste this to go we'll make sure that it's enabled for plugins 3 and we'll change the name to plugins 3 as well and there we go we just copy and paste our way to extend our application wait a second something seems a little bit fishy about doing this we're talking about plugins and specifically we're talking about plugins that can go extend our Blazer user interfaces and we just talked about generating raw HTML from plugins and now we're talking about render fragments from plugins and here I am copying and pasting code to go extend the user interface with more view pieces if you haven't caught on already this is the perfect opportunity to be using plugins in an application now I must admit that when I was first putting the original example together and then the second one and now this third one that I'm doing as I create the video I absolutely was not planning to have the user interface itself for demonstration purposes be plug-in enabled because I was so hyperfocused on showing you the plugins that go into the demo but this code that we're touching everything that I've just been navigating through and showing you hey I go update this to go add a new type of plugin I go update that now this is literally the perfect use case for wanting to add plugins to your application but it does require that you have a little bit of foresight so that you know that you can go design parts of it to be plugin enabled so for example the code that we were just looking at actually is just hardcoded view pieces that have navigation items in them it's been really simple for me to just copy and paste these and update a couple of characters to point at a new thing but if I wanted to build an application and keep extending the user interface and not really necessarily have a limit or not necessarily have it as simple as just updating a couple of characters when I copy and past something it might make a lot of sense to go refactor this navigation menu to have a for Loop that goes and pulls navigation components from a plugin and in fact in the next video that I go make in this series that I'll link at the end of this one when it's complete we're actually going to go refactor this application and change this silly navigation menu to go support plugins at the time of recording this I don't have that done yet but it felt really silly to step through that code talk to you about plugins and then realize hm I probably should have done that for this very application so hang tight let's finish this example and like I said when the next video is done I'll link it at the end of this one so that you can go see that right after and because of the timing of all this if you think this is interesting I highly recommend you subscribe to the channel That way when that video is done you'll get notified as soon as it's ready okay back to visual studio we go let's wrap up the copy and paste stuff and see this plugin in action okay so we only have one more silly thing to go copy and paste and that's actually the razor page that we want for plugins 3 so let's go ahead and grab that from plugins 2 we'll copy and paste this whole page we will make plugins 3 and you'll notice at the top of the screen I actually have iplugin API 2 in a generic plug-in provider that we're going to inject so instead of I plugin API 2 now we need API 3 we're going to have plugins let me give this plugins three as the title and we'll give this a little header that says plugins that load render fragments and then we'll just change this next little part to say let's dynamically populate the UI so this is going to be very similar like I mentioned but instead of this code down at the bottom that gets the fragment content a sync we actually don't have that method to use instead we have get render fragment async now this code is going to get a lot more simple than the previous example for I plugin api2 because all of the work that we have to go put that HTML into a render fragment is just going to be pulled into the plugin now right so if you remember what I was explaining before in this case we've decided let's make that work that the plugin is responsible for and our core application that handles these plugins is just going to be responsible possible for getting the render fragments and dropping them into the user interface all right so we're almost there this page for plugins 3 is actually basically done because everything that we had to do up here actually to populate the user interface was already using a fragment we just had to change this part here from lines 44 to 49 that actually got the HTML to instead get the render fragment but the part we're missing is that we don't actually have a plug-in implementation so let's go add the third library for this new type of plugin and like I mentioned instead of using a module inside of that plugin I'm going to show you how we can use autofac to ditch the modules from inside those plug-in libraries all right so I've added render fragment plug-in library and I've just copied and pasted in the project file Json that we have from the second plug-in library that we created you'll notice that it has autofac included here and because I told you we're not going to use modules inside of the plugins anymore I'm going to go ahead and delete this now because I want my code to actually be compatible between all three plugins when it's up on GitHub what I'm going to do is actually enable the plug-in loading specifically for plug-in API 3 and I'm not going to delete the other modules from the other plugins this way when you're watching this and you want to go look through the code you can see how it's done differently across each of these plug-in types so we'll go ahead and save this I also made this render fragment plug-in class it's not complete but it does have the API here and we just need to drop in the code that actually made the render fragment all right fortunately I had on my clipboard the code from the other spot in the project that actually made this render fragment and I have this content placeholder and what we're going to do is just get that other piece of code from plugin API 2 and actually go get some HTML dropped in here all right so I've just pasted in some HTML to get an alert and change the textt to the plug-in API 3 and I know what you're probably thinking and that's well isn't this just loading HTML and putting it into a fragment just like the other one and the answer is yes but what I'm trying to demonstrate is that we move the responsibility into the plugin for actually building the fragment so the API is actually different here it's not just returning raw HTML and that means that you could do whatever the heck you wanted for your user interface as long as it returned a render fragment and the point of showing you this is just that you might have different responsibilities that you want your core application and plugin to have there's not necessarily any rocket surgery going on here nothing super fancy compared to this plugin versus the last one I just wanted to show you moving responsibilities the cool part though is the reflection that I'm about to dive into right now all right so I'm back in the core application in the autofac module that's there and this is where we have the plug-in providers registered I'm still still going to keep this part the exact same but you'll recall that it did not make a module in autofac inside of the third plug-in Library so we need a way that's going to be able to load that into our core application and of course as I went to go type this out and explain it in the video I typed one key and co-pilot basically completed the entire thing for me almost so let me explain it and then we'll fix it up just a little bit so we have this four each Loop that's going to look through and find all the dlls in our base directory I'm just going to tweak this a little bit to actually have plug-in in the name and the whole point of kind of showing you something like this is just that we want to be able to filter on the dlls from somewhere and in your own application that might be a plug-in folder or something else you might want to have different filters that you apply I'm just using a plugin anywhere in the name filter and ending in dll for us we're only looking in this top level directory so really just the dlls in our bin folder the next thing we do is load that assembly from the file and again in your own application it's production ready you probably want to do some error handling around this you either wanted to do something like gracefully catch the error and continue on log it whatever or you might need it to blow up if the application cannot run without that plugin next is where the reflection part actually comes into play and I admit that when I first looked at this code I actually thought that there was a mistake here because this wear method I thought was a link enumerable extension but it's actually built into autofac and it does what we might expect as it reads I just thought that it was a mistake so register assembly Types on the assembly that we just loaded this wear extension actually allows us to do what you might think and filter on the things that we want to include but I thought that this might just be based on the return type so it is part of the Builder pattern which is super awesome I'm glad that they Incorporated this and I actually just learned this as I was making this video because otherwise I would have grabbed the the types from the assembly and registered them individually myself the part that's not totally right in this filter is that we can't just have I plugin API 3 as the type set another way this statement is insufficient just on its own there's two more things we have to check for first we have to make sure that we're not talking about an abstract class and the second part is that we actually have to make sure that this is a class the reason that both of these are important is that because if we have an abstract class we don't want to register that because it's not going to be useful for us in autofac the other part with this is class is that if we had another interface that was defined inside of this assembly that inherited from iplugin API 3 I don't know what you might want to do with that but you're totally allowed to and if you did that we wouldn't want to register that so the three conditions we need are that the type that we're looking at is actually implementing I plugin API 3 it's not an abstract class and it is just a normal class from there in my application I'm just going to say that I want to register them as the interfaces which will be I plugin API 3 and then we're going to make each one of them single instance this code here is the autofac and reflection combination that will allow us to load plugins dynamically by registering them on behalf of the plugin itself and that means that each plugin does not need to have a module it does not even need to know about autofac unless you want it to and we can still register those types and dynamically load them and to prove a point I'm just going to build that new project that we created drop the DL in the bin directory and we're going to check out the new screen that we just added which should have been plug-in based but I did just copy and paste it all right I've gone ahead built that third project and dro the DLS in our bin directory launch the application let's check out plugins 3 which shows up in the menu and only because I copied and pasted it remember that's going to be the next video that we make this all actually follow plugins and let's see what happens boom awesome so if you recall we actually made that text say whoa this is from plug-in API 3 now this part isn't super exciting because it's just a small change from what plugins 2 did right where this is raw HTML that was created from the plugin and then the core application wrapped it in a render fragment plugins 3 actually allows the plugins themselves to give us back a render fragment what's extra cool about plugins 3 is that we used reflection and autofac to go dynamically register that on behalf of the plugin itself there is no module for plugins API 3 and that is how we can go extend a Blazer UI application just by adding plugins and not having to modify the the base code well I cheated a little bit right because when I added new plug-in types I still had to go copy and paste some stuff but stay tune because that follow-up video is going to address that and we'll explain end to end how that works in this case we actually looked at changing the responsibility of the plugin to instead of generating raw HTML it's allowed to generate its own render fragment so we looked at the differences between the second and third type of plug-in by moving the responsibility around like that part of demonstrating this was just so that you have a little bit of flexibility in your Blazer app to see what you want to do there's going to be way more choices that you can make but the other part that was really important is to get you thinking about plug-in apis who's responsible for what and how that makes sense in your own application and the extra little part that I tossed into this video was that we can actually use reflection with autofac to go dynamically register our plug-in types instead of requiring our individual plugins to have modules that register themselves one of the reasons that's cool is it takes the responsibility off of a plug-in Creator to have to know how to use autofac and be responsible for that they don't have to worry they want to use autofac they can but our core application is the one that cares about autofac because that's how it's going to resolve the dependencies so I hope you found that useful thanks so much for watching and when that next video is done you can find it right up here so you can watch that next thanks again and see you next time

Frequently Asked Questions

What is a plug-in architecture and how does it benefit my Blazor application?

A plug-in architecture allows you to add new features to your Blazor application without modifying the core code, minimizing the risk of breaking existing functionality. By keeping the code for new features separate, you can maintain a cleaner codebase and enhance flexibility.

How do I dynamically render HTML from plugins in my Blazor application?

You can dynamically render HTML from plugins by implementing an interface that allows plugins to return raw HTML or render fragments. In the video, I demonstrated how to create a generic plugin provider that can handle different types of plugins, enabling you to drop the generated content directly into your user interface.

What are the responsibilities of plugins versus the core application in a Blazor setup?

The responsibilities can vary based on your design choices. Plugins can either generate raw HTML or return render fragments, while the core application is responsible for integrating these outputs into the UI. It's crucial to define clear boundaries to ensure that plugins don't have too much control over the core application's presentation logic.

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