BrandGhost

Fixed The WRONG Bug - Let's Debug My ASP.NET Core App

In the previous video, I landed a bug fix in production after testing it locally. But... It turns out I had only fixed the symptom! In this video, I walk you through a different debugging and testing approach for my ASP.NET core application. Was I successful in the end? Find out! 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://subscribe.devleader.ca Check out all of my courses: https...
View Transcript
I finally fixed the issue I was working on so how many times could I possibly break production right in this video I'm going to walk through debugging a problem in my asp.net core application and part of this is going to be elaborating on different architectural decisions I've made that allow me some flexibility when it comes to being able to debug now the scenario we're going to walk through today is unfortunately something that can happen to anyone in my previous video which if you haven't watched yet you can check it out right here and then come right back I was talking about a scenario where I fixed a bug that was happening in production wrote some tests and committed the code with my test passing when I tried to run the feature in production it just wasn't working at all instead of fixing the root problem that I had before I had fixed one of the symptoms so I'm going to walk you through how I built a debugging tool patched up my issues and then got it all pushed back up to production and at the end of this video you can see if I was successful or not before I head over to visual studio just a quick reminder to check out that pin comment to subscribe to my free Weekly Newsletter all right let's dive in the bug that I was solving for in the last video was revolving around a null check and without this this null check unfortunately I was having a null reference exception happen a little bit later on now as I mentioned in the intro to this video this was just the symptom of another problems having this null check is a bit of a defense in depth right now but the reality is that this isn't the underlying problem I was having when it came to resetting the password for a user what was happening was that the email was not being found for that user and as a result I was getting a null back here but the underlying issue seemed to be that the user was not being matched in the datab when I checked the database myself I could absolutely see that the email was there so something suspicious was happening with looking up the user so in this current situation this code never should have had to get hit in the first place in fact there's a problem earlier than this code path while I'm a firm believer that I can go write tests that will cover this code and be able to exercise it with a unit test one of the challenges is that it's not set up to be a functional test I'm not going to be writing tests that go connect out to my true database and truly access the information that I have in production and because I'm getting something going on that's a little bit peculiar with looking up the email I want to make sure that I can interact with my production environment and this is going to lead me to discussing why I've set up some of my architecture the way I have and how I have a set of debug tools that can call into a lot of my code so I can interact with production I've gone ahead and jumped over to one of these tools that I've created and this is the new one that I created for this particular problem I wanted something that I could run on the command line that would allow me to do password res sets for users this is kind of interesting because there might be situations where I do want to go run a tool that can allow me to do that now this might be beneficial for doing something in the future that's manual and I don't have a UI for it the reality is right now what this is going to afford me to do is exercise some code that connects out to my production environment because this connects out to the production environment what I'm going to be able to do is hook up to a lot of my code that I've already written use the email that I'm trying to find in production and see why it's not matching in the first place after after being bitten from the last fix I was really hoping that my unit tests were going to catch the problem but now I'm needing to rely a little bit more on something functional so I can get rid of any assumptions so to quickly explain how my tooling framework works this is just another console application that I've created it's entirely separate from the asp.net core web application as you might see at the top here I have this Auto register command line tool attribute and this means that I have a plug-in system for my command line tool and I can create new tools as long as they implement this interface and this will automatically register them for me through dependency injection and each one of these tools has a method that we can call into and once we're inside the tool I can write things that say something like this where I'm asking the debug console to get some user input and parse that and then we can make decisions about what to do with that but the other part that's critical about these tools is that I'm able to call into different parts of my monolith and this is a really good spot to pause because that word probably triggered a couple of people the system that I have is what I would call a modular monolith and if you're talking to other developers or other people creating content I don't know the exact definition of modular monolith that everyone else is using but my system is a monolith I've separated out the code into different modules but they're all inside of this one big solution they all get deployed together it's not microservices in the sense that I'm not deploying them separately and scaling them out horizontally there's absolutely no value to that for me right now but this system is a monolith like I said it has a plug-in architecture and vertical slices what this allows me to do is develop each one of these different features sort of like its own little micro service because they're plug-in based but I hook them all up into one application and I deploy that whole application together but something that's important that I'm trying to do in my design is making sure that between these different vertical slices or the different plugins that I have when they need to interface with each other how does that work a lot of people have asked me about this before and they say isn't it Overkill like you're going to have to introduce some type of messaging Library between these like that seems like it's chaotic you're kind of getting the worst of Both Worlds by doing this and I got to pause them for a second and say whoa whoa whoa you're right if I was doing that but I'm not I don't need a messaging Library between these things that are interacting in the same process that aren't being separated out it's totally Overkill right now they're just doing Simple method calls and that's it but then you might say well how could you ever possibly decouple these things and my answer to you is that I just put a little Thin Client layer in between so anytime one of these verticals wants to talk to another they have to talk through a client API and that means that behind the scenes behind that client API if I need to change the implementation about how to send messages back and forth it's behind the API it's okay right now I don't need to worry about that but I did start following this pattern early because I had some other processes that that I wanted to run on the side and leverage some of the same code so some of these client apis did in fact have an HTTP implementation like a rest API behind the scenes and I still use the same interface like it was an i whatever interface for some service that I had inside of my monolith and where I'm going with this is that my tool in particular is going to be accessing these interfaces it's going to be able to talk to different you know microservices if you will or the different verticals the plugins I have inside of my monolith through this interface And to clarify it doesn't have to go talk to my production server that's running in the cloud it has the code with it already it's going to be talking to the database that's in the cloud but it doesn't have to connect out to my real server so one of the reasons that I've structured the code the way that I have is because I want to have that reusability yes I would say it's been Overkill in many of the situations but I like following some of these patterns sometimes to an extreme just to see where their limits are and so far it hasn't been that hectic for me and there's been situations just like this one I'm going to show you where it's paid off really well all right so to make a little password resetting tool all that I'm doing is asking for the email that we want to look up for the client from there I'm going to generate a password automatically and when we do that I'm going to set the password using this API and you can see that I'm calling this authentication client to tr set password async method as I was just explaining this is the API this client API for my authentication vertical slice the same thing right here when I'm generating the password I'm asking that authentication client to be able to do that for me but this tool is super simple all that we need to do is get the email from the user that's calling this get the password generated and then pass those two parameters into here this true value is just whether or not this is a permanent password set or we're triggering some type of temporary password reset now this code path is almost exactly what I would have inside of the API route the difference is that instead of parsing input from an HTTP request I'm getting that input right from the console but otherwise this code that you see right here is very similar the one difference is that at the route level what I would be able to do is not have to go through this client API directly but instead I can go through the backend code that this client code is already calling so this is one level of indirection but it's super straightforward if I jump into the implementation of it you can see that it's a direct pass through all that I'm doing is calling this server implementation and it's going to do it with all the same parameters but like I mentioned I do have some cases where I have implementations of these types of things that are using web requests all right so now that I had my tool all put together with just this little bit of code what I was able to do is run this step through it and see why the filtering is not working to find this user with the specified email if I jump back to the code that I was showing you at the beginning when I was hitting this part it turns out I was getting right to here just like as we'd expect so it made sense to work backwards so instead of getting hit right here on this break point what I was starting to do is dive into this method here and seeing why I'm not able to get that user but I'm not going to show you every single layer of this application I'm going to jump to one spot in particular that was really interesting where the problem was if I jumped down all the way to my repository layer I had something really interesting happening when I was looking at the command so this is the code here that was going to end up returning no users matching and that we were going to get a null finally in the end so why were we getting no users well when I was looking at the command that was getting set up here I could see that one of the parameters was being set up incorrectly it turns out that one of the parameters instead of being a string that I was expecting was a little bit more of a complex type and it's the one of type and if you don't know what the one of type is that's totally cool it's a really popular package we can use and in fact I've rolled my own version of this that I use for result types and getting exceptions or return type back the one of type allows us to pass around a type that can be one or more types and it sounds a little bit weird but the reason I do this is because I have different scenarios where I want to handle something like a string an integer or maybe a generic object and it's not a really common thing for me to do but in this particular case I have a filtering library that I've created myself and that filtering Library the types of values that I want to be able to support I use the one of type to be able to allow myself to have one or more types of data that I can filter on in this particular case I'm looking at metadata and the metadata could be a string it could be an integer it could be something else and in this case the email address is a string so what should be happening inside of this filtering code is that the string is getting passed in as the SQL parameter but in fact what's happening is the whole one of type the instance of that one of type is getting passed into SQL so it's going does anything match this one of type I don't need even know how the SQL driver is attempting to serialize that over to the SQL database but it's never going to match the concept doesn't even make sense inside of SQL so of course there's no match so the fix for this was actually super simple all right if I jump into my filtering code this line 60 is where the issue was and it might not be totally obvious so I'll explain it but this entry here is a key value pair so the issue is that the value that goes into this part of the parameter is a one of type so this part that I'm highlighting right here can either be a string or a nullable Boolean and the reason I'm doing that in my filtering is that I can either match a string in particular or I use the Boolean to suggest whether or not we expect the metadata but the issue was that this line didn't read quite like this it was simply missing this extra do value call so unfortunately what was happening was the entire one of instance was being passed in if I added this value in it's now passing in in the true value of what one of was representing so if it was a string the string would get passed in if the nullable bu was set that was going to get passed in instead so this one simple fix this dot value fixed the problem and that meant if I jump back over to this reset password tool when I went to go run this on the command line I could in fact go reset the password it completed successfully and at this point again I'm ready to push to prod because I've now proven that I've solved the problem in fact I've gone ahead and reset the password already I've just proven the code works and the interesting strategy that I used here compared to the last video was that instead of just going and writing unit test to solve it I lean more on a functional approach I created a tool that I'm going to be able to leverage for the future that integrated with the real system by integrating with the real system I was able to step through and not have to worry about data being fudged or being messed up it's the real data that I can work with so this is just a bit of a demonstration that you can use different types of tools for different purposes and in this case I did want to interact with real data so I didn't have to second guess what I was doing and at this point it's great I finally fixed the issue I was working on so how many times could I possibly break production right well check out this video next when it's available and you'll see thanks and we'll see you next time

Frequently Asked Questions

What was the main issue I encountered while debugging my ASP.NET Core application?

The main issue I encountered was a null reference exception caused by a failure to properly match a user's email in the database. I had initially fixed a symptom of the problem rather than the root cause.

How did I create a debugging tool for my application?

I created a console application that acts as a debugging tool, allowing me to reset passwords for users by connecting to my production environment. This tool can interact with my existing code and helps me troubleshoot issues directly.

What architectural decisions did I make to facilitate debugging in my application?

I structured my application as a modular monolith with a plug-in architecture, which allows for easy interaction between different modules. This design enables me to create tools that can access various parts of the application without needing to deploy separate microservices.

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