Soft Deletes: The Upgrade Your EF Core Needs
The Bongo Cat VS Code extension adds an animated coding companion to the status bar.
Transcript 5 topics
Soft deletes overview
0:00Have you ever had someone come to you and say, hey, look, I I deleted this. Can I get it back? And you're like, then no. You deleted it. That's kind of delete.
That's what it means. They're like, really need that data. So now you're looking at a database restore. That's no way to live. And that's where soft deletes could help you.
Soft deletes are are usually obtained by creating another field on your table. Maybe it's a a timestamp or maybe it's just a boolean that flags that that record's been deleted even though it's technically not deleted. But one of the problems with that is that means you have to go update all your reads from the database to say, hey, filter out all those that have that as a boolean true or have a value in that field. And if you're using Entity Framework Core, by default, dot remove is just gonna delete the record. So how can we do soft deletes in Entity Framework Core?
You came to the right video. I'm gonna show you how. Let's get to the code. We'll open up the terminal. We'll do dot net new console.
And then, see, we need to add Entity Framework Core, and we'll add Entity Framework Core dot in memory since we're just gonna use the in memory database for this. See, it's Microsoft dot Entity Framework Core. Get that in there. Then we'll dot net, add package Microsoft entity framework core dot in memory or memory. Learn how to spell.
There we go. Go ahead and just clear the terminal. Close it down. And then let's open up that program CS. Look.
It's just what we expect. To start off, let's just go ahead and create a basic class and then a DB context, and then we'll come in and finish this program CS to do all the testing we're gonna wanna do. So I'll go ahead and create a new one, say, let's call it a subscriber class. And we'll just do public class subscriber. And what does it have?
It's got a public int ID that is just get set, I guess. We'll do string name maybe. That sounds normal. And then we'll leave it at that. And then finally, let's go create a since it's a subscriber, we'll call it a YouTube DB context.c s.
And then luckily, I have a DB context already saved. Pretty straightforward. It's just gonna create a DB set, call subscribers, use in memory database, and we're done with that. So let's go over to the program CS. Now I'm gonna delete out what it's already got in there, and I'm pasting some code that we'll walk through.
We're just gonna create a DB context. We're gonna create two new subscribers, Kelly and Jacob, and then we're gonna add them to the DB context.
Building the subscriber model
2:51So now we should have two records in there. We're same same page? Okay. Then we're gonna go get the first one. That would be Kelly.
Remove, and then save those changes. So now we should have one record with Jacob. And then finally, we're gonna go get all the subscribers out of that DB context. And for each those, just write their name and a Boolean of whether they're deleted or not. Then we'll go get them all again and foreach them all again and write the same thing.
The difference is we're gonna use this ignore query filters extension method. We'll get into more about what that does a little bit later. But before we run it, I've actually referenced a property that doesn't exist yet. If we look, they're on line 22 and line 32. We have deleted at, and you can tell it's nullable because we're using has value.
So what is that deleted at? Well, what it's gonna be is a daytime offset that is nullable that we'll use as a flag to know whether that record is deleted or not. So when we say delete this record or remove, what we wanna do is update that field to UTC now. Right? And if it's not deleted, that should be null.
So to do that and to tell Entity Framework how we're gonna do it, let's go create an interface for soft deletes. We can hack. We can call it isoft delete. And what is it gonna do? It's gonna have a public date time offset That's nullable.
We'll call deleted at. And then you know what I'm gonna do? Oh, we need to save that, don't we? Okay. Let's save it as isoft delete.
That makes sense. And then I'm just gonna copy that line, and we'll paste it onto our subscriber and then tell it, hey. You implement isoft delete. Yeah. You do.
So now our our code should run. Let's go try it out. . Netrun. And what do we get? There we go.
We got Jacob is deleted false, and Jacob is deleted false. So Kelly really did get deleted, but that's because we haven't told the Entity Framework how we wanna handle removes. Let's pause for a minute and talk about interceptors in Entity Framework Core. Entity Framework Core has this nice built in capability where you can hook into certain events and override them to do your own kind of logic.
Intercepting SaveChanges
5:20One we're gonna hook into is an override of the save save changes. Learn to use your words. Put your teeth in, bud. The save changes method. We'll override that and then do some custom processing and then continue on.
So let's go write that interceptor. Now close that down, and then let's create something new. We'll say soft delete interceptor dot c s. And I'm gonna just paste it in, we'll go through it as well. So on saving changes, we're gonna say if the context is null, get out of this.
And then for each entity that has been modified, if that entity state is deleted and if that entity implements isoft delete. So you can have entities that are not soft deleted and some that are. But if it is deleted and it implements isoft delete, then change the state to modified rather than deleted, and then update that items deleted at to be UTC now. And then continue on, which will basically save the changes to the database. So now we need to let Entity Framework Core know, specifically our DB context know, then we wanna use that interceptor.
So we're gonna come back over there on the on configuring. And after using memory database, we'll say add interceptors. And then we'll say new soft delete interceptor, if I'll spell it right. There we go. And that should be all we need.
Let's go dot net run again. Hopefully, we still have our Kelly record. Yeah. We do. So undeleted records, filtered, but we didn't really say.
We said give us all the records. Right? So we still got Kelly, but you notice her deleted is true. And then unfiltered, same deal.
Both of our records types are the same. So now we wanna tell Entity Framework Core that we always want you to add this additional where statement to your SQL or whatever in whatever database provider you're using.
Adding query filters
7:20So that's a query filter. Let's go add one real quick. We'll come into the on model creating. Luckily, I have one saved to my my clipboard because I'm prepared. And all it's gonna do is say, hey, anytime you use that entity of subscriber, add a query filter where the deleted at value is false.
So now anytime I run, you know, context dot subscribers dot to list or dot where or dot anything else, find any of it, it's gonna automatically append this statement to that and filter them out. So if I went and said contact subscribers, find using Kelly's ID, I wouldn't get her back because this filter's also applied. So let's see what happens when we run this. If we go dot net run, I'm gonna leave the other results up there just so we can see them for comparison before the query filter and after. Oh, look.
Our undeleted or unfiltered or filtered subscribers only brings back Jacob because it's applying that query filter to only bring me back the undeleted records. But the unfiltered is bringing me back both, and you'll notice that Kelly's is deleted is true. Why is that? It's because of that code we said we'd come back to. Here we are.
We're back to it. It's this is ignore query filters method. That's basically saying, hey. I'm gonna send you a query right now. And you know those query filters I may or may not have registered?
Forget about them. They never happened for this query. Don't even apply them. Just give me what I'm asking for specifically right here. Does that make sense?
Hopefully, does because I can't say it much clearer. Well, how appropriate that that's where I screw up. Anyway, so that's why our record sets are different. So anytime I'm querying the data, just normally using subscribers dot where or find or whatever, it's gonna filter out those soft deleted records.
And then if I use ignore query filters, it's gonna include it. Now this is really great if you're trying to implement some kind of functionality where you want a trash can or recycle bin kind of thing where you have an undelete or an undo process where you can delete something, but, hey, we could use an ignore query filters and show you what's in your trash can and allow you to undelete those things.
Recycle bin recovery
9:28Pretty handy. Right? Anyway, have you had ever had to use soft deletes in your applications? I've had a few clients where it was a necessity, like, for legal reasons. Well, let me know how it worked for you.
What processes did you use? Was it something similar to this? And, I'll catch you next time.
Sign in to join in. Reading needs nothing.