Shawn Clabough (00:02.27)
Hello and welcome to another episode of Adventures in.NET. I'm Sean Clabo and with me today co-host Mark Miller. Hey, what did you do Mark?
Mark Miller (00:08.234)
I did it! Sean! I just got here on time before the show started and sat down and got my microphone ready.
Jimmy Bogard (00:08.714)
Shawn Clabough (00:17.315)
Oh, nothing is going to get you in trouble.
Mark Miller (00:19.978)
Nothing's going to get me in trouble. Nothing that you know about at least. This is not an admission of guilt. Okay? Let me just start with that.
Jimmy Bogard (00:20.33)
Doesn't seem to be a challenge.
Shawn Clabough (00:22.098)
Shawn Clabough (00:26.134)
Oh, okay. Okay. Got it. Got it. Yeah. All right. Other co-hosts today we got Adam. F-U-R-M-A-N-E-K.
Adam Furmanek (00:33.386)
Adam Furmanek (00:37.746)
That's a very nice interpretation of a last name. Hopefully one day we'll get it right. However, I'm in a little different spot because I can see something just got into my apartment and is flying around. Hopefully it does not go into the camera and it's not that big. Anyway, I'll try to keep it... Nah, nah.
Shawn Clabough (00:57.252)
It's not something dangerous. It's not Mark flying around. Okay. Let's bring in our guest. He's been on the show before a couple times. So welcome back, Jimmy Bogard. Nice to see you.
Adam Furmanek (01:01.81)
No, hopefully not.
Jimmy Bogard (01:14.68)
Yeah, good to see you all again.
Shawn Clabough (01:16.234)
Yeah, yeah, I think the last time we met, besides the show is a project that we worked on together about three years ago, if you remember that one.
Jimmy Bogard (01:25.99)
Oh gosh, that's been a while. That's, it's so hazy with the whole COVID stuff. I'm like, the before time, like, I don't know, yeah.
Shawn Clabough (01:30.558)
Yeah, it was one that started like two months before COVID. And then it was out of Utah. So I won't say the actual company, but yeah. You got so much going on, Jimmy.
Jimmy Bogard (01:36.149)
Oh gosh, yeah.
Jimmy Bogard (01:42.849)
Yes, of course. Oh man, yeah. Yeah, tell me about it.
Shawn Clabough (01:48.186)
Yeah, so what is going on nowadays?
Jimmy Bogard (01:50.99)
Oh, sure. Yeah. So I guess today's episode is really going to be like the last, it's been like the last 12 months of my life of this project I've been on. Uh, there's been like a big, like legacy modernization migration effort. Um, and we, we shipped the first bits back in like March or so ship some more bits, like just last month. And now we're working on the next set of bits to push out in February.
Shawn Clabough (02:16.554)
Jimmy Bogard (02:17.038)
That's pretty much it, like just blinders on getting that stuff out the door.
Shawn Clabough (02:22.446)
Okay, what's that project doing? What are you, what are you, what kind of technology are you working on here?
Jimmy Bogard (02:26.97)
Oh yeah, so it's kind of funny. The company I used to work for, they got sold to Accenture. This was one of the projects that I was initially on back in like 2010, so a while ago that we were building some web apps for them. And they just kind of like been clients of that company for a long time. And then they reached back out last year and said, hey, we wanna do a couple of things. One is we want to migrate this application.
or the series of applications from like ASP.NET 4.8, or like the last version of ASP.NET that was on the.NET framework. We wanna get that off.NET framework and onto.NET Core 6.7, like whatever the latest one was. And then we're also moving the app from on-prem up to Azure. So it's like two big migrations that almost had nothing to do with each other except for a couple of little things. So yeah, it was just a big.
big migration effort for both the framework runtime, as well as like where the app even lived and was being deployed.
Shawn Clabough (03:29.99)
Okay, I have to admit I was a little bit selfish when I reached out to you because I knew you were working on this and I have a big.NET web forms application that I have for one of my projects that's been there for over 10 years and it's like, I need to get it migrated. How am I gonna do that? It's like, Jimmy can tell me.
Jimmy Bogard (03:36.365)
I'm not sure.
Jimmy Bogard (03:41.888)
Jimmy Bogard (03:47.838)
Mark Miller (03:48.951)
Wait, Sean! Sean, we can schedule guests to help us with our work? Can we do that?
Shawn Clabough (03:50.654)
Jimmy Bogard (03:53.498)
Shawn Clabough (03:56.078)
I didn't tell him that ahead of time, so he just learned.
Jimmy Bogard (03:59.763)
Let's suppose you had, just hypothetically, a big web forms application, how might you? Yeah, so that was like, there was a whole big initial discovery effort of just like, well, they wanna do this overall big goal. We wanna get off the.NET framework and onto.NET Core or whatever you wanna call it, NET 6,.NET 7, the latest one at the time. So there's an initial part of just like, well,
How feasible is that? Is that like a rewrite? Is there a migration path for us? Or does it really depend on exactly what sort of stuff is going on behind the scenes? Because ASP.NET as a framework you can build on is pretty big. I mean, it's been around for over 20 years. So there's just a bunch of stuff you can do in that framework. So I was like, this sort of the first thing was just, okay, it's been forever since I've even seen this code. Like what's actually going on here? And then what would be some of those migration paths for us?
So luckily for them, like that migration path wasn't too terrible because it was ASP.NET 4.8 whatever, but it was using MVC 5 and Web API, whatever the latest version of that was. So the code itself actually looked very, very similar to like modern ASP.NET Core sort of code, just like some of the names of things have changed. You mentioned web forms. So that was one of the things we had to look at. There are things that there is no...
easy direct migration path, because those things aren't supported in ASP.NET Core. And Web Forms is like the top of that list. I'm sorry to say. There's no, that's a, like every page you have to rewrite one by one. There's no, I haven't found any tooling out there for that kind of stuff. But this application didn't have any of that stuff. They had like two ASP.x pages and a couple A-S-H-H, blah, blah. A-S-H-X things, which are...
Shawn Clabough (05:32.993)
Jimmy Bogard (05:54.178)
I forgot they even existed. Handlers, right? Yeah, like, I forgot those are the thing has been so long. So there's a couple of those kinds of things that don't have a easy migration path, but at least for the like, if you're doing controller based stuff, MVC or, you know, web API controllers, it's actually quite a bit easier to do so. Sorry, Sean.
Shawn Clabough (05:54.314)
The handlers, they're handlers, yeah. Yeah.
Shawn Clabough (06:16.358)
Well, it was either David Fowler or Jeff Fritz made me aware of this project called Core Forms. I don't know if you've ever heard of that. It is something that could possibly be used to modernize a web forms project so you can actually run it in a.NET Core environment.
Jimmy Bogard (06:26.034)
Jimmy Bogard (06:39.402)
I mean, that makes sense too, because there's so much ASP.NET code out there, Webforms code out there. I mean, like I've got the gray hairs to show it, that's where I cut my teeth, was building, starting with ASP.NET 1.0. That was like my first really professional web job. I mean, I did a little bit of ASP classic before then, with record sets and all that junk, but like there's so much of that sort of stuff. So it makes sense. It's like maybe Microsoft won't step into that.
Shawn Clabough (06:59.955)
Jimmy Bogard (07:06.058)
But then there would be other companies that would want to step into that void and provide that sort of path for folks. I mean, I was given one project that migrated a 50 year old mainframe from COBOL to web forms, or some kind of.NET something or other. They would transpile COBOL to.NET and like, it actually worked. But there are gonna be those markets for those kinds of things. This stuff though, we...
Shawn Clabough (07:20.724)
Shawn Clabough (07:31.11)
Yeah, that's about the same time. Yep. That's about the same time I started professionally. Wow. 97, 98, things like that with classic ASP and things like that. And so, and then saw you, they were going to do it. A SP plus was going to be the name originally. And then they went with, you know, ASP.net.
Jimmy Bogard (07:37.983)
Jimmy Bogard (07:47.078)
No, of course they're kicking themselves. Like, why do we still have this dumb ASP in the name? Active server pages? Oh, come on. But it's branding, so, you know, it's hard to.
Shawn Clabough (07:52.755)
So I take it you didn't decide to do a rebuild.
Jimmy Bogard (07:59.398)
No, and that was one of the things we got kind of lucky that the client approached us at the same time Microsoft was releasing some tooling to make this sort of stuff easier. I can't share any, you know, NDA discussions, but you know, they have this problem where like they have tons of clients on.NET 4.x. And what do we do with those clients? You know, we do support the.NET framework going, like indefinitely going forward.
Well, that's actually what they're doing now, right? NET 4.8 has no support in date. But the idea that there are new features getting added, or I mean, especially with this team was running into, is that they would encounter bugs in libraries and find that library has been updated in like seven years, because it's just targeting that framework. And those authors or companies just didn't wanna keep those things going forward. So for them, it was just basically a kind of a risk mitigation thing of, okay, sure, the.NET framework is fine.
but all these other things that our code is depending on are slowly getting abandoned. And we want to make sure that this code base can live for 10 more years or 15 more years. So can we do this migration path to.NET? And my gosh, I'm going to say.NET 7 because that's the name, but I mean, everyone else thinks of it as.NET Core. So I'll just say.NET Core as the other.NET, the new.NET. So to migrate into there so that they continue to, you know, they're still building out features on this thing. So they want to get off into something like that.
Shawn Clabough (09:24.074)
At least they didn't call it.NET NT. Yeah. So what kind of a...
Jimmy Bogard (09:28.773)
Oh,.net vista. I mean, I don't know.
Jimmy Bogard (09:36.726)
So yeah, that first sort of stuff we did was just to look at, okay, what the heck is this app doing? What is it depending on? What are the migration paths for each of those components? So like I said, we looked at, well, it's a.NET ASP.NET 4.8, but it's MVC. And so that's a very similar code base to ASP.NET Core MVC and Web API. So we didn't have to do a big rewrite of the features and the pages.
We could kind of just migrate each of the pages individually, but not have to change drastically what they are doing underneath the covers in order to do so. If you're on web forms though, I mean, unless you're using this like a third party tool, if you wanna go straight vanilla ASP.NET Core, like web forms is not a thing there. So you'd have to like page by page, one by one, rewrite them and get them pushed over.
Adam Furmanek (10:30.966)
But when you say...
Shawn Clabough (10:31.302)
Yeah, the thing that I'm missing the most on the new versions of.NET, I think, is the new language. So you can't use C-start A, things like that. Go ahead, Adam.
Jimmy Bogard (10:37.319)
Adam Furmanek (10:40.778)
But when you say rewrite page by page, you can't launch it until you rewrite everything, correct? Or is there a way to host that side by side in some magic?
Jimmy Bogard (10:51.542)
Yeah, that's that was the other kind of big thing that Microsoft pushed out is tooling to help support this incremental migration from one app to the other. And so it's kind of, there's a few things that came into play to kind of come together to enable this kind of thing. And one of them was Microsoft created this their own reverse proxy, it's called YARP. So for yet another reverse proxy. And so this reverse proxy is written in.NET.
and it can be hosted inside of an ASP.NET Core application. So your ASP.NET Core application can handle requests and it can also reverse proxy to do other things, which is a little bit different than something like NGINX for example, which is a kind of a pure reverse proxy, that it just does that job and that's it. Where with this now, and they call it this, it's part of the system web adapters set of tooling that they offer, is when you,
decide to, they call it upgrade. We decide to upgrade a project. You can even do, they even like Visual Studio plugins. You say right click project, upgrade, and it'll walk you through this assistant that creates a new ASP.NET Core application alongside your existing ASP.NET application. And that new ASP.NET Core application is gonna be the target or landing place of all your new controllers and pages and whatnot. And the way that reverse proxy works is the request first comes into this ASP.NET Core application.
And if it cannot successfully handle that request, like it doesn't have a controller or if it gets an exception or something, then it forwards that request over to your.NET Framework application. And it waits for the response and then it then feeds the response back out as a reverse proxy to the original client. So because that reverse proxy can't handle like anything,
It is kind of magical. Like I do a demo of this. And when I do this, when I show it to clients and talks and stuff, it's just like you right click it, you say whatever Visual Studio now launches two applications, but the reverse proxy now that just does nothing because there's no logic in there. But when it launches the browser, it's got a new port because it's a new application, but the app looks exactly the same. And it supports like everything we could throw at it. Like we threw a, you know, like single R at it, you know, with different kinds of
Jimmy Bogard (13:15.242)
different kinds of single our clients to be able to do web sockets and all sorts of great, like it handled all that stuff. It was just, it really was kind of magical. Like, oh my God, this is so much easier than it used to be. When I did this a couple of years before this, we were migrating to ASP.NET Core 3.1 and this tool didn't exist. So we actually had to include NGINX where every month we would say, okay, what are the new routes we've got and use NGINX to add entries to.
manually forward those routes over to the other application. But now, because it's self-hosted, like anytime a new controller shows up, that thing handles, or any kind of thing that handles route, that ASP.NET Core application now handles those requests.
Adam Furmanek (13:56.446)
That sounds really cool. And how does it look like when it comes to hosting that in Azure? Is it any different from hosting a regular app service or do you need to do some tricky stuff?
Jimmy Bogard (14:08.398)
It is different because it is two applications. Like you have an ASP.NET Core application and now you also have your existing ASP.NET application. So this client, for example, even though we're migrating them to Azure, we wanted to get sort of the first deployments to be as soon as possible. Like we need to get this in production and start seeing if this is working and see if there's any problems. So initially we actually went into their on-prem IIS instance.
and hosted in IIS the.NET 7 application alongside their existing ASP.NET application in, oh gosh, IIS what it calls them, applications, yeah, IIS applications. So we'd have to do some changes there to like, you know, we had to create the whole, you know, build, package, deploy pipelines to this new thing. But our very first deployment was literally this empty proxy application that handled all the requests and just forwarded everything over, it was on the same machine, but to the ASP.NET.
framework, ASP.NET full framework application.
Adam Furmanek (15:13.19)
Okay, so for on-prem, this is kind of doable. Obviously for Azure, a little bit trickier, right?
Jimmy Bogard (15:20.45)
It is so I mean for Azure the only thing we really needed to do was we didn't want for me for on-prem as well The ASP net full framework application. We wanted to make sure it could not handle external requests But luckily I was working with an awesome Azure networking person who like knew all that junk and can be like, oh no this can only accept requests from this thing over here and I don't know any of the technical things behind it, but I know I got that to work. I'll make sure there's like
VNATS or private link? I don't know, lots of things that I just like, someone else could take in that stuff. But yeah, it wasn't not straightforward, but it was definitely like, it's definitely possible just to have like a network person understand what they were doing to make that work.
Shawn Clabough (15:51.517)
Shawn Clabough (16:10.566)
Is there any tricks that you had to do for authentication? You know, so because you're trying to authenticate into both applications at the same time. Yeah.
Jimmy Bogard (16:16.678)
Oh man, you're giving me flashbacks now. I'm not in a good way. Oh yeah, so authentication was a big, I mean, authentication and security identity is like always a huge concern of any application. It's never just like a drop in, like, I just enabled this check this box, everything works. So yeah, it was. One of the things we tried to do earlier was like, well, let's get authentication migrated over first. But the problem is you still run it, it's two applications.
two separate web applications. So if I have two web applications and I wanna enable authentication between both of those, like suddenly you've introduced like a single sign-on sort of situation because I've got two separate deployed web apps. Now on-prem, it was easier because we were using, in this case, we're using forms authentication with the ASP.NET whatever. And so things like data protection certificates were already installed on that box.
that the apps are deployed to. So we could, in the ASP.NET Core application say, okay, you're also doing forms authentication and your data protection certificate is also right here. It's just also in the same box. We also had all sorts of problems with like, well, cookies, we needed to make sure that those all still work. So what we wind up doing was deferring authentication until the very, very end. And then Microsoft exposes a series of adapters
so that the authentication mechanism inside your ASP.NET Core application can still work in a transparent manner. So you're not authenticating on that. You're not logging in to that web app, but what it will do is, it's kind of wild. You enable an extension on the ASP.NET Framework application that exposes APIs that when the ASP.NET Core application needs identity and login information, or like really like your cookie details, it will make an API call.
to the ASP.NET Core application. And then there's an API key to make sure that only that originating server can call those APIs. It returns back your identity claims, just as Jason, and then fills your claims identity or claims principle with those claims. And your ASP.NET Core application is just like, well, I got these claims, they said you're logged in, everything's good to go. So our controllers in action still just had,
Jimmy Bogard (18:40.55)
enable authentication or use authentication and make sure that I authorize these different ones. But behind the scenes, it would call an API to get that information out. We didn't have to do that. We could have done the shared cookie thing, but we knew we were gonna have headaches with that approach. So we just said, forget it. We're going to just wait until the very end of the project and that'll be the last controller we migrate over from the ASP.NET 4.5 application or 4.8 over to the core. At that point, we...
you know, all the authentication logic would be moved over as well. And, you know, still using cookie auth, whatever. See, even the same data protection certificate, we just didn't want to try to go there out of like actual shared authentication, like logging in with either app sort of thing. That was just going to be too bonkers to try to figure out. But there's tons of options there, but it's highly dependent on what you're doing with authentication.
Adam Furmanek (19:35.11)
And this application you're talking about how many hosts was it utilizing.
Jimmy Bogard (19:42.39)
So the existing on-prem app, which was the same as Azure, I guess, was... It was two web apps, each of which were load balanced between two web servers. So they weren't enormous web apps, like it was for... These were web apps for non-profit scholarship, scholarship application, and ongoing sort of help for the students sort of thing.
So it was a pretty constrained set of folks, a number of people that were using this. A part of the deal is we, like feature development can stop. There are always product owners wanting to add features into it. So we needed to make sure that whatever approach we took wasn't putting these big like giant code freeze roadblocks that'll be like, okay. We just wanna like make sure that if we're scheduling work, that it's not the same week that we're trying to schedule moving the specific controller over to the new app. That like, okay, we'll do a little bit, but it's no...
three month code freeze, flip the switch, hope everything works. We did weekly releases as we went of code moving over from.NET Framework to.NET Core,.NET 7.
Adam Furmanek (20:47.478)
Sounds good. What about caching in this scenario?
Jimmy Bogard (20:53.47)
Oh, so I put caching in the same bucket as shared state between servers, because caching was also like kind of like, we need to share, well, I guess not exactly, because their current app, but caching, they had an ASP.NET Core was using SQL caching. So it was a shared state between all that. So there are some things we could have, like the ASP.NET and the ASP.NET Core applications could share state, but that was really just the business data.
So the business data used a shared library that was both Entity Framework 6 and in Hibernate, if y'all remember that. Cause again, this was 2010, so that was like state of the art back then. So it was using Entity Framework 6 and in Hibernate. So the business data could easily be shared by both applications because both of those libraries can be multi-targeted for.NET Core and.NET 4.x. So that was fine.
The other data could not necessarily be shared. So output cache couldn't be shared. Session state could not be shared. The, let's see what else, signalR could also not be shared, it turns out. Oh gosh, what else? The, I said, oh, hangfire was another one that we had a really tough time with trying to get to be shared there. So like output caching, we said, well, that's a page by page basis.
we don't have to like share output cache between like, because those are page-based. But then we had SQL level caching. So that's when we looked at, okay, what kind of libraries we're using for the data access and do those data access libraries have, do can they run at both of those runtimes? And it turns out they could. So the data caching was able to be shared across.NET Core and.NET Framework. Things that couldn't though like,
the SignalR stuff, we had shims put in place so that if you wanna raise a SignalR event, it would actually call an API over in the Dynad Framework application to like push a message out just because the version's time went up. And sometimes we just deferred features too to say, maybe we'll wait on the controllers that use SignalR till the end so we can migrate them all at once to be like, okay, now we're all on the new SignalR.
Jimmy Bogard (23:19.83)
So there's a lot like feature flagging sort of stuff going on to like determine, you know, what is the right behavior for depending on which purpose. SignalR is also tricky because we're also moving to Azure and we were using a SQL backplane for SignalR so that, you know, both apps, because it's, you know, it's in a web farm sort of scenario that there's only one message getting pushed out. But in Azure, Azure does not support, SignalR does not support Azure SQL.
in Azure because they want you to go to Azure SignalR, you know, like their actual hosted service. So they're like, there's Lydia found the stupid if statement that says, query these SQL Server server properties. And if it returns back with Azure and no one exception, we don't support you. So like that's, I thought that was just kind of rude. It's like, it's not like it doesn't support it. You just don't want to support it. So like that's, that's great. So you just little things like that. We would run into the things that being
exactly supported on both sides. But Hangfire was another big problem for us because... So Hangfire is a distributed task and scheduling engine that can be run inside of your ASP.NET application and ASP.NET Core. So it can spin up background tasks. It also has cool things like there's management UIs that you can log in and see like, oh, these jobs failed, go restart them. And supports...
persistent storage for both the metadata and the queues themselves. We had an issue though, they're like, Hangfire is very easy, you can just basically point it at a method in your class and say, run this in the background sometime. And it will do all the magic to like capture the right data to be able to wake up and run that one method sometime in the future, it's kind of magical. But we had the problem that if the ASP.NET core application or yeah, ASP.NET core application is also listening to the same
jobs as the ASP.NET application, they can then like both run the jobs at the same time, or maybe the code doesn't exist. Like we didn't extract out the job code in a place the ASP.NET Core application could look at. So we wound up creating two queues for both applications. So our application logic had to know where does this application logic live? Does it live in the.NET Core side or does it live in the.NET Framework side and route that message to the right queue depending on the framework. And there was that...
Jimmy Bogard (25:42.958)
sinking feeling when we got the exception the first time of like unable to locate method. And we're like, oh no, it's because it's on the other app and we're over here. Whoops, now we got to figure this out.
Adam Furmanek (25:55.99)
So you cover really interesting scenarios. One thing I'd like to ask is when you were doing this deployments, did you have any outage? Did you take the application down for the time of the deployment or did you keep it like up and running?
Jimmy Bogard (26:13.614)
Oh, that's a great question. So for the.NET 6 migration or 7 migration, no, we did not do application downtime. There are very few cases in which we needed to like stop people from using the app to be able to switch over something. A big one was that hang fire stuff. Like we had to like stop the web app and make sure the queues got drained. And then, okay, now let's go ahead and perform the thing. But the team themselves, they were kind of used to doing this anyway. They would go to...
Like if they're doing a big data migration, like adding a new column to a table that they need to like seed with data, they would take the app down, show an offline page and be like, under maintenance, run their stuff and then bring it back up again. So for those kinds of things, we'd schedule out, but otherwise, no, anytime we had new controllers and actions coming over, we just go and push them out. But you guys haven't talked about yet the actual controller action migration. That is something we actually had to do as part of this.
Adam Furmanek (27:04.106)
So you're saying...
Adam Furmanek (27:11.506)
Okay, so just to wrap on this up, so you're just actually saying that you can kind of migrate the code from old.net framework to.net core with no downtime.
Jimmy Bogard (27:22.43)
Yeah, exactly. As long as your deployment process has that facility. So if you're using something like Octopus Deploy or like deployment slots in Azure that support those zero time downtime deployments, we did exactly that, that we were doing continuous deployments as we were finishing controllers every, once or twice a week, whatever it was, deploy out the new endpoints. And really the users didn't even know that because the cookies didn't expire. The...
the new endpoints just went to the new app, but they didn't see anything different. It didn't look any different, feel any different. It was just a seamless experience for them. So we're able to like very low risk, move one controller at a time, all of that stuff over, get it tested and pushed out and not have any of kind of these like big bang, flip the switch, cross your fingers, hope it works sort of thing.
Shawn Clabough (28:17.354)
So if somebody's looking at doing this, they could essentially just start with an empty.NET Core project, put YARP in it, and then basically just pass through everything to start with. And then they just have to point their URL to that YARP basically, so that then everything's passing through and then piece by piece, they say, okay, YARP or.NET Core app, don't pass that to YARP, do it yourself.
Jimmy Bogard (28:28.831)
Yeah, that's what it is.
Jimmy Bogard (28:43.402)
Yep, that's exactly so it's basically the last, it's just middleware and it's the last one in the list. So if nothing else handles the request, it's like, well, I couldn't do it. Let's go ahead and forward the request over. So even we have these issues of like this question to our team of, okay, as we're writing a controller and action over, we're actually just copying and pasting the code. That's how we're doing our cut and paste and basically just correcting namespaces. Those only big things that really change from side to side.
But if there was a problem, we did find that if you get an exception with the ASP.NET Core application, by default, it will just forward the request over to the Dynat Framework application. So we would have pages that were actually broken, we didn't know it because we had not deleted the code and the other side. So we wound up like renaming controllers of like, delete me. So that like, we would still blow up with a 404 or whatever, like, you know, just get the kind of error page, the normal error page.
on the main one.
Shawn Clabough (29:46.09)
So it seems like you could actually do this approach from migrating any type of project over to.NET Core. Somebody had just a pure Angular website and they went and moved it to Blazor or something like that. They could pass things through with YARP and put pages up in Blazor and then just have a mixed match of that.
Jimmy Bogard (30:06.506)
Yeah, I've even seen people do that. Again, like your web forms example, what they'll do is they'll configure their routing so that the ASPX are now like, you know, MVC style or even Blazor, but it has the same URL. And then they may do then redirects like, okay, let's get these people onto the right URLs. But you know, as they migrate over, they'll still like, okay, we'll just like, oh, grab that route. Because like, do we go correct all the links in the ASPX side all at once?
Probably not, so we were using these, this redirects to like say, okay, let's not touch all those pages that are still linking over. Let's just use some redirects to get them over to the right spot.
Shawn Clabough (30:52.134)
To me, this sounds like just an awesome approach, you know, especially for my case that I had to bring you on for, for getting rid of web forms that.
Jimmy Bogard (31:04.198)
It is like, again, this stuff released, we started this project in October of last year. It's a guy about 12 months ago. And this sort of, these tooling was only in preview, like just two or three months before that, like July. So it was just like pure great happenstance. Like, oh my God, there's something that just seems tailored built for exactly what we need to do, which is this incremental migration with the sort of seamless ability to bring on new endpoints. It was so much more difficult before, the point where we were like, someone asked for this, like, ugh.
You know, if you want to, it's fine. You just have to pay way more. Um, so we went up riding, uh, just from like a numbers perspective, it was, uh, something like five or 600 total actions and a hundred and something controllers migrated in about two and a half months with a team of four. And if I was to look at that project, you know, like even just a year before, I would have tripled or quadrupled the length of time I would have taken. Cause it'd been very manual effort for each one versus now.
Shawn Clabough (32:01.366)
Oh, easily, yeah, easily, yeah. So could you go with the hybrid approach with this? Having YARP and the new application in Azure and then the old one on-prem and...
Jimmy Bogard (32:02.804)
Jimmy Bogard (32:14.438)
That is a great question. I went for like just being the architect on this, I took a kind of a, trying to mitigate the most risk possible. So that's why we didn't know what was good. We were doing an Azure migration at the same time. We didn't know what was gonna win in terms of like what was gonna finish first. So I planned for having the.NET 4.5 or 4.8 and ASP.NET Core application on-prem and an Azure.
It just wound up happening that we finished the.NET 6 migration like two weeks before Go Live of the.NET or the Azure migration, but that was completely unplanned that we just happened to. One thing we did want to worry about as well is latency from the proxy to the framework application. On-prem, those are right next to each other. I think we measured like 10 milliseconds of extra latency, so not a big deal.
But in Azure was slightly more, if we start to make those further in part, the proxy and the framework application, that's when you're gonna start to notice degraded experience for the end user. And so that was like top of mind as well is that we don't wanna make this worse. We kept telling him, yeah,.NET cores faster. You won't know until like four months from now, like we don't wanna make things worse. And then it gets better. We like, let's try to make it marginally worse just with a little bit of extra latency.
Adam Furmanek (33:40.938)
So based on how you did that, can you share anything you did wrong? I mean, you mentioned problems with Hank Fire and other stuff, but did you do something that you actually regret you did and now you would do it completely differently just because you learned something more?
Jimmy Bogard (33:48.789)
Jimmy Bogard (33:59.226)
Yeah, so we got a chance to kind of do this again in the next project. And so there are a number of things that we just found. Okay. Uh, we'll kind of skip past that discovery, you know, you just call them discoveries. I don't want to call them like mistakes in front of the client, you know, since I consult, I was like, no, there's, you know, discoveries we had. Um, I mean, the, the big one was exactly. So like the authentication was a good, a big one. We're like,
Adam Furmanek (34:18.506)
Definitely, we don't have problems, we have challenges.
Jimmy Bogard (34:26.15)
Initially, we made that the first one and it was so difficult to do. We're like, just forget it, make that the last thing. And then maybe we could do before, but we're the ones deciding the order in which we tackle the controllers as a team. So we can just make that one the last one and just say, we'll do authentication and log in last. All the business logic behind that stuff is already cross platform because it was using custom authentication. Like it wasn't using ASP.NET identity.
or like the whole identity stack that it had. So we just said, we'll just do that last, just wait until the end. What else? Hangfire is another one. We initially took a tactic, let's make all these jobs cross-platform as well so the jobs could execute anywhere. But in reality, it was a lot easier just to add a attribute to the Hangfire job to say, it's this queue, not that queue. That was a lot easier. So we just kind of skipped these sort of
Jimmy Bogard (35:28.718)
I would have, one of the things we had a question about is like which ASP.NET core should we target? Should we target the LTS version or the STS, which is basically ASP.NET core six versus seven? And so we, the client wanted to go LTS, but they were actually missing features in ASP.NET core seven that we had to find solutions for. And one example actually is output caching.
that didn't exist in ASP.NET Core 6. So we had to find some like janky open source library that was like backporting ASP.NET Core 7 output caching to 6 and did that where I was like, I probably would have pushed a little bit more like, it's okay to go 7. The end dates for support are almost exactly the same for 6 versus 7. So, you know, it should be fine to get that, you know, slightly difference in support end dates to like not have to have this custom code that you don't wanna support.
Adam Furmanek (36:25.206)
Cool, cool. So going on this direction a little bit more, the approach you took and everything you learned, do you think that could be applied to kind of, more or less, any web forms application? Or did it just work in your case because of some characteristics of the app you were working on and wouldn't work well in some other context?
Jimmy Bogard (36:46.41)
So a good example is the web forms piece. There's no analog to the actual pages themselves. So if you look at the landing place for all the UI code, you wanna, for us, it was very, very similar. It was MVC5 to ASP.NET Core. And those types of namespaces are almost exactly the same. A lot of the guts changed. So we didn't bother trying to migrate any of the middleware from ASP.NET, like any of it.
Those are all rewrites, but it was a lot of stuff that either was now out of the box or had very similar equivalents to. So if you look like your web config, there's probably a bunch of stuff in there that has an equivalent, but we're not trying to port that over. We would just choose the right way of doing those things over in ASP.NET Core. But otherwise, that's what kind of the additional survey was, was looking at what is the code that exists and is there a migration path for all of that code?
Web forms, for example, there's not. And even in some of the NVC stuff there's really not a good migration path. So we want to understand like, were there things that we need to migrate first before we came over? And one example is that we were using some file upload control that hadn't had a release in eight years. And it works great still, but for that one, that company went out of business. And so we're like, well, we can't use that control anymore.
So we decided we'll go ahead and upgrade the control in ASP.NET first, so that we move it over. It's actually the same component and library. And so it just like seamlessly moved over. But it is, that's like the, that's the whole point of the initial discovery phase. It's just like, okay, do we have a story for the migration path for all this junk that's in there?
Mark Miller (38:35.766)
Jimmy, I'm interested to know, how do you know when you're porting that what you've ported is going to work as well as what you came from? Are you relying on, for example, test cases? Are you relying on multiple people looking at the code, reviewing the code? What's your certainty level?
as you push out each change, right? And what are you basing that certainty on?
Jimmy Bogard (39:12.298)
So that was a big question for us of just, how do we get confidence that the things we're moving over are working the way they used to before? So there were a few ways we did this. One was like all the business logic was already covered by tests. Like we had, I don't know the percentage, it was like 90% coverage or something like that, but everything we built was already covered in unit integration tests. There was business logic right behind the UI.
For the UI itself, we relied a couple of things. One was, I guess all the code we wrote did go through a peer review process. So everything went through pull requests. So another team member had to look at it. And that was mainly to look at like, not everything we pulled over could just be copied and pasted and done as is. So we would always review like, we had to change something, how something worked because ASP.NET core is slightly different. It would go through a review process to see does that make sense. Then,
everything went through a QA process as we were writing it to do just basic happy path testing, to make sure like, you know, the code paths or the, then the happy path of the UI would work as expected. But before Go Live, and this was, and this is more of an Azure thing, so I guess it's not that, we did go through a complete regression test of the entire application that took about two weeks before we said,
we're live, we're good to go live in Azure, but for all the individual controllers and actions, those would go through our QA folks. So as we finished them, they would then go do the regression testing for each individual page one by one. And it went to the same rigors as it was like any other feature that was being developed.
Mark Miller (40:52.105)
Mark Miller (40:57.166)
So the team of four are not, that's not the QA team. That's not included in the QA, is that right?
Jimmy Bogard (41:01.646)
Oh, sorry, four devs, me, infrastructure folks because we're going to Azure. So there was a whole mess over there. And then QA, Scrum Master, Product Owner. So like four devs plus like the other people that you need to like, you can't just have devs because they'll, you know, inmates running asylum sort of thing. So four devs. Yeah, so like four devs, they were like just, you know, sort of dedicated to
Mark Miller (41:21.678)
That's my favorite. That's my favorite, Jimmy.
Jimmy Bogard (41:32.018)
migration, this.NET 7 migration.
Mark Miller (41:34.594)
So are there moments along this path where you realize that the test cases or whatever you need for that sense of security is inadequate and that something's gonna need to be filled in here. Does that happen along this path? Or is your 90% coverage nailing it and everything else with regards to the UI, you know?
Jimmy Bogard (41:59.71)
We actually did bring in, we did bring for the business logic because that code was relatively easy to make cross platform anyway, that there wasn't a whole lot we had to do that. It was really UI stuff we knew that these frameworks are completely different and we need to cover these cases. So we actually did bring in additional QA folks to be able to cover that. And also just not to be a bottleneck because we know like we are gonna have to retest every single page in this application.
in a relatively short amount of time. And so we did bring additional QA folks over to be able to accomplish that. Dedicated to just like, okay, we're pumping out these pages they have to bring them over. But in terms of things we ran into is like, because we were basically retesting the entire application, we ran into many bugs that had been introduced, but not by us. It was just like, just been introduced over the years. This is a 15, you know,
Mark Miller (42:54.498)
Jimmy Bogard (42:57.374)
13 year old application. And so we'd hit things like, oh, actually it wasn't a bad thing to do anyway. That's like, we're finding things that we'd have to go in and be like, okay, this actually, does this already exist? And so we go to production and find, oh, nuts, actually it's in production as well. So this is a regression, this is not a regression. This is an existing bug and get a triage like we did everything else.
Mark Miller (43:20.35)
And are you are your devs writing this those test cases in some cases or is it the QA team that's doing that? Okay.
Jimmy Bogard (43:26.654)
The QA team, our devs, the devs that we brought in were more experts in ASP.NET Core and ASP.NET. So they understood both of those frameworks. They were not an expert in the domain or the application. So plenty of times we hit, we're like, okay, I just migrated this. How do we even use this page? Cause I don't know how to get here. It's like part of a workflow. I'm like, how do we set up the data? And so we did have domain experts to like, and QA to like, okay, here's how you can just even test this page locally.
Mark Miller (43:39.704)
Jimmy Bogard (43:54.91)
because you may not know how to get to this spot in the app.
Mark Miller (43:58.434)
Yeah, so that's cool. Yeah, I'm just thinking about your delete me class. At some point, you've got to add a new test case for that, I would imagine, just to support that porting process.
Jimmy Bogard (44:06.286)
Jimmy Bogard (44:14.626)
Yeah, so there were cases where we left controllers over there and we even had like a unit test that was like, here are all the controllers that should not be deleted yet, for whatever reason. And so anytime we had that, like we just had a test that would just be like, if there's a new controller that shows up that shouldn't be over there, then it'll be in this list here. So it's like an explicit opt-in that says, this is the things that have to stay till the very, very end. Like that was one of them. There are a couple others of like, oh God.
Just random other little things we knew that had to wait till the very, very end for the migration process.
Jimmy Bogard (44:52.318)
So yeah, at the very end, it was an application with no controllers and only like web config. And we're like, okay, I guess it's time to delete this app because it's not doing anything. So actually we were in Azure by that point. And so we monitored the app insights traffic to see is this app getting any more traffic? And after a week of no traffic, we're like, well, then we got it boys, kill it. And deleted the app and it was gone. That was when we celebrated.
Adam Furmanek (44:52.778)
Mark Miller (44:52.833)
Adam Furmanek (45:19.03)
So if you were to present a short cookbook, how to tackle a migration like that, what would be the steps you would focus on the order? I mean, Sean would probably be very interested in that.
Jimmy Bogard (45:37.051)
Yeah, so it's kind of a, there was like the kind of discovery process, the planning process, the execution and then like the sort of, sort of tear down at the end, kind of post, post migration tear down. So the migration themselves, like there wasn't a lot of input from me as the architect, because like basically just sort of churn out these different ones. We definitely hit things though. We're like, okay.
this is a very specific way of accomplishing this problem in ASP.NET classic. How do we accomplish this problem over here? And do we want to provide shims to allow both to work? Do we like modernize it to bring it over or do we kind of keep it as is and just sort of provide that shim? So it was basically those kind of four main strips of kind of discovery plan, it's kind of execution and then tear down at the end.
Jimmy Bogard (46:34.538)
for the discovery and planning process, a lot of that was just understanding the lay of the land, getting the survey of what's going on in this application, what features does it have, and what dependencies does it have, what libraries and components is it using, understanding the migration path for each of those. And then from the planning perspective, we were looking at how should we go about tackling this? What is a good order for us to migrate these sort of things? So I would go through and look at like...
Well, what are the, what are groups of related features? So if developers are working in an area that they can tackle, you know, all of these things together, cause it's, you know, very similar kind of stuff. Um, what are the, what are the controllers of things that have these dependencies that maybe tackle problems we want to defer on or want to tackle together? So we looked at, um, I made this giant spreadsheet of like, here are all the controllers, here's how many dependencies they have, here's how many using statements they have, cause that kind of tells us how much it's doing, how many actions it has.
then what features are being used in this controller? Is it using, is it using signal R? Is it using caching? Is it using, you know, all these, just list out the features it could be using to understand how hard is it gonna be to migrate any one of these things over? And that helped us kind of sequence it out or group things together or defer things to be like, okay, we need to spike signal R. So let's not do any signal R yet. Let's just defer those and someone spike it out. Well, I would be the one spiking out.
figure that out and then, okay, now we've got a plan for any of those kinds of controllers to move over. So we definitely started with like low hanging fruit first as we figured out some of the harder problems and then say, okay, now we've got a solution, let's go ahead and then pull those over and get them scheduled with everything else.
Adam Furmanek (48:19.146)
That reminds me one of big migrations I faced in completely other space though, which was actually done twice. First they wanted to capture all the use cases, plan them ahead and then failed because it was just way too big. So then they started doing stuff like incrementally, starting with small things and then moving on to bigger, bigger as they learn on the go.
Jimmy Bogard (48:47.786)
And we also had a Wiki page for all this too. We'd read like, as we figured out how to migrate X, Wiki page entry, okay, because we just keep running into these. Like now we have a solution for X, so go put it in the Wiki. And now the next controller we hit that, we have the solution already written down for us.
Shawn Clabough (48:47.985)
Shawn Clabough (49:05.29)
Okay, so we'll make sure to put a note to all your articles on this, to your website in the show notes so everybody can get there. We're getting towards the end of time, but I can't let you go before you kind of give us an update on all the other things that you work on. I don't think you ever sleep. You got auto, you got this project, you got automapper, you've got mediator, and you've got this thing called Respawn.
Jimmy Bogard (49:22.841)
Jimmy Bogard (49:33.458)
Oh yeah, that's another one.
Shawn Clabough (49:34.841)
We haven't talked about Respawn before, but just give us quick updates on those projects.
Jimmy Bogard (49:37.343)
Jimmy Bogard (49:40.862)
Oh gosh, I mean, a lot of those now, the big ones for like automap or mediator is just understanding how should I support net frameworks going forward. So big thing I'm looking at is like, do I abandon like.net framework completely on some of these things? Because, you know, you can't use various C sharp features until you start getting on new frameworks. So there's this whole question of like, do I have a very wide umbrella of people I support or do I just say, forget it?
If you want the old stuff, use the old stuff. So that's a lot of my libraries I'm looking at like, what should be my sort of framework support plan going forward? Like if Microsoft doesn't support the framework, should I anymore? I don't know. So that's really kind of where I'm not in a lot of my open source stuff is just understanding like, do I modernize all of these and abandon the old stuff versus try to have a really wide umbrella of people I can support.
So far, because no one pays me to work on these things, I'm like, I'm okay abandoning old stuff because I only care about the people that actually pay me to work on this stuff, which is mainly like clients that need stuff done, whatever.
Shawn Clabough (50:52.634)
Yeah, maybe it'll be like, you know, the military or some people that say hey, we'll pay you to You know keep those things up to date, you know, like military pays Microsoft to keep you know updates for old versus Windows
Jimmy Bogard (51:03.418)
I mean, you know, someone wants to pay me, that's a totally different story than like no one paying me and be like, well, this is just hobby. So
Shawn Clabough (51:13.506)
Exactly. Okay, let's move on to picks unless there's any other last questions. Nope. Okay. Adam, what's your pick this week?
Adam Furmanek (51:26.614)
So I will have two picks. One of them is a crazy sport called underwater hockey. So there is a very nice YouTube video. You can try to take a look and don't try that at home. Think twice before doing that. But generally the idea is you go down the pool, you keep your breath for like seconds or minutes and then you play hockey underwater. So really cool, really nice.
Yeah, I encourage you to take a look and try it out. And the second, because I always try to go with technical picks, the other pick I have is, well, a feature of Windows that many of us never used or stopped using just because we have BitLocker. And this feature is called Encryption Fire System, EFS for short. If you have a search computer or
If you want to protect yourself on even more on some hosting environment where you can't, for instance, enable BitLoka, then EFS may actually be a very interesting way of protecting some of your personal files from, for instance, some monitoring applications provided by the system administrator. Just keep in mind, because Internet pretty often tells you that if you Google up
how do I change a user password from the command line? They just give you a tool that basically resets the password and breaks all your EFS files. So generally make sure you know what you're doing, you back up your keys and you stay safe this way. So that's the pick for today.
Shawn Clabough (53:07.858)
So for underwater hockey, are they using a normal hockey puck or is there something special there?
Adam Furmanek (53:13.61)
They use... Yeah
Mark Miller (53:13.83)
It's I just looked at the video. I was just going to say I was because I was freaking out when Adam talked about it at the beginning of the show. I was like, no way. I was just I was going to get so mad. But then I saw the video and the puck looks normal. But the stick is held in is small and held in your hand. It's like a scoop kind of thing. And I'm like, thinking, oh, OK, I can calm down. I can cancel my therapy appointment that Adam made me book.
I get to get to a better place in life now. Yes.
Shawn Clabough (53:42.598)
Ha ha ha!
So it's like in a swimming pool, but the ice is the bottom of the pool. But it's not real ice, it's ice, it's just the floor of this pool is where all the lines that would normally be hockey. And geez.
Mark Miller (53:51.05)
Yes. It's gonna be okay, Sean. You can cancel your appointment too.
Adam Furmanek (54:01.67)
Yeah, the difference is you use very short stick, which is kind of like a knife long, so really short. And because of that, you can get injuries really easy because you are really hitting on, you know, others, people, hands and whatnot. So that's generally very contact sport. And you can't actually pass the pocky like very far because obviously water is around. So it stops you. But generally it is a team play.
Mark Miller (54:02.476)
Shawn Clabough (54:16.467)
Adam Furmanek (54:31.59)
So you can have strategy and other stuff and apart from that, well, you need to keep your breath underwater. So really good people can play. I mean kids starting like five years old onto adults and there is World Cup that is played. I think every other year or something like that. I think this year it was in Australia. So generally it is a sport you really can do and it's fun. I encourage you once again.
Shawn Clabough (55:01.214)
So it seems like the goalie would need like scuba gear if there is a goalie.
Adam Furmanek (55:08.734)
I mean, they typically swim like on the surface of the pool and they just look down. But yes, sometimes they need to actually go down, but you don't have any like, you know, a bottle with oxygen, nothing like this. You use this regular pipe for snorkeling. So the typical game looks like this. You swim on the surface. You look down what's going on.
And like if you are a forward player or something like this and you realize, okay, now is the time I may go down to get the pocky and start, you know, trying to score. So then you go down, you keep your breath, you stay underwater for something like minute, two minutes, depending for how long you can keep your breath. And then when you realize, oh, that's a little bit too long, you just swim up, take a breath, and hopefully some other team mate is, you know, going to step in and help score.
Shawn Clabough (56:05.002)
probably takes a decent sized pool to do this in too.
Adam Furmanek (56:08.234)
They play the regular pool typically, although I mean regular pool as an Olympic pool, right? But sometimes if the team is small, you can have even smaller pool like half Olympic one and still play. I don't know whether there are rules of how many players are there in each team. I guess they are, because like if you have World Cup obviously you need to have some book with all the code. But generally it's pretty flexible, so you can imagine.
Shawn Clabough (56:36.894)
So my blow up pool probably wouldn't work. All right.
Adam Furmanek (56:41.527)
Mark Miller (56:42.698)
Now when I bring my Shiv hockey stick in there to your blow up pool, Sean.
Jimmy Bogard (56:43.182)
I'm gonna bring my children back into the cave. They're really smart.
Shawn Clabough (56:49.659)
Alright Mark, what's your pick?
Mark Miller (56:51.974)
I recently saw two movies that I both thought were excellent. One of them is Spider-Man Across the Spider-Verse. Really phenomenal animation in this thing. Great script. Great performances. I really enjoyed that. The, I guess one, I'm going to give you one.
Spoiler and I'm only spoiling the very end of the movie So I don't think it's too bad of a spoiler here and that is that it's a part one of part two No one told me that until I got to the end of the movie. I was like what there's a what where's my?
Jimmy Bogard (57:23.119)
that it's a time where the phone keeps going through that. So when it got to the end of the month, I was like, what the fuck? What a shitty phone. Ugh.
Shawn Clabough (57:31.986)
That's what I felt when Dune, when Dune, the more recent Dune came out. It's like, that what?
Mark Miller (57:35.262)
Right, right. Yes. What? No one told me. All right, fine. I got to wait.
Jimmy Bogard (57:40.913)
I had to explain to my kids what a cliffhanger is. I'm sorry kids, this is what is known as a cliffhanger. And we're going to be hanging for a while. I'm sorry.
Mark Miller (57:46.176)
Right, second movie I saw was Flash, which I thought was pretty good. You know, DC Universe.
I don't know the actor's name, but the main character plays himself in two different age versions of himself in the same scene, and he does a great job. Usually, if the performance isn't great, I'm totally aware of it. I was just entertained as heck by this 18-year-old version of the guy talking to...
probably a 20-something, 30-year-old version of himself. And I was buying it. So I thought it was great.
Shawn Clabough (58:32.318)
I heard there was issues with the special effects and things like that. They weren't the greatest, et cetera.
Mark Miller (58:36.522)
Well, you know what, I wasn't, you know, I wasn't, I didn't come in there looking like, you know, saying they had to have the best special effects. And I guess, you know what, I will say my younger son, there was one particular, you know, location that's visited several times that's special effects heavy and he was, he thought that was pretty cheesy.
So he did call that out. I didn't have a problem with it because I was just so impressed with the acting. I thought the actor did a great job, the directing of this actor in these moments. I thought that was great too.
Shawn Clabough (58:59.818)
Shawn Clabough (59:10.042)
Yeah, I think what made Dune really bad about it is they gave you these little clips of scenes and whatever, and you didn't know that was not going to be in this episode. Even if you knew there was another one, you didn't know that they're going to be they're going to be that bad at you. Yeah.
Mark Miller (59:23.534)
That's a class action lawsuit.
Jimmy Bogard (59:25.922)
Mark Miller (59:27.51)
Yeah, the next one though looks great. Have you seen the trailer for the next one? I'm ready for it too, Sean.
Shawn Clabough (59:29.818)
Yeah, yeah, I'm ready, but now I'm prepared. Now I'm prepared, yeah, that there could be a part three, four, five, yeah. All right, Jimmy, do you have a pick for us?
Jimmy Bogard (59:34.646)
We'll see you next time.
Jimmy Bogard (59:44.758)
Yeah, let's see so I was in the Netherlands for the past like week or so for a conference and They had this snack food that I had like so much up because I love that it's called bitter ballin and it's like this Beef stew that's been deep-fried in this encrusted outside and it's I must say like 50 of those things I don't know how much but it's like
It was like a Dutch hot pocket. So like the outside is crispy and the inside is like lava freaking hot. So you have to like dip it in cold mustard to like try to even it out. So I went to a few tech talks, but the thing I remember the most from the conference was all those deep fried balls that I ate over the week. Bitcher ballin'. Yes, bitcher ballin', yeah.
Shawn Clabough (01:00:26.716)
It was called Bitcher-Bolland? Is that what you said? Bitcher-Bolland. Yeah, okay. So, my pick this week, another streaming show. So, I like to watch a little, a lot of things. I've been watching The Wheel of Time.
So Amazon, you know, streaming show Wheel of Time, it's a fantasy world thing where only certain people have access to magic and most of them are like women and things like that. So they just finished bringing out the last episode of season two. So I found it very interesting. If you like fantasy, magic and things like that, and you have Amazon Prime, you can watch it there.
All right, Jimmy, thanks for coming on the show. If our listeners have questions, what's the best way to get in touch with you? I think you're pretty active on most social media.
Jimmy Bogard (01:01:14.601)
Jimmy Bogard (01:01:24.442)
Yeah, I guess Twitter slash X, whatever it's called these days, my DMs are open there so you can always ping me there. Or even LinkedIn, if you want to hit me up on LinkedIn, I am checking that more now that Twitter has gone down to such a, such lows. So that's probably the easiest way to get in touch with me is one of those two social media platforms, yeah.
Shawn Clabough (01:01:38.047)
Shawn Clabough (01:01:46.318)
Okay, great. And if our listeners want to get touch with us on the show, they can reach out to me. I am on X and I'm on threads. Also.NET Superhero is what you can reach me at there on both of those. So thanks everybody. And we'll catch you on the next episode of Adventures in.NET.
Shawn Clabough (01:02:11.226)
I'm going to click leave and then see what happens.
Jimmy Bogard (01:02:17.258)
Oh, you left.
Adam Furmanek (01:02:18.862)
Yeah, it's... And now I hit stop.
Mark Miller (01:02:19.062)
Yeah, we're doing a test.