 On today's Visual Studio Toolbox, it's part two of our Entity Framework Core Series. Phil Jepixi shows change tracking and also explains an error we introduced in part one when we changed property names in the scaffolded code. Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green, and joining me is Phil Jepixi. Hey, Phil. Hey, Robert. This is part two of our multi-part series on using Entity Framework Core and in part one, we started with an existing SQL Server Database, and then Phil ran some scaffolding code. It was one line of code which created classes for each of the tables and then took account of all of the relationships and all of the underlying goodness that was inside the database. Today, we're going to actually now do stuff with that database, and we're going to see how we can start working with the actual data in an application. Is that a pretty good summary? It's pretty good summary. Perfect. All right. We talked about dependency injection briefly on the last episode, and I want to just show it in action of how we configure a DB context. The DB context itself doesn't know its connection string, doesn't have any knowledge of what provider to use. We have to tell it that. Again, the DB context is the connection between your app and the actual database, essentially. The DB context is, I don't want to use the word connection because it's overloaded here. The DB context is the master control program of EF Core. It's the general contractor. It's the thing that you hired in the project of working with the data. Correct. The DB context we have to configure. I like that one. You feel free to use it in future training courses. The DB context factory has to be configured. We have to give it a connection string. We also have to tell what database provider to use. In these examples, we're using SQL Server, but there's also an in-memory database, which you can use for things like testing. You can set up Oracle, even Cosmos DB provider. Very simply, I have created a context factory. I'm using it to create in code. It's really designed for the command line interface. The one line of code that we ran from the command line to create all these objects, when we do migrations going the other way, this is how we tell entity framework core what to connect to. It's really only meant to be used in design, but we're using it in code for simplicity. But to show you how it works, we have a context options builder. We assign a connection string, and we tell it what database provider we want to use. We're going to use SQL Server, passing any connection string, and then we'll cover these in greater detail in later episodes. But what I'm doing here is enabling a retry logic. If the Internet's glitchy, it will retry up to five times, and then I create a new instance of the DB context. Now, here's a question before we move on. Go back to that. In real world, how do you handle things like connection strings? Obviously, you shouldn't be putting this type of information in your source code, and then checking it into your repository. Correct. What do you like to use? Let me just clarify that a little bit. I'm okay checking this into my repository because this is not a production connection string, it's only used in development because it's the design time factory. With ASP.NET Core, there's a very rich configuration ecosystem where based on the environment you're running in, development staging, production, made up names, whatever, it'll load a different configuration file. For example, the app settings.production.json file that would have all the production values in it, we don't ever have access to with developers. That's handled by the IT pros. All they have to do is put that file on the server, we never see it, and it's not checked into source control. Now, that's one way to do it. You can use Azure Key Vault, there's Azure Active Directory, there's various other ways to get that production information into your app. But from a entity framework core perspective, it never sees the production connection string. Prior versions of entity framework, you had to have it in the app.config file, which when every developer had access to it, as well as source control. Does that answer your question? We did an episode on Azure Key Vault a while ago. If you're doing a desktop app, WPF or UWP or something, then that would be a recommendation you would, from you? Yeah, absolutely. When we do WPF, we use WPF for child floor automation systems. Then we use EF core with WPF as our data access layer. The connection string is actually coming from a service and being injected into the application, so it's not in the config file. Okay, perfect. Thanks. Right. Again, I'm changing how time factory is supposed to be used. I just want to clarify that because somebody is going to watch us and say, it's only for design and you're right. It's for design and demo. Down here, I have new context factory create DB context. It just gives me one. It is a factory, so I'm using it. But again, I just want to stress, it's meant for design time and not for production like I'm doing. We should be injecting an information in, but I wanted to keep everything simple. What I want to talk about now in detail is to change tracker. This is really the business value that I see the most from using an ORM in your application. If we're not using an ORM and we're pulling data back, which we can do, we're at store procedures, you can use commands, connections, data adapters. Now you have data in your application, and somebody works with that data. But now we have to figure out how to persist those changes. So I have to track in my application what fields changed, what records were added, what records were deleted, and I'm doing all of that plumbing manually. Then when I want to save that, I have to write all that SQL very specific for those fields, for those objects to make sure I'm saving the right data. Is it complex? No, not at all. We can all do that. Is it a pain in the butt? Absolutely. So it's plumbing. It's plumbing. It's time consuming. So let's talk about the change tracker. When we pull something from the database, and we'll talk about simple queries, probably in a later episode, depending on how far we get, we are loading it into the DB set as we explored on the last episode. When we pull it into the DB set, it gets added to the change tracker. What the change tracker then does is basically wrap a proxy around that and say, okay, I'm going to track this particular instance. So if you change a value, it will then know what the current value is of that object, but it also knows what the original value was. So what was the value when it got loaded into the DB set? If you add a new object, it will then say, hey, this object has been added. If you delete it, it's been removed, and it tracks all that for you. This is where the DB context comes back into play as the master control program or the general contractor to use your analogy. So if I use your analogy, then I have to say, and that's Robert Green's analogy every time. No, I give you permission to use it unattributed. Oh, okay. So it's a nice open source site. It's your payment for being on the show. So the general contractor then says, you want to, as a developer, we call save changes and we'll talk about the save changes method in later episodes. But save changes is the trigger point that says to the DB context, let's figure out what's going on and let's persist this data. So the change tracker then reports to the DB context, here are all the objects I'm tracking, here are the ones that have changed, and here's what's changed on them. So then the DB context takes this information and talks to the database provider in our example to SQL server provider and then creates the SQL necessary to persist that data to the database. Now that's all done for us automatically. So now we have all these changes wrapped up in a SQL call to save changes method by default is transactional. So I add three records, I delete two others and I change six, and it can be across any table within the DB context, I call save changes. What happens is a transaction gets opened, the SQL gets executed to do all those things that we've asked it to do. It checks to make sure that everything worked. If anything failed, then it will roll back the entire transaction, and if everything worked, it will commit to transaction, and then any server-side properties such as row version or ID, if it's a sequence, will then get populated on those objects in the change tracker in the DB sets, and now we have an updated set of values and our in-memory objects matching those database table records is updated to match. So there's a lot of heavy lifting going on that we don't have to think about. Right. Does that make sense? Yeah. Okay. So if we look at just some very simple examples of how the change tracker works. So if I create a new instance of a person record, and I'm not creating all the fields, it doesn't matter, and then I can check the state of that object, and we can run this, but I'm not going to waste the time running it. What it will come back is, actually, you know what? This is easy to run, but it will come back and say, the entity state, let's run this one. Yeah, let's run this. All right. Yeah, we can run this. This is easy enough to run. So let me put a breakpoint here, and then run this, and I'm calling the run samples. All right. So I have this new person. I'm getting this state of this. This is an interesting syntax because we haven't added it to a DB set yet, but I can say context, which is my DB context, entry, and it gets me to enter the entry, even though it's not in the actual DB set at this time. So if we want to, this is because Robert had me change the names. That's right. Awesome sauce. You didn't change it to navigation, right? No, I didn't. All right. So all this fall, you didn't change it everywhere? Well, I copied some code in as the problem. You can change it back if you want. No, I see what the problem is. This is actually good. Now that I see what it's doing, we can run with this error. Because what we did was, we changed the names, we broke it, and I think this would be important to point out because other people are going to run into this if they follow along with the video. Okay. So I'm okay with, I know how to fix this. Yeah, let's run this. Okay. And interestingly enough, easy for me to say, we're going to run into a problem here, but we'll explain what that problem is and it's because you had me change the names in the last episode, but it's an important one to fix. Your code doesn't work, it's my fault. It absolutely, right? But I can use the general contract to determine unless you're just going to take that back for me. No way. So we're going to run into an issue when we try and read this entry. And the exception that we're getting says the inverse property attribute is invalid. Now what happened was, and I should have picked up on this at the time, we were in the person object, and Robert and I both agreed that we didn't like the naming convention and we changed business entity to business entity navigation, and we use business entity contacts instead of business entity contact. Right. Because business entity contact or customer email address is a collection, we said those should be pluraled. Right. Or pluralized. Perfect. Here's the biggest problem, one of the biggest problems with the way to scaffolding works. These inverse properties have to be strings, but the problem is that their magic strings in this point and we're not using the name of attribute. So if we go to the business entity, we have to figure out which one it is. So you know what I'm going to do? I'm going to change them back, and I'm going to show you how that you will want to fix them in the future because I don't want to take the time on the video, but instead of doing like here, they use the name of for the property. All of these should be the name of. So this should be name of business entity dot business entity navigation or business entity dot person. The reason they don't do that is because of the name conflict here, which we talked about in the last episode, but I'm going to revert it so we can get the samples working. But I do want to point out that when you change the names, it has to be turtles all the way down. Otherwise, you're going to run into that problem when it tries to build that model. Does that make sense? Like we're referring to properties on other objects, but we changed this name. So I'd have to go through and everywhere there's a business entity property referred to as a string and have to change it to business entity navigation. That would just be a lot of work that, yes, it's important to do, but not for this show. All right. So let's run it. Now that we've reverted, yeah, we've broken all kinds of other things. Awesome. This is the scaffolding code. Yeah, this is scaffolding code because I used the built-in rename function. Yeah. But when I just renamed them back, I didn't use the built-in rename function. Okay. So that's a good learn. If you're going to change the code, the scaffolding built, be careful. Yeah. I recommend cleaning it up if you're going to continue using the scaffolding. But what a lot of people will do is they'll start with the scaffolding and then move to more of a code-centric approach from there moving forward. Let's make sure that we haven't broken it again. Here is the advantage of having the scaffolding in place. I can go back to my command prompt here, and I can nuke everything that I have and redo it again. Okay. So I actually have to do dash-force. Maybe it's dash-force. So we have just overwritten all of that screw-in-with-the-rename stuff that we did in the last episode. It seemed like such a good idea at the time. It is a good idea, just not when you have a 20-minute episode. Okay. All right. So now we're going to get the entity entry, which is what an object in the DB context land is referred to, and we're going to get this state. If I just run this to the end, the first state is detached. It's not in a DB set. We haven't added it in. Then when we add it in, and let me just get to the code to match up here. So here we have our new object. It's not in a DB set. It is in a detached state. Okay. When we do a simple query and we say, get me the data from the database, and then I can line these up like this. So if I pull one back, it's unchanged. If I add it, it's added. If I remove it, it's deleted. Then if I change a property, it's modified. Then when I change a property and I get a modified result, I can actually go through and on that entity entry, I can get the properties that are modified and then display the original property value as well as the current value. So this change tracking does it a couple of things. The goodness is as we've discussed, it's keeping track of everything that we want to have changed. The badness is that it's keeping track of everything that we want to have changed. What I mean by that is if you have an item that has 200 properties, and we can discuss whether or not that's a good idea to have a class of 200 properties, but what life happens. Now you start changing those. Well, now we have two copies of each of the properties that have been changed in memory. If you have thousands of these in memory, then we can argue whether or not you should have thousands of things in memory, and I would say you should. But now we are adding some memory pressure. So we can argue whether or not you should have a thousand items in memory, and I would say you shouldn't. But again, life happens. But now we can possibly be creating memory pressure. That memory pressure is because we have all this change tracking going on. So I want to point out that we can turn off change tracking when we do a query. So let's go back to where I am getting a simple query. I'm doing a find which we'll talk about probably in the next episode, but let's do this a little differently, and we'll say var person to equals my instance of the DB context, my DB set which is called person, and I'm going to say where x lambda x. And this is just a link. Business entity ID equals five, and then I'm going to say as no tracking. Then I can actually also do first or default. I'm not going to do that, it doesn't matter for this demo. What this query here does, if I would make it a comparison and not an assignment, this then says get this thing back, but don't add it to the change tracker. We're not going to update it. So it's in your DB set, but the change tracker says I don't care what you do. I am never going to report this back to the DB context you have changed it. Okay. So that'd be something you might do if you're pulling down records to display in a list or something, or if it's just read-only data. So if you have a really high volume website, let's say it's an MVC style application, what I see people doing is that on all of the gets, they will pull the data back as no tracking because you can't update from a get, you're just displaying it on the page. Then on the post, they'll actually, and on all of the gets, they're only displaying the data and actually do an update. So in the get methods, HTTP get version of an action method, they'll use as no tracking to pull back with no tracking information. Then on the post where they're actually updating the data, they'll use entity framework core with the change tracker to get those updates. Now we're not talking about a huge amount of performance differences. But if you're a Stack Overflow, for example, when you're getting thousands of requests per hour, maybe even per minute, then it matters. If this is your internal line of business app where you're processing legal cases with your 50-person lock firm, it's probably not going to make that much of a difference. Okay. There's one other thing I want to talk about before we close off this episode, and that are query types or items that do not have a primary key. So in prior versions of EF Core, we had a DB query type. What we have now is a specialized DB set where we tell it and it doesn't have a primary key, and we don't have to call as no tracking on it, it automatically knows not to add it to the change tracker. This is useful for views, for store procedures or for, as we'll see in a later episode, doing a from SQL call to pullback data where the link may be difficult to write, but you want to do these joins. All right. So I think it's a good place to stop on this episode. Absolutely. In the next episode, we will start querying the data. Yeah. Yeah. So next episode, we'll talk about querying and about projections about how to do joins, filtering, paging, sorting, all that fun and excitement. Cool. So we will see you next time on Visual Studio Toolbox.