 On today's Visual Studio Toolbox, Phil explores NET Framework Core's change tracking events. Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green, and with me today is Phil Jepixi. Hello, Phil. Hey, Robert. We're well into our NET Framework Core series, nearing the end of it. Kicking with gas. Today, we're going to talk about change tracking events. I'm interested in this one because I didn't necessarily know what that was when you suggested we talk about it. Yeah. One of the things that was nice in EF6 that really didn't make it forward into EF Core was there is an on-materialized event, and the on-materialized event would fire when an object was reconstituted from the database into a class. Now, why that matters was, for example, for MVVM, and actually, the next show we're going to talk about is how they fixed the problem in MVVM the right way for MVVM, and of course, we don't need it anymore. But the change tracking events do two things now. It will raise an event when something where its state has changed, its entity state, so maybe it's gone from added to not modified, or not modified to edited, and it'll also fire an event when something is added into the change tracker. Now, why this matters is if you want to set up some logging. There's a couple of ways that you can do change logging. You can certainly do database triggers, and a lot of people do that, and we could argue for where I have a whole bunch of shows and get different people on to say whether they love or hate triggers. There's no middle ground with triggers. But if you don't want to use triggers, here's a way in code that you can accomplish the same thing. We've done this for a lot of our financial customers. We'll have, for example, a customer's table and then a customer audits table. At any time a change is made to a customer record, we automatically write to the customer audit table. They can go back to any point in time and see this data as it changes. Does that make sense? Yeah. Okay. Let's look at code. We've got two events that we can hook into with an application DB context or a derived DB context. The tracker, the track changed and the state changed. So the track change is fired when something goes from not being tracked to being tracked. If it comes in from a link statement or some sort of EF query whether that's from SQL interpolated, from SQL raw, regular link, then it's going to come in with the from query property set to true. Let me remove the breakpoint for now so you can see my highlighting. That will be set to true so then we can say it came from the database. Otherwise, it's going to indicate that it came from code. In other words, I said, blog B equals new blog context.blogs.addB. I haven't called save changes but I added into the change tracker. Do you use that a lot? It would seem like you already know where it came from in your code because you just wrote the code to get it in there. So I don't use it a lot. Sometimes there are some edge cases where I used it and it was more of working with the context holistically. If I've added a blog but I've also pulled four blogs back, so now I have these five blogs in there and I've edited two of them, I've deleted another one and I go to call save. The one was from source that I know it's brand new. The other one was from queries and I know it was changed. No, I don't use it a ton. I'm blanking right now. It was two different edge cases where we used it, but it's there. The state changed is a little more convoluted and what I am doing is looking at the properties on E. So there's really just the old state and the new state. So I'm looking at the old state and the new state to determine the action and I think the easiest way to show this is to step through some code. So let's do this. Let's just run this. I'm going to create a new blog. I'm going to add it into the change tracker. I'm adding it into the DB set which will then add it to the change tracker and then I'm going to call save changes and let's just watch how these things trigger. Okay. It's rebuilding a database right now. I won't bother showing the command window because it just is a command window. So the old state was before change was made, new state is after change was made and every entity has a pair of those in the change tracker. Right. So I've just added the blog and it was not from a query. It was from code. So I'm going to output that the entry ski minutes blog was added from code. Okay. All right. So let me run. All right. So now we have also triggered, that screen was jumping, these state changed. So if we look at states. Now again, this is after the add or before the save changes? Correct. Here we do. We have added to the DB sets but we haven't called save changes and we have then output that the blog was added from code. Right. All right. Let me do a continue. So tracked fires before changed. And you'll notice that change didn't fire at all. Oh, okay. Because this state never changed. Got it. Okay. All right. So now I've added the blog. So maybe this is part of where the fuzzy was these come in. The change tracker is tracking states. And then after the event, right, it's no longer the same state as the change tracker that fired it. It's the after effect, right? Because it's going to fire after I call save changes. So I've just called save changes. So here's my end. The state doesn't change when you add something to the DB context? It comes in as added. It doesn't come in as unmodified. It actually hasn't changed. It's been assigned. Oh, okay. Got it. So the initial setting is not a change. It's just an initialization. Yep. Good to know. Okay. All right. So my entry is a blog. Yep. So now I'm going to write that. So what's the old state? What's the new state? Show us those right now. The Schematics blog was added, old state, before the state was changed to unchanged. Okay. So when you added it, it started out as added. And then you saved it, and it is now unchanged. So the state was never, got it. Okay. We didn't do any editing, right? So we didn't modify it. We didn't try and delete it. Got it. So what we can do, then, is set up, and this is not all of the code, of course, because you've got to pay me for that. But the skeleton of the- You're not paying me for this much either, huh? Yeah, you're not paying me for this either. Plus, I said that so you don't get all the code. Now you've got to hire me. We've got the old state, so I'm checking the new state. So the new state is unchanged, which means it has been persisted. Mm-hmm. So it's unchanged. So now I'm going to get the old state. So there's the action. It was added. So in my logging table, I can then grab the values of the blog record as it is now and write it and say it was added. So it's important that this pattern, you're going to be logging the data after it's been changed. Okay. Right, because if you- Well, it doesn't make sense to log it before it's been changed, because if you don't have a blog, what are you going to log, right? Right. But I could then go in call code to say, okay, save to the logging table, the blog status, and then now I'm going to have a running history of all of the changes and I can put in there as one of the fields in the audit table, and this is what we do, is what the action was. Was it deleted, was it edited? And then we dump out the entire record and then you can use SQL server and reporting services and all sort of BI tools to do analysis of it. Okay. All right, so this is something that we use fairly often. Let me close my console window. There's something else that was added in that has the same name as EF6 code and the name does work because it truly is an interceptor, but it doesn't work the same way EF6 interceptors worked. So in EF core, I can do a command interceptor, a connection interceptor or a transaction interceptor. And what is an interceptor? So an interceptor is going to sit between EF core and well between your code and EF core and when it's listening, right? So it's going to intercept in this example, there's a transaction starting, I'm starting a transaction. So it's going to intercept that call. You can do stuff with it and then it'll let it continue through. So if you think of the old commerce pipeline analogy, you've worked with commerce server or any of those, right? So I've got a pipeline, right? So I've got this transaction that started. So it starts in EF core, goes to database, comes back, right? So it's interceptor, I'm gonna spoke. I said it's between your code and EF core. In this case, it's between EF core and the database provider, right? So it's going to start this transaction, I intercept, I can then do things to it. So I have access to the event data. So the connection ID, whether or not it's async, what's the isolation level is, some of these are get only, some of them are get set. The transaction ID, I do have access to the context so I can actually look at what's in there in the train tracker. So you have full access to stuff. I'm gonna be completely transparent in that I haven't found, I'm not saying they don't exist. I'm saying I haven't found good real world uses for these yet. The command interceptor, and the reason that I say I haven't really found a good use for it yet is I've got command created, command creating, right? I guess there's something, right? The command and executing failed, I won't log that, but I have logging all over my apps anyway. What I don't have access to that I wish I did, and this is me just going back to the EF6 days, is something that says give me the objects as they're coming back before they're fully realized and available to my app, I might wanna do something with it. And this is me thinking of the iNotify property change issue that we had with WPF and EF6 that was resolved by doing an interceptor. But there's a bunch of them, the way you plug them in. And again, hopefully readers put in comments if you're using these, where are you using them? Cause that'd be great, I just haven't found a use yet and maybe I haven't looked hard enough. But again, this goes back to the dynamic configuration capabilities within EF core. So I'm saying use SQL server and oh, by the way, I wanna add these interceptors. Okay. So those are interceptors. Cool. So it's a lot of power to look at. I don't have a real business case for it yet, but hopefully somebody will chime in on the comments and fill us both in. All right, awesome. All right, good stuff. Well, guys enjoyed that and we will see you next time on Visual Studio Toolbox. See you soon. Thanks.