Deep Dive With Entity Framework Core 5 | Trevoir Williams | Skillshare
Search

Playback Speed


  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Deep Dive With Entity Framework Core 5

teacher avatar Trevoir Williams, Jamaican Software Engineer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Introduction

      3:29

    • 2.

      Setup Development Environment

      3:53

    • 3.

      Setting up the Solution

      3:59

    • 4.

      Creating the Data Models with EF Core

      8:11

    • 5.

      Specifying the Data Provider and Connection String

      9:18

    • 6.

      Migrations and Database Creation

      14:33

    • 7.

      Generate Migration Scripts

      2:10

    • 8.

      Reverse Engineer Existing Database

      10:24

    • 9.

      View Diagram with Entity Framework Core Tools

      4:18

    • 10.

      Adding Verbose Logging to EF Core’s Workload

      5:02

    • 11.

      Simple Insert Operations

      20:33

    • 12.

      Simple Select Operations

      7:51

    • 13.

      Filtering Records

      12:51

    • 14.

      Additional Execution Methods

      10:26

    • 15.

      Alternative LINQ Syntax

      6:16

    • 16.

      Simple Update Query

      13:33

    • 17.

      Simple Delete Query

      8:23

    • 18.

      Tracking Vs. No Tracking

      7:53

    • 19.

      Review One-to-Many Relationships

      7:44

    • 20.

      Adding Many-To-Many Relationships

      17:26

    • 21.

      Adding One-To-One Relationships

      6:35

    • 22.

      Generate New Entity Diagram

      1:37

    • 23.

      Inserting Related Data

      10:52

    • 24.

      (Eager Loading) Including Related Data

      20:53

    • 25.

      Projections and Anonymous Data Types

      13:50

    • 26.

      Filtering on Related Records

      3:17

    • 27.

      Adding Views and Other Data Objects

      5:31

    • 28.

      Querying Keyless Entities (Like Views)

      6:02

    • 29.

      Querying with Raw SQL

      8:02

    • 30.

      Add and Query Using Stored Procedures

      4:47

    • 31.

      Executing Non-Query Raw SQL

      4:51

    • 32.

      Seeding Data

      12:16

    • 33.

      Rolling Back Migrations

      7:28

    • 34.

      Manipulate Entries Before Saving Changes

      14:28

    • 35.

      Extending DbContext

      7:04

    • 36.

      Implement Full Database Auditing

      25:18

    • 37.

      UPDATE: Implement Full Database Auditing - Fix

      3:26

    • 38.

      Data Validation with Data Annotations

      10:25

    • 39.

      Fully Using Configuration Files

      4:36

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

74

Students

--

Project

About This Class

Overview

In this course, Deep Dive With Entity Framework Core 5, you will learn to work with data in your .NET applications.

Most times when course are created for .NET technologies, the details of Entity Framework and it's sheer power are neglected. We get distracted with abstractions and layers and don't focus on what Entity Framework is doing and can do.

In this course, we will review the general benefits of using Entity Framework Core 5, which is Microsoft’s flagship Object Relational Mapper (ORM), to relieve you of many concerns and challenges that come with this component of software development. We will also spend time discovering how to EF Core translates classes and references to Database Models and Relationships.

We will learn how to write queries, update databases incrementally, rollback changes and explore the myriad capabilities that Entity Framework Core affords us.

When you’re finished with this course, you’ll have the skills and knowledge of Entity Framework Core needed to fluidly interact with data and write quires for for .NET Core applications with ease.

By the end of watching this course, you'll be able to:

  • Construct a data model using code-first and database-first workflows

  • Understand Entity Framework Commands

  • Use migrations to manage database changes

  • Apply Database validations and constraints

  • Perform CRUD operations using LINQ

  • Apply best practices with Entity Framework

  • Extending Data Contexts

  • Understand how Change Tracking works.   

  • Manage Database Structure using Fluent API

  • Handle One-To-One, One-To-Many and Many-To-Many Relationships

PREREQUISITES

In order to take this course, you should have at least 3 months experience programming in C#. If you need to strengthen your C# fundamentals, you can take my C# beginner course C# Console and Windows Forms Development with LINQ & ADO.NET

Content and Overview

To take this course, you will need to have some knowledge of C#. Even if you do not have much exposure to the .NET development stack, this course is very beginner friendly and chock full of development tips.

This premium course is smartly broken up to highlight a set of related activities based on each module in the application that is being built. We will also look at troubleshooting and debugging errors as we go along; implementing best practices; writing efficient logic and understanding why developers do things the way they do. Your knowledge will grow, step by step, throughout the course and you will be challenged to be the best you can be.

Meet Your Teacher

Teacher Profile Image

Trevoir Williams

Jamaican Software Engineer

Teacher
Level: Intermediate

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction: Welcome to this new course Entity Framework Core, a full Tour. My name is Trevor Williams, and I am a software engineer and lecturer with over ten years experience in developing applications, as well as teaching people how to do this. In this course, you're going to learn about Entity Framework Core, and this is Microsoft's flagship Data Access framework or ORM for dot t. Now in this course, we're going to go through a few things that will set you above and beyond your peers and your colleagues in terms of how you interact with your databases while building.net applications. We're going to explore entity framework and how it helps you to build applications faster in.net, as well as how it helps you to manage database creation and changes using simply C sharp syntax. That's right. Using entity Framework Core. You don't necessarily have to go into SQL or going to the management studio to manipulate your database, and by the end of this course, you're going to have a good understanding of how all of this is managed through migrations, the database context, and database models. You're going to understand how to write link queries to interact with your database objects and tables, as well as explore many other advanced features of entity Framework core. Now, by the end of this course, you're supposed to be comfortable with entity framework and how it allows you to write link queries, and interact with your data. A little history on entity framework. It has been in development since 2008 in DottFramework, but it has evolved and evolved and evolved and right now we're at D five or Dt COR five. And so the library, which is currently in use in a stable state is EF Core five, which is what we will be exploring. However, soon there will be EFC six, and most of what we'll be learning here will be transferable knowledge. Now, a little bit more about what EF COR is. It's an object relational mapper. I used the acronym ORM earlier. That's what it stands for. Object relational mapper. So it's an open source library with APIs that make it easy for you to interact with the database. While we'll be using SQL server in this course, a lot of FCRs capabilities can be extended to other databases such as post resca and cuaide and my ca. So there are a lot of benefits to come from learning C because it increases your productivity. It helps you to reduce repeating code, and it allows you to focus on other things in your application while keeping the code based nice and consistent throughout. It is also enterprise ready. That's right. If you're building a big application and you're worried about having too many queries and having mesic code, FCR is perfect for you because it's nice, it's compact. It allows you to keep the syntax in C sharp, and it is stable enough to work in extreme conditions. I know you're looking at this course and wondering, is this course really for me? Well, if you want to learn about FCR, you're at the right place. If you know some C sharp and you have some database knowledge, you are at the right place. If you want to learn modern ways of interacting with your database using technology, then you're definitely in the right place. At the end of this course, you'll have no weaknesses and there are no threats. You can only get better and better. So what are you waiting for? I'm happy to have you in this course, and I'll see you in the next lecture. 2. Setup Development Environment: Hey, guys, welcome back. So in this lesson, I'm just going to walk you through what you need to set up your environment for this course. So I'm already here on the Visual Studio website, you can get there by saying sutudo.microsoft.com. And what we're going to be using is the Visual Studio IDE community edition. No, this is the flagship IDE given to us by Microsoft for.net development. So it is perfect for this course. However, if you're not able to use that community edition, on a Windows machine and you're using a MAC, then they have the MAC version. If you're not using a MAC, then you can use Visual Studio code, which is also open source completely free and available across every one of the operating system. Now, things I will be doing in this course will differ if you're using Visual Studio code, but I'll do my best to make notes along the way to try and make sure that you are able to carry out the operations. Now, after downloading Visual Studio, you'll be given well, at least downloading the installer, after launching this installer, you'll be given a screen similar to this. Now, I already have it on my machine, and what you're seeing are the different workloads that I have already installed. You don't need this many for this particular course. If you already have the ones that I'm about to show you, then you can go ahead and skip this step. But if you are here, and you need to know what to get. You need this.net cross platform development workload, which gives us the actual you know, the basic libraries for.net core development. And we'll be doing a little web activity. So it would be a good idea to just get the AP net and web development workload also. So at the bare minimum, you need those two. The more you click, of course, the more that will be installed, so you don't have to click as many as you see me having ticked here. So AP net and web development as well as.net cross platform development. So you can go ahead and install those Now, you need to complete this step, especially if you're not using the Visual Studio and a Windows machine, because if you're going to be using Visual Studio code, then you need to install the SDK in the background. That comes with Visual Studio, right? But then if you're not using Visual Studio once again, you can go ahead and download that SDK, so you can just get here by going to Microsoft co. And from that landing page, you'll be able to navigate the.t5 downloads page where you're going to be getting that SDK. You can also go ahead and get the run times because we need them for hosting, but at the very least you need that SDK. Okay. Now, after everything has been installed and set up, you just want to make sure that everything is installed. So you can go to your command prompt, whichever OS are on, and then you can just type.net H. All right? So when you do that, you will bring up if you're seeing a printo to the screen looking something like this, then you're on the right path. So it will tell you the SDK version that you have, and it will just show you all of the different commands that you can use. So you can actually use these commands when creating a new.net core application, and especially once again, if you're using Visual Studio code, then you're definitely going to be using the.net commands a bit more. All right. So that's really it for setting up the environment. This is a five minute video, but it will take probably a few hours or a few more minutes rather than this if you are setting up from scratch, but it won't take too long. So as soon as you're done and you can verify that you have everything installed, then you can move on to the next lesson. Okay. 3. Setting up the Solution: Hey, guys, welcome back in this lesson, we're going to start setting up our solution for the project or for this course. And what we're going to do is start off with a blank solution. So I'm going to go ahead and click Create New Project, and I'm just going to search for the blank solution template. And that is the one that we're going to select. So go ahead, hit next. And I'm going to call the solution named entity framework net five, right, indicative of the course that we're doing. And have my location set I'll go ahead and click create. And just like that, we have Visual Studio with our blank solution file. What we're going to do is start off with adding a new project to the solution. So I'm going to add new project, and then I'm going to be adding a console application because we're going to be using a console app to go through the fundamentals of entity framework core. Right? Before we get into all of the excitement of web and this and that, I want us to understand what FCR can do, because if we understand it from a console app, then the possibilities are limitless when we have to build more complicated applications. So we're going to go ahead, click Console Application, hit Next, and we're going to call this one entity framework net five console app. So go ahead, hit next again. We're targeting t five as our target framework, and then go ahead and create. Okay. Right now that our console app is created. We're going to create two more projects, and these are really going to be class libraries. Now, in enterprise development, one of the fundamental principles is separation of concerns, meaning we have the application, which is going to probably be doing pulling the strings, talking to the user. But then we need other projects where we're going to be storing database related objects and database related configurations are separated. So that's why we're adding two more projects in the form of class library. So I have class library to the left here. You can always scroll, but you can do search if you need to and always remember that we're dealing with C sharp. All right? So for the class library, we're going to call this first one entity framework net five dot data. And then it's still the target framework is still t five, and then we hit Create. And last, but by no means least, we're going to go ahead and add one more class library, and this one is going to be dot domain. So the same naming convention that we have entity framework net five dot domain, right? And it is still a.t5 target framework, and then we go ahead and add. So that is really it for our solution setup. So once again, we're going to be operating with three projects, at least right now, three different projects. This one, the console app listing of it as the UI, you can see here it as an output type of executable. So it knows that it should execute or it should do something. That's so do core configuration or project files are set up because if you look at this one different from the class library, you'll see that this one only has the target framework. This one knows it's target framework, and the fact that it's an executable. That's only material difference between them two, right? So this one will represent where we are going to be writing out our practical code, but then we'll be setting up our domain and data objects inside here. Well, domain will have our data models for classes, and then data will have our data configurations and files. So when we get we'll start some fun and start modeling some of the tables that we will be using for this course. 4. Creating the Data Models with EF Core: You guys welcome back. So in this lesson, we're going to start modeling our database tables. Now, what I have on screen is a database diagram or ERD entity relationship diagram. It goes by a few names, and there are a few representations of it. But the fundamental concept is that it is a diagram showing the different entities or tables inside of a database and what the primary key column is, what the other fields are, and then depicting that there is what we call a foreign key relationship between two tables. Here, it is showing that we have teams and we have leagues. Those are two tables. The league has an ID, which we're going to make an auto incrementing integer and it has a name. And then a team also has an ID, also as a name, but then it has a league ID. This league ID is what we call a foreign key to the league. A team belongs to a league. That's the relationship. If you're familiar with database, then you already know what's going on. If you're not so familiar, then that's no problem. I hope you understand at least the concept behind this diagram. All right. What we want to do though is set up tables like this inside of EFC. We want to use FCO to model out a database that will generate these tables. So you have different conventions when it comes to developing on top of a database. So we can do it database first, meaning somebody went in designed the database and everything and then we start building the application afterwards, or you have code first, which even though it implies that the code comes first, what it really means is that we are going to write the code that is then turned into the database. That way, if we need to change something down the line, we actually just change the code, and then we can let that make incremental changes to set database. And EFC supports either one. That is the beauty of it. So for the majority of this course, we're going to be looking at code first and how we can make marginal changes or incremental changes as we go along modifying the code. But we will look at how to reverse engineer an existing database into an EFC set of files, right? So enough talk, let's get into that. So my data models or domain models are going to end up going in the domain project, right? So I'm going to get rid of this default class that we got with it, and I'm going to go ahead and add a new class, and I'm going to call this class league. Now, generally speaking, I always advise my students, whenever you're creating a database, try to start off with the ones with the fewest dependencies and then go on because you don't want to build a table that has two or three foreign keys relating to other tables and those tables don't exist yet. Since in this situation, League is a standalone table, meaning league is there. A league is going to be there at least right now without any teams, but the team depends on the league. I want league to exist first. Create that class, I'm going to make it public so that other projects can access it. And then inside of this, I'm going to have the two properties, ID and name. So we're going to say public int ID, public string name. You see here, especially if you're familiar with the data types in SQL, you'll see that we're not seeing Var char here. We're saying string. FCR will be able to translate the native C sharp of what data types. Into the equivalent SQL data types. We'll see that later on. That is one of the benefits. We can use our native C sharp inside of our FCO application, and the translation to the database will happen anonymous to us. Another important thing to point out is the naming conventions and the support that you get from FCO when you follow certain conventions. Generally speaking, an ID column is going to be called ID. Sometimes you might qualify and say league ID or the table name ID. Now, COR will identify either one of these conventions, and it will automatically infer that this is intended to be your primary key and auto incrementing QL identity column, right? So it'll automatically see that naming convention and tell SQL that this is the primary key column. So if you wear off the path of that general naming convention, then you're going to have to do additional configurations to get that to work. So to me, it's easier to just work with FCR than to force F cord on another pass. It's not the hardest thing to do, but why give yourself more work when it can be that easy. Just by saying ID, you will know that this is the primary key and fit up everything else for me. Now, I'm going to create the next table or the next class represent the next table, which is team. I'm just adding that to the domain project. Team is the name, and then team once again, public will also have ID and name. As we saw, but then it has a third column, which is the league ID. That represents a foreign key. Now, what we're going to do when we have a foreign key, we have to add two properties. One, being the table name or the name of the table that this is a foreign key to and ID. Once again, naming convention. Second would be a navigation property and most times you'll see them making this virtual. Let just add that. Public virtual league league being data type of the class that we just created with the same name. That's fine. But the combination of these will leave F to infer that this matching the table name and the word ID means that this is a foreign key to that table. All right. So if you've had to set up a foreign key in SQL server, you know that you have to go in and you have to choose the columns, and you have to do this, and you have to do that. And there are a few steps. This is all it takes. Follow the naming convention and have that navigation property. And FCO will just say, Okay, I know that this is a foreign key, and I know that I can automatically include or automatically present the related details to this record through this property. Later on, we'll see it at one to show too much information. But I'm just trying to show you that from the get go, once you follow these fundamental naming conventions, FCR is going to do a lot of the work for you. Okay. So that's actually it for now. We have created the classes that are supposed to represent the tables in the database. Of course, the columnames match the data types are conceptually the same as what we would expect them to be in the database. And later on when we come back, we're going to look at how we set up the DB context, the provider and the actual connection to the database because none of that exists right now. There's no database. We're just modeling it out and saying, Well, these represent what I want to store. So when we come back, we're going to see how we actually set up the link to the database. Okay. 5. Specifying the Data Provider and Connection String: Our guys were back and the objective of this lesson is to set up our data class so that it knows that it needs to connect to our database. All right. So our data class, we're going to have to add a few libraries to this to let it actually interact with that net framework. Sorry, with entity framework. Because right now, nobody knows anything. We're just talking about entity framework. It's named entity framework, but it doesn't know anything about entity framework. So let me just start off by deleting this default class, and I'm going to jump into the new gate packages and let us go to Bros and we're going to be searching for entity Framework core SQL server. So before I go ahead and install that at the jump, let me just explain. You have Microsoft entity Framework Core. This is the base library for everybody. I think every the variation has a dependency on this one. If you look at it, it's a modern object based mapper for.it supports link queries, change tracking, and it works with a number of databases, CQL server, Azure, CQight postres and others. What you realize is that from UGT, you can actually get different libraries to support the different databases. In our case, we're using Microsoft k server. But if I was to search for entity framework core and just look through the different variations we see our k server, we see in memory, we see CQight. You see Postgres, you're going to see MCL. I'm not going to go through all of them, but the point is that whatever database it is that you have under the hood, there is more than likely a library to support it and you can get that from New gt. All right. As I said, today we're only working with SQL server, so let us go ahead and install that one, so I'm just going to download it. And in doing so, it's going to show you all of the other dependencies that it has, right? But that's fine. We can just go ahead and click Okay, accept any license agreements and let you get installed it in our project. So now that it is installed in our project. If I look back in the CSP file, just by clicking the project file for the data for the data project. You'll see here that now we have that package reference installed. Alternatively, you could have also gone ahead and put that line similar to this. It could say node package reference, include the name of the library, the version that you're interested in, and then just to build and it will automatically go and get the dependencies for you. So it could have done that also, but let us proceed using newG. So I can close all of that off. And the next thing on the agenda would be to create what we call the DB context. So I want to create another class and then I'm going to call this class football league football league context or football league DB context. We tend to put that in, DB context just means that it is the context or the connection. Let's just say context equals connection. DB context means DB connection file, so to speak. And then this is just letting you know what database it is. It's not a naming convention per se. That's how I am doing it. I'm just explaining to you why I'm naming it like that. So I'm going to make this public public class DB context, and then every DB context is going to inherit from the default DB context coming from EF core. So you have different DB contexts, you have DB context, you have identity DB context if you want to user authentication. But for now, we'll just stick to the basic DB context, control and dot and then that will let me know that I need a using statement for entity framework. Core. Now that is satisfied. I can tell it about the tables. Remember, this is going to represent the connection to the database. So whatever is in the database needs to be represented here. We have the models that represent the tables in the database. Now we have the link to the database, we need to let this link know about the tables. So I want to say public DB set. And this DB set DB set just means that the set of rows or records are really a table in the database. You are modeled of team All right. And your name is teams. So you can kind of read it like that just say understand exactly what you're typing as you go along. No, I need to have a dependency. I have to add a reference to the domain project here. And so I'm just going to you could click that, but I've had problems with that before, so I'm just going to go ahead and do it manually click dependencies, add project reference, and then add the domain project, click, and then we can just go ahead and use our using statements. Then I'm just going to duplicate that line and do the same thing for a league the DB set for league, we're calling it leagues in the database. I have a DB set for team objects, we're calling that teams. This, once again, will generate or interact with a table called teams, and anything that is in the teams table will be serialized or converted to native team class that we have defined. The next thing I want to do is actually let it know about a database, and we need to do what we call a connection string for that. So connection strings generally, like in a web application, they would be passed on, so you have the connection string in the web application and it's passed down through configuration settings into the context. We don't have that luxury right now, so we'll just do it manually, so I'm going to write override. And then the thing is that the DB context, which is our EFC class comes with a number of functions that we can actually overwrite and do our own thing with. But the one I'm interested in is configuring. This means whenever you're configuring the DB context, what do you want me to do? I'm going to override it. I don't need to override anything on the base or interact with the base, but I am going to tell it that the options builder UCL server. You see that is courtesy of our dot server, FCR library, right? So UeQL server. And we're going to put in our connection string right here. Because this is a demo app in the early stages, we're doing all of this. But of course, I'm walking you through it so you understand why we're doing it this way. So a connection string comprises a few parts. We're going to say, data source is equal to, and then we would specify the server. So I'll be using local DB, which is a local server built into Visual Studio, and you can access local DB databases via the SQL server object explorer, which you can go to view, and there it is. All right. Local D B, backslash, backslash, and then we're going to say SQL local D B. Get that spelling right. Just take some time and get it just like how you see it here, even with the parentheses and everything, the double backslash and all of that. Next up, we have the initial catalog and this is basically the name of the database. What are we calling the database? I'm going to call it Football League underscore FCR. That is our connection string. All right. And with that, we have actually completed setting up the data provider and setting up the link to the database. Now, of course, this database does not yet exist, right? So I've set up a link to something that's not there pretty much. And I've said these tables need to exist in set database, which, like we just said doesn't exist. So I'm just going to look in the SGO server explorer real quickly just to show you that it really does not exist in there. I have a few test databases, and as you can see, it is not there. So when we come back, what we're going to be doing using FCO to generate this database so that we can start interacting with it. 6. Migrations and Database Creation: Hey, guys, welcome back. So in the last lesson we had set up our DV context, which, like I said, is our connection to the database. In this lesson, we're going to start looking at migrations, which are our instructions to the database. Our first migration will be to create the database because a migration will always say, what existed, what exists now? What is different? Let me give instructions to make those changes. At this point, there is nothing. So what is there now is nothing, and then the instructions will generate what should be there. Right? So in order to do our migrations, we need another tool from our new gate packages, and that library is literally tools, right? We need FC two. Go over to New Gate, I'm going to go ahead and hit tools and download that one and that automatically comes with design and some other libraries, but I'm just going to go ahead and let that install. After installing that, I'm going to have to set a project reference between the console and our data project because when we run a command to update the database, we have to have it run against the startup project or the main project, and the main project needs to know about the other projects with, you know, the connection and the DB context and so on. So I'm just going to go ahead and add a project reference to console up for both of these because we we'll need to be able to access the domain objects. We also need to be able to access the domain the DB context, right? So I'll just go ahead and add that And then know that all of that is done, we can run a migration. To run migrations using Visual Studio, we want to go to the package manager console. So you can go to tools, you get package manager and you see the package manager console. I have mine available to me already down here in this panel, and of course, you can move the panels all up. Let me just redo this one. Now, before we start the migrations, I just want us to take a look at the extent of the options available to us. In this package manager console, I can say get hip and help and then type in entity framework, and then give that a few seconds, and then it will generate a nice document showing us all of what is possible through the entity framework. It starts off with a unicorn and it tells us about the library, It gives us all the command lets and what they're used for. The same list that I just pointed out in GT, it's available here with documentation. So like I said, we'll be adding a migration, and then we'll have to update the database after every migration. I mentioned earlier that you have code first versus database first. So if you're doing code first, well, that's the add migration update database. If you're doing database first, then you would want to sca FO DB context, which basically says, I'm looking at the database and generating the class models based on what I see in the database. So you see that they have quite a few options available. You can script the DV context, Scripto the migrations. If you're still old fashioned to generate the migration, you want SQL script. You can do all of that using these tools. So let us go ahead. I'm just going to clear this CLS, clear that console, and then I'm going to know add a migration. So the default project, once again, needs to be the executing project in the solution. And I'm going to say add migration. And then I'm going to give it a name. So the name, typically, you want to use a sensible enough name that you or somebody else. Well, let's start with somebody else. Somebody else can come along and say, Okay, that is what that was a general idea of that migration. You also want to use a sensible name because after you go on vacation and come back, you want to look back at this migration and understand why it was done and what it was for. So don't name the migration X or just something silly, give it some meaning. So like there's nothing here. I have no migrations, no database. Yet. I want to call this one initial migration. I also like to camel case my names. When you use spaces, then you have to take extra precautions when you have to, you know, reuse the names and so on. So I just use camel casing. You can use underscores, whatever, but I just don't like spaces inside of these names. So I'm going to go ahead add that migration. It's going to build the project, and the build is successful, but I have an error. So let's read this error. And in this course, I'm not going to shy away from errors because I know that a lot of people hate the errors that they get in EFC. Sometimes they're not very clear, and it's hard to troubleshoot some of these errors. So I'm not going to shy away from errors. If anything, I'll deliberately conjure some errors so that we can go through them together. But this one is just it's simply saying that the startup project, which is the console app, doesn't reference the design project, right? So, in other words, I need to install a package in the console app. So an easy way to get that done. Of course, you can go to New gate, but I'm going to try and do it through the project file. I'm going to create a new item group and give it a package reference to include microsoft dot entity framework core design. And I'm just setting the version that I know I'm using through the rest project. This version may vary based on when you're doing the course. So if you're taking this path, then that's fine. If not, you can always go to New gate and add the entity framework design package like we've done for all the other packages. Next very important step is to make sure that we have our data project selected. I just did see a list to clear the noise and the errors in the section, but I just want to point out that we need the default project here to be whatever project you have the DB context in, for us, that's data. I'm going to go ahead select data, and then I'm going to just press up arch which brings me about the add migration initial migration command. Go ahead and try that again and this time, we have our migration created. Now, let us take a look at what we got here. We got our folder called migrations, and it has at least one file in there with the name of the migration that we gave it. Notice there's a timestamp on it. We also have this other one that is the context snapshot. So basically, at this point in time, this is what the database will look like, and we have our migration file. Let us take some time to appreciate exactly what is in a migration file. So typically in SQL, you would have written SQL statements, create table, create, well, sorry, create database, then create table. Then you put in all the fields and the constraints. If you were writing a script. If you were using the management studio, you would have been clicking and all that would have been generated in the background. Well, a migration file is pretty much going to be a representation of all of those actions, but with C sharp context and built on top of what we called the builder pattern, right? So if you just take a look at it, it looks complicated, but just sit back, take a deep breath, and look at it as a developer, you'll notice that everything it is saying to you makes sense, right? The migration builder, that's an object being passed in here, create table. Name off the table, leaks, right? And then it's automatically going to make sure that it creates. So even though in the DV context, I had suggested, sorry, I'm just trying to say a lot here. I had suggested initially that when we're creating the class models, we start with the ones with the least dependency. Because if we started with team and team would have a dependency on this navigational property for league and league didn't exist then, this would have been a little error. So it would have been prudent of us to create league. Then league is already there by the time we're creating things that need league. Okay. Now, in the DB context, I didn't necessarily follow that order. I just basically randomly listed them. So I didn't necessarily list them in the order that I wanted them to be created because obviously, I would have wanted leagues to be created before teams. However, if you look in the migration, it already went through and saw that leagues has no foreign keys, Teams does. So leagues has to be created before teams does. That's pretty much what FCR just did. So it's creating the league table. It's giving it the columns ID and name. And if you look closely once again, following name conventions, ID is automatically going to be an int, not nullable, and it is a CQL server identity auto incrementing. All of that was generated because we used the word or the name ID for this column. FOR inferred that this is what we wanted as our primary key. And then you'll see here where it's saying the column type is string. That's a C sharp type, but then in the database, we're making it NTR. All right? Just let that soak in a little. Then we go ahead and add the constraints that actually make this the primary key, which is I the ID column. Then it repeats those actions. Migration builder, create table, create teams, create the columns, once again, inferring what the primary key is, and then adding league ID as an int, But then it goes a step further with the constraints where it creates the primary key and it creates the foreign key, which is linking the column league ID to the principal table leagues and the principal column ID. So you see all of that is inferred when we use our proper naming conventions. There might be times when you have to, you know, go off course, and that's understandable, but don't let it be a case where you're always going to be doing your own thing and fighting against QR, and then you end up doing twice as much work when QoR is more than happy to do all the work for you. So after creating all of that, it goes ahead and creates an index on that foreign key column. Now you'll also notice that there are two Sorry about that, there are two methods inside of this migration file. We have up and we have done up pretty much means when I am updating the database or upgrading the database, this is what I want to be done. D means that if I am to undo this migration from the database, this is what should be done. D just like up is opposite of D, the code that is in up is going to be doing something entirely contrary to the code that is in do because there are times when you make a database change, and then you say, Oh, That's not really what I wanted to do, then you make a change. But then when you want to make that change, sometimes it's easier said than done. So at least FCR is saying, well, I am I am telling the database what to do when the migration is being done, and I'm also going to tell you what not to do or what to do when this migration is being undone. So all of that is automated within this file. Okay. All right, so now that we have the migration. We're going to finish off this lesson by running the most magical and probably the most important command of them all, which is to update the database. So up until now, we still don't have our database. We go ahead and type update hyphen database. Press Enter, let it build. And what that command does is it looks at the migrations that it probably knows about and then looks at the migrations it doesn't know about, and then just picks up from that point and executes the migrations going forward. Here you see it say applying migration, and then it's telling me which migration it applied if there were five would have applied five, but there's only one, so it's letting me know it has done it successfully. So when I look back in my database list, I will now see the database by the name that I had defined in that connection string, football league EF core. When I expand the tables, I'm going to see EF migration history, which is basically a table in the database, keeping track of the migration. So you can tell up to which version your database is. Right? And then you have leagues and we have teams. And if you look, you see keys, Okay, so that is the primary key. And you see here that League ID is a foreign key. All of that was set up in the database for us courtesy of this migration. Now, in the next lesson, we're going to look at how we could have scripted this migration because you may not necessarily want to run update database and that and let FCR have complete dominion over everything in the database. But there is a way that we could have generated an SCO script based on these instructions, and then you would be able to execute it on your own. So when we come back, we'll look at that. 7. Generate Migration Scripts: You guys, welcome back. So in this lesson, we're going to talk about scripting our migration. So the case study for why you'd want to script as opposed to, you know, generate a migration file and do it from the package manager would be maybe just separation of controls. Maybe there's a database administrator who is in charge of database changes. So you the developer, wouldn't be making these database changes, but you've done the migration against your local. You need to hand it off to him so he can do it in the environment. You know, there are different situations that might determine whether or not you can do it the way that we just did it in the previous video. Or you would have to script the migration and hand it off. Nonetheless, we're going to learn how to script the migration, and it's a very simple procedure. So we have only one migration, so the script won't be the most complicated one. As the script grows, there are particular behaviors that get added accordingly, and the script grows with the different migrations, right? So later on, we can revisit that, but right now, we just want to see how we would script this migration. So in the package manager console, it's pretty simple. You just say script hyphen migration. What it's going to do is the regular build, and then it's going to generate the SQL file that corresponds with all of these commands. There we go. You'll see here that it's checking if the migration history table is null, then go ahead and create that table. Then we start the transaction, we create the tables accordingly. We get the index, we insert into the migration history, the migration that has just been executed, right? That's it. So once again, this is probably going to be done when you want to hand off the responsibility of the database changes or database creation, whatever it is, to somebody else in your team in the organization, and that is what FCO allows. 8. Reverse Engineer Existing Database: Hey, guys, welcome back. So in this lesson, we're going to be looking at how we can reverse engineer a database that exists. So once again, we have code first where we can write the code that generates the database. But in some situations, you might already have a database that you wish to scaffold out into your class models like what we have been doing up until this point. So in this lesson, we'll be looking at that, and you would have taken note that I have already created a new project. So you can do this if you want to follow along, but it's not absolutely required because we're just going to look at the command and see exactly what it does. So I've created this new console up project, which is solely for the purpose of scaffolding the DB, so you can see exactly what happens. So I'm going to make it my start up project, and we're going to go over to the package manager. And as a refresher, I'm going to say get help, and we're going to take a look at the command to scaffold the database. So here's the list of the commands, and we see here that we can say scaffold DB context. So that's the one that we're going to be paying attention to in this lesson. So when we want to scaffold, I'm just going to copy it and paste it as the next command I'm going to run, and we're going to use the same database that we're working on in this entire course, which is our football league database. I'm going to scaffold it into the models, right? So I need to specify the provider. So hyfen provider. So this is a parameter. Provider, and our provider will be microsoft dot tit framework core dot SQL server. All right? So that's the name of our package, and that is our provider. So that just goes to show that this scaffold DB context command could be used for other databases that are not necessarily SQL server. All right. So we specify the provider, and then we're going to have to give it the connection, all the connection, here would be the same connection that we're pretty much using in our B context. So you just have to formulate a connection string. Since I already have one, I'm not going to type it from scratch. I'm just going to copy it over because like I said, we're using the same database. But whatever database it is, you write a connection string. Data source is equal to the server, it's on initial catalog is equal to the name of the database, because it might be on a server somewhere else. It might not necessarily be on the same server. So you just want to take note of the fact that you write that connection string according to where that database is. So I'm going to open quotation marks, put in my connection string. Close quotation marks. So one more thing that we want to do before we try to execute this command. We want to make sure that our project references the same libraries that we had to set up when we were doing our initial scaffolding or our initial migrations, right? So if we take a look back in the data, we see that we had to reference the kill server, and we had to reference the tools and tools references design. All of these are needed. I'm just going to take this entire item group. Copy and then go over to our new project and paste and then do a quick build. And then after that has been successful, we're going to come back to the package manager console. I'm just going to double check that the provider is correct and the connection is correct. I'm going to make sure that the default project or the target project is the project that we want to do the scaffolding to. And then I'm going to press enter. All right. And after pressing Enter, did the building give me a t warning and then a very deadly exception. Now, like I said, I'm not going to shy away from errors in this course. I think seeing the errors and working away through them is very important. So this error is saying that a network related error has occurred when trying to connect to the server. So that means something is wrong with my connection string. And I think I know what it is. It's the double slash because we have to use the double slash in writing the connection string in the literal context here. But then instead of the package manager, I don't need that double slash. I'm going to remove it, and then I'm going to try that again. And this time, I have no error. I got the same warning, but I have no error. And if you take a look behind my package monitor console, and inside the project, you're going to see a bunch of files appearing. All right? So let's take a look at what each one of them represents. So I'm just going to collapse anything that's not absolutely necessary. And then let's take a look at the context. So it generated a context file. The same context file that we created manually, it just to generated it. It gave it the name based on the database. I gave it the inheritance of DB context, it has two constructors, one parameter s, one with the parameter accepting options from somewhere from some other application, that's fine. We have our DB sets both created as virtual, and then we have the same unconfiguring method, right? So if I did a side by side comparison, you kind of see that while mine is much more or always is much more simplified. It's really the same thing, right? We have the unconfiguring, and then it says, if there are no options configured, then it puts in that connection string just like we did. However, it does give us this warning, which is the same warning that we saw in the package manager console. It's just saying that you want to avoid putting your connection string directly inside of the context like this. And like I said, we're doing this provisionally because it's only a console app, so we're just putting it right there just to get it working. All right. But in a bigger application, it would live in another file and be passed on demand. You'll also take note of another method, which we haven't quite looked at yet, but like I said, you can get to override most if not all of the methods that come standard from the DB context class, and this other method is on model creating. So this one is basically just saying that we have the anation the relational collation setting the character sets that we're going to be using for this database, and then it goes on to say model builder dot entity team has index, and it has one league with many teams and has a foreign remember that just by following the naming convention, when we were creating the database, it kind of implied all of this for us or did it in the migration. In this situation, it's looking at an existing database, and then it is trying to formulate the rules in creating this existing database or interacting it with it that need to be adhered to. All right. In the same way if we were to change this database name and then tell you to do a migration, it would actually look at all these rules to know how to do a migration for a brand new database that looks identical to the one it's scaffolded. So it's a very I'm telling you, FCR is very, very intelligent and it's doing a lot. It's pulling a lot of strings in the background for you. All right, so that is what the DB context really looks like. And then if we take a look at our classes that were generated, you see that we get a partial class, we get hash set that represents the collection of team. So I'll get to that in a minute. But we get that construct and we get the same properties. And if we're looking team, it looks virtually just like how we did our own. So that means we're on the right track, right? We still have that foreign key and the navigational property towards the league, which is also virtual. Now, just backtracking to what the league class has, We see here that we have an additional property that we didn't put in our own. So I'm just going to bring up the original league. This is the one we created, right? And then the one that was scaffolded has this additional property, which is a collection of teams. Now, the concept here is that it's one too many. We know that. We created it, and we'll go over one too many later on, but I just want to highlight that what it is doing here is it is saying that one league will have access to many teams. One team, belongs to one league. But a league has many teams. That's the one to many. So with this navigational property set to be a collection or it could have been a list or well, it used collection by default. When we were creating, we could have said list, we could have said I numerable. But the point is that it is the collection of related items to this entity. So just by getting one league, I could include all of the teams, and that would save me the heartache of having to do two separate queries, sorry, to get the teams and the league that they're related to. All right, so it's very powerful stuff doing it like this. So we can later on add that navigational property to our own, but I just wanted to highlight what exactly was being scaffolded and what exactly is being done. And pretty much that's it to scaffold in the database. There are other little things that probably you can do, but at the most basic level, if there's a database, maybe started building sc server, or it's a part of a legacy project or a legacy system and you want to rework it, you just want to start working on it without starting from scratch. Then you can use this command, bring in or that database in the form of these models. And then through the context, you can just start working with it out of the gate. Okay. Okay. 9. View Diagram with Entity Framework Core Tools: Hey, guys, welcome back in this lesson, we're going to take a look at the FCO power tools and how they can help us to visualize our database through Visual Studio. Now, this is coming off the heels of me showing a database diagram earlier where we were in the SQL Server Management Studio and I generated that diagram zero just to depict the database that we were trying to build at the time. You might not necessarily have that luxury, and I'm not saying you need to go and get the management studio, but using the EFC power tools through Visual Studio, you are able to generate a similar diagram and see how FCO visualizes your database or whatever database it is connecting to. So let's get started. If you have Visual Studio, that's good. You just go to extensions, you go to manage extensions. And when that dialogue comes up, you just search for FCO power tools, and when you see in the search results, you just go ahead and hit download. Okay. After that, we'll need to restart Visual Studio, so you can go ahead and do that quickly. Now, after closing Visual Studio, the extension manager is going to come up and finish the installation. If you don't already have this feature DG ML editor installed, which probably was installed with Visual Studio, so you might not have to go through this step. But if you're going through the step, then it's no problem. Just go ahead and hit modify allow it to bring in the dependencies. And once that procedure is done, you can re open Visual Studio and then What we're going to notice is when we right click a project, we're going to see a new menu item in the form of FQ power tools. So you'll see a number of options, some of them looking just like what we've been discussing up until this point with the different migrations and scaffolding and forward engineering, all of those things are actually possible through these FQ power tools without the need to write the commands. However, for this particular lesson, what I want to focus on is adding the context diagram. So of course, once again, we want to make sure we're in a project that has the DB context. We're going to right click, go to FC Power Tools, and then we'll say Add DB context diagram. And then it's going to generate a new file with an extension dot dGML and we're going to get our diagram. So I thought I would be able to select, but it's actually just dragging all around. So you see here, it's giving us this DB context in the form of a diagram, giving us the team, the different properties, and we can click or hover over the property to get a bit more information on it. It's the primary key, and it's not notable. You see all of those annotations or all of the meta data is available right here on hover. And if we hover over the table, it's pretty much the same thing. So from visual studio, we can use this tool to visualize exactly what is happening in our database at any given point. Now, given that we only have two tables right. Now this diagram might not seem as exciting as it probably could be. But you can just take some time to look at it. You can hold on control and scroll to zoom in a bit or you can just change the zoom up top here, and you can take a look at the legend where it points on what the different symbols or the different colors are, you see here, red represents a navigation property. The What's that purple or bluish, I'm sorry, I'm slightly color blind, but this purplish color represents the foreign keys, and then the primary keys highlighted, and then the different properties are. So you can take some time to just take you know, take it in, you can see the different kind of relationships. But as our database grows, we will revisit this and take a look at how FCR infers, what kind of relationships and different annotations on different properties as we go along, and then we can see this diagram evolve with our database. Okay. 10. Adding Verbose Logging to EF Core’s Workload: Hey, guys, welcome back. So in this less, we're going to be setting up FCR with some additional options to spit out more details about what it's doing. So COR is really quite by default, but we wanted to make some noise because when we're executing our commands, I want us to have a visual representation of the Q that's being orchestrated and the different operations being carried out in the background while we're doing our thing. So what we're going to do here is extend the options builder to know that it needs to log to And I'm going to let it log to console that right line. So because we're using a console up it's on the right. So we log to console that right line. And I'm going to add to the configuration or let's say, the pipeline of what it should be logging. I want it to log the DB logger category command name so that we can see a visor representation of what exactly is being done. And I'm going to extend that to let it know that we want a log level. I was going to say a log level. Dot information. And I think I'll have to include they will go include a library for Microsoft dot extensions dot logging. I think I got ahead of myself just now. So it's new DB logger category, and then we close a curly brace here. And then we have the log level information, and then we close the parenthesis there. So take a look at that line. Apologize for that. So we log to console dot right line, new array, and then we're just passing in the command name line right there, DV logger category database command name, and we're letting it know that we want a log level of information. So that means everything it's doing we want to be seeing spit to the console. Another thing I'm going to include is enable sensitive data logging. That enables us to see or it tells FCR that everything that is happening in the background that you probably wouldn't want your front end user to see, we want to see it. It's our app we're learning, so we can at least do this with zero risk, but you wouldn't necessarily want to do that on a production server, but it does help with debugging in certain situations. So now that we have all of this wired up, I'm going to just jump over to the console. You don't necessarily have to do this step. I just kind of wrote some code so that we can start interacting with F course, so you can see exactly what all of that would amount to. But in the next few videos, we will be going through all of this code together. But for now, I just want you to see a sample of the additional logging. So on screen, you'll see the console application. And if you take a close look, you'll see the logs being spit out. So we have info at that time stamp. We did this command, and then it's showing you that we execute a DB command, the parameter was. So what would happen naturally if we didn't enable the sensitive logging is that these parameters, you would not see the values. So enabling sensitive logging, like I said, will show some details that would have been left out for, you know, security reasons, hence the title sensitive logging, right? So we can see exactly what values are being passed in, and the size of the value, the type, the command time out, and then we see the actual SQL being generated insert into that table, the values, and then it's just taking that parameter. So it's not putting that value in directly. So that just goes to show that FCR is actively doing parameterization, which is a key component to the fight against SQL injection as a security loophole that many people try to exploit, right? So it's parameterizing the simple query, and then it just goes ahead and selects the ID from there and updates the scope identity. But right now, I'm not going to get into too many details about what the script is doing. I just want to highlight that we are now in a position that when we're going to be writing our FC commands, we can see actively the SQL that is being generated in the background. We also see the run time this to 61 milliseconds. But there might be times when maybe a query is running too long and you're wondering why you probably need to tweak it. All of those things you can do when you have this kind of logging at your disposal. Okay. 11. Simple Insert Operations: Okay. Hey, guys, welcome back. So in this lesson, we're going to take a look at how we can perform simple insert and select res using EF COR. Now, coming off of our previous lesson where we looked at the verbose logging and adding additional information to the console. You'd have seen that I had this bit of code that we are to explore and try to understand. So the first line that I want to point out to you, and this is once again, the program dot CS file in our console app. All right. So the first line that I want to point out to you is where I'm instantiating our DB context. So I have private static football league DB context. And just in case you're wondering where that name is coming from, that is the name of our DB context here. Remember that I said that this file represents the connection to the database. It has the connection string, and well, it knows about the elements or the entities rather in the database. So this entire class as our database grows our contracts, this class is what gives us that gateway access into the database and allows us to interact with the different entities. So we have to have an object instantiated of it. Now, in a regular dot nic core, well, let me not say regular dot nic core. In a bigger dot nic core application like a web application or even a blazer application, you wouldn't see it being done this way. You would see it being injected in. But once again, we're here to learn about the syntax of F core. So I'm trying not to focus on those other aspects of development in this particular course. All right. So we're instantiating or football league DB context. I'm calling the object context. You would have seen different suggestions given to you, so you can choose whichever one you think is more indicative of what this object needs to represent. But context is almost like the universal word to mean the file that connects me to the database, right? Hence's using it in the name of the class and even the object name. Once again, naming is more subjective. So that's fine if you have other ideas. So we just instantiate it here. And then we move on to the main. So like how we did it in the class, this is global to any other function that we're going to be using inside of this class file. Now, to add something to a table, the syntax would be context. Once again, that object that represents a connection to the database. So we say context, dot. The table we hope to interact with, and this this word leagues is really relating to whatever we called the tables here. All right? So that is leagues. So if I just retype this from scratch, I'm going to say context, dot, I want to see a bunch of options. I can add, I can add a sync. I can do a number of things, but then you'll see the different properties, and some of the properties would include the table names. There's leagues, and if I scroll down a little bit more, there's teams. So as many DB sets as we have defined in the context file, we get to access them directly right here whenever we need to interact with said tables. So context leagues, and then what do I want to do with the league table. In this situation, I want to add. Now, if we take a look at the ad function, you'll see here that the overload is expecting the well, not the overload. The parameter that is expected by the ad function rather is of type league, and it's just saying it expects a league entity. So in other words, it expects some object of the type league. So that's the beauty of F court allows us to stay in C sharp because in Q syntax, we would have had to write something like insert into leagues, and then values and then list out the values and that's if we're not going to specify the columns and then the values. And yeah, that syntax would be it is purposeful, but having to put it into the C sharp and then convert it and then do all of that, FC is just saying stay in C sharp, I have the object context. I get the table you want to add, and then you give me the data you want to add. So I added a football league from Jamaica here, but then we can change this and let us try English Premier League. All right. So new League instantiating an object, and we're passing in the properties, and League really only had ID names. So because ID is already an incrementing primarily, we don't have to provide a value for the ID. We can access it sure, but we don't need to provide a value when we're adding. All right. So let us go ahead and run this command and see what happens. Logs here is showing us that it has successfully executed this command, it took 112 milliseconds and it put in the parameters and everything. And then here is the CQL syntax that was generated. So insert into leagues, then the column names, then the values, and then the value, and then it just updated that object accordingly. Now, another line that I want to point out is line 14, which is the ultimate line that you need to call. No matter what you do, thinking you're interacting with the database. Once you are manipulating data, meaning you're adding or you are updating data or deleting data, you have to call safe changes because all this method does and the like methods for the update and the delete. All they really do is track what changes need to happen, but they're tracking them in memory. Right? So for as long as the application is processing this particular operation, it is just tracking it in memory that, okay, I know I need to add this. I need to update this. I need to delete that. But then until you save changes, save changes actually says, generate the SQL, send to the database and attempt this, and then we'll roll back if anything fails and let the user know what happened. All right? So that's really what the save changes is for. And then we called save changes A sync, but then there is also the save changes without the AC that's fine. I'll use it without the AC since this is not an asynchronous function. I will just keep it simple. But once you're using asynchronous programming, you can always use the A sync version of these methods. Which, of course, have to be placed in an asynchronous method. So I just swapped out all of my methods into the A sync versions, and I change the main function into an A sync task. And then because of that, now I can I have to say a weight add a sync, weight, save change is a sync. Now, another thing, I just want to break this out a bit more. Because right here I am adding the object directly into the parameter. That might not always be the case, because especially in bigger applications, like when a user is submitting the form, then in a web application, they submit the form, you would get all of the data from the form in an object that you need to pass into the database. You wouldn't necessarily want to get it from the form and then have to write new league and then try and put each each property with each value again. So you can always just initialize Subq Sevar League is equal to, and then I can initialize a new instance of league. Let me just do this to cut down any long typing. There we go. And then I can give this one liga. You can tell I'm a football fan, right? So La Liga. So this would be the Spanish league. Now that I have my object that represents the league that I wish to add to the database, I can now say add AC and pass in that object. So it's really that simple. You can build your object elsewhere, then you add it, and then when you save changes, that is when you get it committed to the database. Now, what I want to point out to you also is that once we save changes, this object is going to automatically get updated with its new value. So let me add a breakpoint and run. And what I'm going to do here is initiate a watch window, and I'm putting the league object there so we can track its values as we step line by line. All right? Let me just pin this, make it a little bigger so we can see it. So first line, nothing has happened yet. League is null. Then I'm going to step And then you'd see g has an ID of zero and the value Liga. And then it's going to save the changes, and then we step one more time. And if you look at the object, you'll see it now has the ID value. So this comes in handy when you have operations that are chained, right? Because you might have an operation where you need to add something to the database, and then you need that value, that new ID value in order to maybe display the details of this record on the next page. So I submit a form, you edit to the database, then you bring me to the page to see the data. I just submitted what it's reading from the database. Well, this is where that comes in handy because then I can query the database directly on that record ID right after the operation has completed. I'm just going to press F five to continue with the execution. And once again, we see our very friendly message here saying that it has added that data to the database. No, just for depiction sake, as I said, we want to see some of the errors that we might get. What I'm going to do is try to add an ID value to this object before it gets added to the database. So I know that the ID just now was seven. So I'm going to put in an ID. It really doesn't matter what value because if anything other than zero, when it goes to the add operation, it's going to give an error. So I just wanted to see the kind of feedback that we're going to be getting from EFC, right? So just running it and allowing it to do what it needs to do. And then you see here it is failing, and we're getting this error here. It is saying an error occurred. CQ exception cannot insert explicit value for identity column when identity insert is set off. So you see we're getting literally one of those errors that we would have seen if we tried to do this in SQL directly. If it's not the same error, then it is worded very similarly to it. All right. So that's the kind of feedback that EF COR will give us whenever we try that. And we'll see here in our console log that it just failed. It just tells you failed and insert into that, that just failed. All right? So if I continue, then the execution will end accordingly. All right. So we're winding do, and I just want to do one more example of how entity framework work can make life easier. So in this situation, I want to add a new league, and we're calling this one Syria, that's Italian league, and we add the league like we know we have to do, then we save changes. But then I need information from this league in order to conduct another operation. In this situation, I want to add teams. So obviously, a team needs to exist in a league, right? So I need the information from the league that was just created in order to create these teams. So what I'm going to be doing is passing this league object over to this method. And then notice that we'll have to save changes because every time we're carrying out some operation against the context, we have to save changes in order for it to take effect in the database. So, I already created that method I made a static AC task, and I call it add teams with league ID. Well, I call it with league ID. Let me just take off league Let me take off the ID because I am showing two different operations in this example, and you'll see why, and it's taking a parameter of type league, which is the league. All right. So you can just go ahead and replicate that. And then in this method, I'm trying something different. Instead of one, object to then add, I am doing a list of objects, and then I'm going to add range. So this is now going to take advantage of FCRs ability to do bulk operations. Now, a little tit bit bulk operations, it is a feature that allows FC to kind of batch multiple like add operations and so on into one Q statement and shoot out. But then the team kind of decided that, well, it would be more efficient at smaller numbers of records to just do individual SQL statements. However, at a certain threshold, you'll start seeing one SQL statement with all the information. That's just a little bit about how these bulk operations work in the background. But in this method, what I'm doing is defining a list of teams, and each one, I'm giving its name. So this is vents, and this league ID is the ID coming over from our league object. Right? And then the next team would be a sim following the same format. But then the third one, which is Aroma, I'm doing something different. Instead of using the foreign key, I am now using the actual navigation object and reusing the object that has been passed in. So we're going to see exactly how that works. So, these would be the traditional way. You have the foreign key, you put in the foreign key value, and it's satisfied, right? But then in this team, instead of using the foreign key, I'm using the actual object to pass in. So let's see what this would do. So I'm going to put a break point at the first save changes, and then we can just step through and see the operation step by step. So I've added the watch for the teams also, right? So let me just step through the league hasn't been created as yet. So step use F ten, so I don't have to go over to the context. I know we have our ID of eight. If we look in our league object, we see it updated accordingly. Then I'm going to use F 11 to go into this method where teams is now defined. I'm just going to step through its creation. And then if I look in the list of teams, I see I have all of them with their respective IDs, and then Roma is here with the Actual navigation object instead of the league ID. All right? So league ID for Roma is zero, but the league ID for everybody else is eight as expected. However, the navigation property for everyone else is null, and for Soma, it has its navigation property. Now, let us see what happens after we save changes. I'm just going to press five so we can see the logs that gets spit out. And you see the first insert operation happening right here with the league. Oh, no, that. This one is with the league. Sorry, right? Then we see another one happening for the first team, and we see that the ID, the league ID of eight and the name that's being passed in league ID name, right? And then for the last one, you notice that it's the same SQL statement. It gets the eight, it knows it's Aroma, and it does the same kind of insert. So that's just going to show you that you can put in the actual foreign key value and it will, of course, do what it needs to do with the insert, or you could put in that whole object and it will still infer that well, the object has its primary key. So obviously, this is a related object, so the foreign key would be the primary key offset object and whatever other data. So FCR is doing that for you in the background. I just wanted to highlight the different ways that you can insert a record that has a foreign key dependency. Now, there are a few other things that you can do, but sometimes they only come with experience, but at least if you understand the basics of how to add something to the database. And notice I had to add here, save changes and then add again and save changes because I needed this to be added for the key to be generated so that I could carry out this operation. All right? On the flip side, if I had done something like this and just take the time to look at it. I just kind of reworked it so I didn't have to sit and watch me type. But what I'm doing is still initializing a new league, and then I have a team This new team has a name Bern Munich, and then I'm passing in this league object. Now note, I'm not doing a I'm not saving changes in between these two lines, right? So first league, fresh team, and it's getting that fresh league object. And then all I'm doing is adding the team and saving changes. And notice the difference between this ad and the previous ad ACC lines. You have the option, of course, to highlight the table that you wish to interact with. So if I said context dot leagues add a sync, and then passed in a team object, it's going to be an automatic error. Why? Because once again, the ad operation requires a type of league, right? So I can't be passing in a team object. I could of course have said that teams and everything would be okay. But then if I didn't specify the table, entity framework would automatically know that, okay, this is a team object or it's a list of team objects like we did down here, where I just said context dot add range passed in the list of team. So it knows that clearly it's going to look for the corresponding table for that data type. So you don't necessarily have to at least when dealing with the context, you don't have to specify the table all the time. All right. But what I want to point out is that when I do this and then do one save changes, it's actually going to create the dependent property and then automatically create the team with that foreign key. So let's take a look at that operation. So when you look at what it generated, you'll see that it executed the command to create the league first. There it is creating the Bundesliga, and then it gets that ID, and then it goes ahead and executes the one to create the team passing in the new foreign key into that new team. SCEF CR is just pulling all of these strings for you. All because I told it, I want a new team, and this team is a part of a league that I don't know the ID of. So it said, no problem. I have this. It goes ahead, creates the league, gets the ID, creates the team, and then just lets you know everything is done. So if you took a look at that team object, you'd see all the data relating to the team and the league. So when we come back, we'll start looking at doing some select queries because up until now, we've only been putting in data, putting in data, putting in data. Now, let's look at how we can read the data from the database and display it in our application. Okay. 12. Simple Select Operations: Guys in this lesson, we're going to be talking about simple select operations. Now, before I move on, I just want to point out that we did quite a bit from the simple insert stuff, and I have extracted them into individual methods. So I've got rid of the code, and I've kind of commented them so that when you look back at it, you can see what is happening where. All right? So I just have them commented on because we don't want to keep on adding and adding and adding the same old data. So now it's time for us to look at selecting, so I'm just going to part them, put them to the side, and then we can continue to work around them. All right. So when we talk about selecting, this would be the R in CRD. So I don't know if I use that acronym prior to speaking on this topic, but CRD create read update delete, right? That's an acronym that is widely used in database development, and it represents basically the four operations that you'll always carry out on a database in any application. You're creating data, which is what we just looked at when we look at inserting, that's creating you are going to read data, meaning you want to retrieve the data that is in the database. That's what we'll be doing now, and then later on we'll look at the U and the D, which stand for update and delete. Now, when we want to retrieve data from our database, we start thinking about formulating what we call link queries or link statements. Link, which is short for language integrated query. It's basically a dialect. I call it a dialect of C sharp. It's a way that allows you to write a query using C sharp, and then of course, FC is going to translate that into QL. Let's get started. The first and simplest thing that you can do. The simplest link query to retrieve data from the database would be and I'm just going to define a variable. So I want all the leagues to come back, and I'm going to say Regus is equal to context, which is connection to the database table I'm interested in, which is leagues. And this is telling it that look in the DB set of leagues. But then I want it in a list. So I'm going to say dot to list. Then it's going to ask me to include a certain library. I'm just going to control do you see it's telling me that I need the library system dot link in order to do this. I'm just going to go ahead and include that, that error goes away. And just like that, I have told it, select star from table called Leagues. That's pretty much it. Right? Then what's going to happen is that context creates connection to the database. It goes to the league table and then this list says, extract the data, bring it back in a tula form, but then materialize them into a list of league objects. Just the same way that we had created the list of teams, where is it, here's my list of teams. The same way we created the list of teams, it's the same way it's going to materialize into the list of leagues for us. So what I'm going to do is do a for each, and I'm going to say for each league in leagues. I want to console dot right line. So I'm just hyphen using a hyphen to separate the ID from the name. All right. So let us take a look at that. All right. And when we look at the output, we see here the statement that was executed, select ID and name from leagues as L, right? So that's basically that SQL statement, which you could just copy from the console, go over to your management studio and execute. So that comes in really handy when you're troubleshooting, maybe you wrote a query and you're not getting back the results you want. You can always get that SQL statement and try decrypt why this cle is being malformed. Once again, that's a very powerful tool. However, when we take a look at the objects being printed out, we see everything that is in the database. So while testing, we probably entered the Earth strap Premier League a few, too many times, also the Premier League, but then we see all the other leagues as well. So that is what we get when we just simply say context, table name, give me them as a list. Now, some important things to note when it comes to the syntax, this is what we call the executing statement. So without this, that query wouldn't be run. This would just say, Well, legs is now just the hash set or the DB set off the table called legs, right? So when we put on the to list, that's when it actually says, I will go and execute that query and enumerate them and send them back as objects. So if we were supposed to do something like leave off the to list and then still execute this. This query would not get executed until it actually starts the four each loop. So it would just be like in a state of stasis here. And then when we start to four each through, then it would say, Okay, okay, let me go and get them so I can enumerate through. Now, the danger of doing it this way is that the connection will remain open for the duration of this four each loop. Now, with only maybe 15 records, that doesn't seem like much of a big deal. I mean, okay, fine. But then when you're going through a bigger database, you have that connection open. And let's just think of every connection to the database as an expensive operation in any system, right? So you want to reduce that as much as possible. And you also don't want a connection to be open for too long or longer than it needs to be. So when we do the statement like this and we could even get rid of that leagues part of it and just say for each VR league in context leagues. The point is that this would actually kind of create a lock and a very inefficient one at that. As a matter of fact, the more operations you have doing in that for reach, the time between going through each item increases, and then of course, the connection stays open, and it becomes even more expensive. So I'm just pointing that out, yes, you might try this and it works, but it is not the most efficient way to do it. The most efficient way and the smartest way to do this, once again, would be to Put on that executing operation, let it iterate through and get the list, and then that list is no stored in memory. That connection is no close to database, and then you can do all of your operations and manipulations against the data afterward. All right. So that's really it for doing the simple select queries. I've kind of extracted all of that code and put it in this method, which is a static void method. So this one doesn't use any asynchronous operations. So we don't need to make it an async task, right? So it's a simple void. And what I've done is to highlight which one is good and which one is pretty much bad. So not everything that works, even though it works, it has some underlying ramifications that you might not be aware of, but that's why I'm here to kind of point out what is the best way to kind of gobble your operations. Okay. 13. Filtering Records: Hey, guys, welcome back in this lesson, we'll start looking at how we can filter our queries. Now, the case study for filtering is obvious, right? What we've looked at in the simple select is that we are selecting all and then we're iterating through all. There are situations where you don't want all, you want specific records, and that's what we'll be looking at first. So I've created a method query filters. And before I move forward, I want to highlight that I did say that we made this one void because nothing synchronous was happening in this method. Well, thanks to entity Framework Core, we do have to list A sync. And when I control that, I just have to add that using statement for entity Framework Core. And then thanks to that, I can now make this synchronous. As well as add the weight before the AC call. So let us move on to our query filters. So when we want to add a query filter, and I'm going to say leagues is equal to or context, dot, the table we're interested in, which is leagues, then dot, and I have access to a number of methods on this side. So if I wanted to say, give me all the leagues where the name is equal to some value, then I can say dot, where and then this where function takes a parameter that looks like a predicate or it is a predicate or a Lambda expression. So the format for a Lambda expression is you have some token. I'm going to call it Q. Most examples on the Internet, you'll see them use Q. However, there is no stipulation as to what this token must be. Of course, you treat it like a variable name. So if you want to use Q, if you want to use league, you know, whatever it is. So if I said Q and then it's followed by this Lambda arrow, Then I can now use Q to represent any one record in the table. So that's the format of Lambda expression. If I used x's the same thing. If I wrote out the word league I'm just highlighting that it doesn't really matter what you call it, they all will function just the same way. Maybe this one would read better because once again, it's saying database, give me the table call leagues and give me the records where any one league has a name that is equivalent to some value. If we wanted to find say Siri, then that's what that would look like. Once again, this token could have been Q, it could have been x, it could have been y, it could have been Z. So it doesn't really matter what you use as that token. Now, if I do this, it will not execute until, once again, we end with our to list. Since we have the A sync version of the to list, we can say to list A sync, and then I'm going to await that and of course, make the method an A sync task. Then after doing all of that, I'm going to repeat this operation where I'm just going to print them all out to the console. I await this function call and of course, and I wait goes before everything else. I'm just going to fix these things before I move forward. So when we call that, it's going to go ahead look in the table where that condition is met. So this is going to be true or false, so it's either meeting the condition or not. If it has met the condition, it will be added to the list and eventually returned here. So let us run this query and see what we get, right? And then we see it is coming back. We have our one record coming back. If we take a look at the SQ well, we see it's just a simple select query, but it adds on that were clause with that condition. All right. Now, I hard coded. Notice that unlike the other times when you saw the parameters, it actually just put the actual value inside of the query, and that's because it was kind of hard coded. So I did mention that parameterization is good protection against CQL injection. So the reason FCR has not used parameterization in this situation is that it sees that I am the one who hard coded the value from the code. So I wouldn't put in a SQL injection directly into my own code. So of course, like, okay, it's safe because my master is the one who did this, so it must be fine, right? But then the reality is that in normal situations, you'd probably be getting whatever it is you're searching for from somewhere else, right? So let us get a bit creative here. So I'm going to tweak this function a bit, and I'm going to say console that right line interleague name. So I'm prompting the user this time. So once again, this is a console app but in a web application or so on. Usually, you allow the user to depict or determine what it is that they need, right? And you carry out the filtering accordingly. So I'm prompting the user, and then we're going to store the response in this variable. And then that is the variable that I'm going to use to execute the query. So I'm going to say where The Lambda expression, Q dot name is equivalent to league name. All right? We could also even say dot equals, because we could just rely on the C sharp functions that we probably would use in a regular statement, but you notice that that looks just like a statement, right? It's the same kind of logical operator that you would use in a condition. Because once again, this is just a condition that needs to be true or well, it needs to be true in order to be included in the list. Right? So now we kind of boasted it. Now we can interact with it a bit more. So I'm going to run this, and we're going to see the prompts. I've already entered Syria I press Enter. And then it took 83 milliseconds to go out and look at it, now it parameterized league name. And then it is executing with the parameter. So you see, once again, this is automatically kind of safeguarding against CQL injection through the parameterization, we're getting back our seria. Now, we only have one league with that name, but we did have multiple red stripe premier leagues. So I'm going to try this again with something that I know is going to return multiple records. We're trying again with red stripe Premier League, and the filtering will now bring back all of the records. You see it is working. It's bringing back every single one that has that exact name. And remember that this time we didn't use the double equal sign. We used the C sharp because this is a string, so we said dot equals league name. So it allows us to write that C sharp syntax that we are familiar with in other parts of our code right into the query, and it just handles all of the rest for us. Now, let us do one more experiment here. There are situations where your filter might not be as exact as a true or false. It might be a kind of fuzzy logic where you want to know if it contains, especially when we're dealing with words, right? So in a typical search, you would be able to type in a part of the word or part of the expression you're looking for, and then you would see matching results. So I've extended the prompt to say interleague name or part of, and then what we're going to do is do two queries. So I'm going to take all of this and duplicate it And what I'm going to do is called one exact matches and the other one partial matches. So for the partial matches, I'm not going to say dot equals, but I'm going to say dot contains. Because in C sharp, if we were to try to figure out if a string has another string in it to say dot contains. So that is what I'm going to pass over in this were expression. So let us take a look at this. So I'm just going to use I'm going to enter one bit of string to search for. I'm going to enter premier right? I know I don't have any league that is an exact match for premier. So we already saw the exact matches work. But I'm just going to type in premiere. And then what you're going to notice is that it's going to execute both queries. So it's going to say, executed this, it's looking for premier. So this one has the wear clause for the league name premier. Of course, there are no. But then this one has the league name like or chart index L dot name is greater than zero. So it just chart out a whole nice query to say, I'm looking for something like what is in the parameter. All right? And then that is when we see all of our leagues that have the word premier coming back. All right. So that's another way that you can go about filtering your queries. Now, when it comes to the, you can use the content, sorry, or you can use EF functions. So I'm just going to duplicate this, and I'll commend that part out, so that's option one. Another option would be to use the function. So I still need the token, still need to initialize this, but I'm going to say EF functions. Dot. And then you see here, I can do contains, I can do and a bunch of other functions that would be available to me if I was doing straight SQL. Some of them are right here for access for us. So I'm going to do the like because that is more like what we're looking for. I'm going to say like and then I'm going to say q dot name. So the method takes, what is the expression or what is the string? What is the database column that I am comparing on. And then the second parameter would be the string pattern. If I wanted to put in a specific pattern, if you're familiar with colority then you know that when you're dealing with like, you have that wild card. So I could say premier modulus, which means it starts with the word premiere. If we put the modulus in front, it ends with the word premier. So whatever you would have done in SQL for that modulus for that wild card bit, you can do it inside of the string and it will just aggregate it and create that expression exactly like what we would expect. So in our situation, because we're dealing with this variable, I have to get a bit more fancy. I don't want to hard code what we're looking for. I'm just going to use some interpolation here, and I'm just going to put in the value that is coming in from the input. So we're saying, get me all the leagues where The name is like this search pattern. All right. So let me just do that one more time so we can see what that will spit up. This time, I'm going to type in La. I don't know. I don't have a Liga. I don't know which other league in the database might have the letters LA in it, but we see La Liga being returned. And if we look at that query once again, we see that it is giving us the format and then it's saying where the name is like. That parameter. So you see that kind of looks a bit cleaner than what the contains query generated, but at the end of the day, they're both going to give us very similar, if not the same results. So that's it for a little experiment with filtering our queries. When we come back, we'll look at how we can go about aggregating data, maybe one, the first one and list, the last one and the list, we one the sum of everything in the list, those kinds of things. So when we come back, we'll take a look at that. 14. Additional Execution Methods: Hey, guys, welcome back. So the objective of this lesson is to understand or have an appreciation for some of the other methods that we have available to us via Lincoln FCR and how they work exactly. So I've already gone ahead and created a new method, and this is for additional execution methods. So I have a statement here that looks just like a select statement coming from our previous activities where we have context that leads where, and I'm just saying contains A. Now, the case study for an aggregate function or one of these additional execution methods would be that you probably don't want the whole list. Probably you want something done against the list or you want to cherry pick the list? So when we talk about aggregate functions in C, we talk about things like Min max, count, some, those kinds of operations, all of those are available to us through these additional aggregate functions. Now, another thing that you'd probably want to do is get the first or the last in a list, those kinds of things, right? So I'm just going to use this as a quick example. And what if I wanted the very first league that has the letter A in its name? Then after seeing all of this statement and adding the filter, I would then end it with a first or default. You'll see here that we have, of course, the Async methods and for every AC there's a non AC version. But it's not necessarily the other way around. So not every method has an Async counterpart, but you'll see that later on. Okay. So here, I can say I want a first or default or I can say the first. The difference between first and first or default is that first we'll always expect to see a list and it will get the first. So if nothing is returned, then it will throw an exception. It will end the execution with an error. All right? First or default over will say, I will attempt to get the first and if there is nothing to get, then I will return null without ending the execution at that point. All right, so first or default is probably a safer bit. And then what that will do is just get back the one league that is at the top of the list. All right. Now, we could actually simplify this because what we're doing here is saying give me the work clause and then put in the condition and then get me the first. I could have actually just done this. I could have said context do leagues do first star default, this condition. That is an executing method. Remember going all the way back to our simple select. Sing context dot leg doesn't really do anything, but then once we put on that method, it executes. We saw the same thing with the to list and the ware. But in this situation, I can just say first star default and then that condition and then it will execute the query accordingly. So I'm just going to kind of break out and show you some other commonly used execution methods. So I'm going to replace that line with a simple statement that is getting our DB set here, right? And of course, from we have the DB set, we can access of the execution methods. So let's just go through them kind of in order. So we already looked at the tost. We know that the tolls is going to execute the Selcti statement. We also have first and first R default, and we just discussed the difference between the two of them, so we can say leaks dot first a sync or first R default a sync and once again, they do have non asynchronous versions to these methods. Based on your situation, you may be using the A sync, you may not be using the A sync, but I'll just keep it consistent and stay with the A sync. We also have the single or single default. Single it does what the first would do except first is seeing a list, and it's just going to pick the first, literally first from the list. Single is going to operate similar to the first where if it is looking for one, first is expecting a list and it's going to take the first. If no list is returned, then this throws an error. Single is expecting only one record to be returned. So whatever qu you is expecting only one record to be returned. If it sees more than one, it will also throw an exception. Single will just not throw the exception but return default under similar circumstances. You can always read up more when you just hover over the method, it will give you some education as to what it does. Now, when it comes to some of the traditional aggregate functions that we would probably know from SQL, you see count. So you can say leagues dot count sink you will execute the count query. You have long count, you have minimum, you have maximum, you have S, you have a number of other ones. Like I said, you can always just say leagues dot or context dot table dot, and then you can scroll through and you'll see the plethora of methods available to you. You can choose to use them when you need to use them. But sitting down and trying to go through all of them at once might be exhausting, but At any point you think you have an idea in mind. You can always just do the dot control space and just look through and see which one of these meats you need at that point in time. Now, another execution method, which is not necessarily a query method like one of these, but it will execute against a DB set is to find a sync. This one will actually just go out and find based on a value, so that value is going to be the key value, right? So finds an entity with a given primary key. So when you say find a sync, you're expected to put in whatever unique identifier is therefore that table, and then it will bring back that record. It will either bring it back or bring back null, right? Or it will return the record or return null. So that is how that find a sync work. So let us try executing and see exactly how this would work out for us. So I've set a break point at the start of this method, and I'm just going to go through step by step so we can see the C quel being generated. I believe when it gets to the single and single or default or at least the first, it will probably throw an error on one of these, but let us step through and see exactly what is happening. So I've put my windows side by side so we can see every step of the way. So when we say to list, we'll go ahead and execute that one, And then we see here that we're getting that select query that we're familiar with already. So I'm going to go and do the first. So you see it's getting the first, it's select top one. From league. All right. So that's what the first does. And the first or default, if I'm not mistaken, we generate pretty much the same SQL as the first. But like I said, if first doesn't see anything, then it will throw an exception. Now, single is definitely going to throw an exception because it is only expecting one record, but it's getting the list and saying one single. So when we step with that one, then it throws that exception. There we go. So the exception is that sequence contains more than one element because it was expecting only one element to be returned, right? So I'm just going to kill that execution, and I'm going to comment on that line just so that we can continue with the rest of the execution. So I continued the execution against all the other methods, and you'll see to the right, some of them were successful, some were not. So single singular default and pretty much all of these math ones through errors, Really, because, well, I'm not really doing anything mathematical here. I have to make sure that I'm finding the mean of something, finding the max of something. Obviously, that's not really happening when I just say leagues, right? But the point is that these are some additional methods that you can use when you're doing your more complex operations. And if you look to the right, you'll see that up to the cot when we asked when we asked for count and we asked for a long cont, the difference between the two was that we had select cont star for the cot and then select cont big star for the long cone. The only material difference really is that count returns an integer and this one returns a big integer. So, you know, based on the size of the database, you may never end up using long count or big in SQL, but the method is there regardless. So that is really it for the execution methods. As we go along, we might find use for them. And we may need to interact with them otherwise. But right now, we don't. I'm going to comment out all of those that probably gave errors, and I just want us to see the q that gets generated when we try to find. So we see here the execution for the find is that it finds a top one where that condition is met. So all of that once again is generated for us. And more than likely we're not going to hard code that ID anyway, that ID would be coming from somewhere, you know, like when somebody clicks on edit in your user interface. You have to know what record they intend to edit. You have to go and find that record, return it, and then present it to the user. So that's a case study for when you would use that find. So that's it for us looking at additional execution methods. As you saw, there's a much longer list than what we have gone through here. But you have these four reference, and I'm sure you'll find good use for them in the future. 15. Alternative LINQ Syntax: Hey, guys, welcome back. In this lesson, we're going to be looking at alternative link syntax. So up until now, we've been looking at link in the form of these execution methods and different Lambda expressions that we can place in some of them to kind of get our operations unique to our needs. Now, That is fine, using the Lambda expression method is perfectly fine, and as you can see, it can be written in one line, nice and clean. However, there is an alternative syntax that some developers may find a bit more intuitive because it looks a bit more similar to traditional SQL, but it is still C sharp. So this is actually the first way that I used to write link before I discovered or got comfortable enough with Lambda expressions. And I have the code prepared here for you, so I'm just going to walk you through it. So this time we're looking at the teams, right? And then our syntax here is saying from some token. So this could be, you know, we used Q in our Lambda expressions. This represents something just like that Q. We could say team, we could say Q. I'm saying I. All right. So from I in, and then we say the table that we're interested in looking in. So context that teams, then select. So like I said, it looks a bit more like Q because it actually has some of those SQL keywords, right? It's just a little bit backwards because it would have been select star from table. And this one is from record in table, select record, or we could say select something specific. But we'll look at that later on. So when I do this query, it is going to give me queriable of teams, right? So that means I need to convert that into a list. But let us execute and see what we get, and then we can refine as we go along. If you look at that, it actually executed this query quite perfectly, right? It gave us our select statement, and it's giving us back our teams. So we didn't have to explicitly say to list and try to convert it. Now, one of the benefits, however, of having it as a list, is that when it is in the form of quarable, our ability to manipulate it to a certain extent is limited, right? So if I looked at teams, there are certain functions here that are unique to i quarable and there are certain functions that are unique to lists that you probably would want to use later on in your coding that you can't really get when you're dealing with an i quarable. So from time to time, I actually just wrap this all in parentheses and then say to list, and then that just gives me back my list, which is a bit more native to what I would want to be doing in my C sharp code at the end of the day. Of course, to do that, I have to change my method from void to AC task, right? So then we are getting our list, and as you can see, the list, the collection types work, fairly similarly, but like I said, there are certain things that the list affords us that an querable enumerable, and the other type of list collections or collection types in C sharp, they just don't. But of course, everybody's situation is different. Use the one that is appropriate to your context. Now, let us extend this. We also looked at querying, or sorry. We also looked at filtering our queries. So right now, this is just doing a standard select star. Right? But what if we wanted aware clause? Well, we would say from I in table, and I'm just going to break the line here. So it looks a bit more readable. Where, and you'll see that it kind of highlights those keywords for us. So where I which represents expression or any one record in the database. So I can say where I name is equivalent to, and then put in my records. So the same way that we were able to inject in or, you know, a variable or a static name or any of those things, we can do all of that right here. Let me see if this would even work. I'm going to try to say, where I name or let me say where EF functions like. And then this would be I don't name. And then we have the league name, which I'm just going to do a quick prompting this method for also, this would be team name. So Enter team name. Let me just change the verb name. And there we go. So we're prompting for the team name and part of. And then we're saying from the team table where dot functions like dot name, right? Our expression, and then we want to select, and then we want to put all of that in a list to return to our variable call teams. So this is going to work, of course. I'm just highlighting the fact that we can use the alternative syntax in the very same way that we use our Lambda expression. So here I'm testing it, and I'm typing in B A Y as part of the team name. We see our select statement with the statement being generated for us, and we're getting back Bar and Municx since it's the only team that we have that has BAY in it. So once again, this is our alternative syntax. Whatever you can do in the Lambda expression, you can also do in this kind of syntax. Personally, however, I find the Lambda expressions to just be a bit easier. So ever since I got comfortable with them, actually abandon writing my queries like this. I haven't written queries like this in years to be honest, but it's good to have the knowledge of all of your options and alternatives, if need be. Okay. 16. Simple Update Query: Hey, guys, welcome back in this lesson, we're going to look at performing an update operation. Now, the general workflow for an update would be that you retrieve the record, that you intend to modify, then you make the modification, and then you save the changes. So that's a typical workflow. In any application that you use, that is what is happening behind the scenes. You indicate you want to editor record, it goes and finds that record, presents it to you, make it changes, and then when you click Save, it Well, you would have made the record changes and then the save would commit those changes to the database. All right. So let us first look at retrieving the records. So what if we wanted to modify? And we have some let me just go and look at our leagues. We have some duplicate values in here. I think we have three red stripe football league records. And yes, we do. So we have IDs, two, three, and four being the same. So what I'm going to do is just change the names on some of these, right? So I'm going to change the name of three. It's a duplicate but then in retrospect, we realize, oh, that really should have been the Scottish premiership, right? So we know that we want to modify record with ID three. So what I'm going to do to retrieve the record is SVR League, z equal to context, leagues do find, and I'll just use a sync. Of course, if I'm using a sync, I have to transform my method into an A sync task, and then I wait it here. So we're going to say, find a sync, and then remember that all we have to pass in is the primary key, so we know we want record with ID three. Now, when we get that league, the change I want to make is league dot name, I want that name to now be Scottish premiereship. Then we save our changes. So we already looked at the save changes from when we're inserting the same save changes. Anytime you are going to augment data in the database, you have to call this to commit the command. Then I want to print out the result of this or I want to print back to screen this record. I'm just going to create a method called get record, which is specifically just to go and find this record. I have it defined up top here, or I'm just going to say Varg, go and find the same record. So after the save changes, then it would have closed the connection to the database. I'm just going to await get record, which is just going to go and retrieve the record and then print it back to the screen just to show that the update procedure was successful. So let us give that one a goal. So when I execute, we see here that it is doing the select, that's where it is doing the find, so it retrieves the record, and then it is carrying out that update, so it retains the primary key and the new value for this record. And then it carries out that update statement, set the name to be whatever values in that parameter, where the ID is in that parameter, and then it goes ahead and updates. And then, of course, our get record is just re opening that select query, that's a selquy and giving us back that record. All right. So let us try that again, and I'm not going to change anything. I'm going to leave the same record. I'm going to make the same change and everything is going to remain the same. And then let's see what happened. This time, we don't have an update statement. Take note of that, right? We don't have an update statement because it notices that whatever change I'm making, it's not really a change. That is the value in the database already. So it's not going to bother to waste its time to say, Oh, I need to make a change. CEFCO is intelligently choosing not to open a connection to the database and carry out a command that it realizes it doesn't need to. Now, what enables this kind of change to happen, meaning that FCR is seeing that the data that we're presenting for that field is different, so then it knows to save changes. This is called tracking, right? We look at tracking and no tracking later on. But just as a quick preview, whenever it is that we are modifying objects, FCR is actually tracking in the memory what objects it has if there are any changes made to them so that when we say save changes, it will literally say, Okay, I can see a change was made here, I have to commit this change, et cetera, et cetera. So that is what tracking allows us to do. Now, there are other things that are other ways that we can carry out an update because an update may never be as straightforward as finding that exact value and just changing one property and then just saving changes. A lot of the times when people, especially have user interfaces and they're interacting with a form, when we allow them to edit the data, we don't know what has changed. So we can't for sure, know that, okay, we're only going to update name, and we're only going to update this and that. We don't know if maybe in editing a team, they change the league and the name. We don't know. So For this other example, I'm going to use team. So I renamed our method from update record to simple Update league record. So it's clear that we're dealing with the league and it was a simple update, and then now we have simple update team record. So let us take a look at the alternative. Now, in the case where somebody using a web interface would have submitted the form, that means that we already have all the information about the record that we need at that point in time, meaning we don't have to go and find it before the update because we already found it before showing it to the user. Now the user has submitted, we have the new data. All right. So I'm going to simulate this by creating a brand new well, an object of type team, right? And then what I'm going to do is specify an ID this time. So up until now, we haven't really given any of our objects any IDs because those are auto incrementing. However, what we need to realize is that when an ID is present, of course is going to look through the database tables for the record with that ID. So I'm going to go over to the team table. And what I'm going to do is manually insert just so we can get through this activity team that is local. It is in the Red strip Premier League, but I'm going to put it in with a few spelling errors, right? It's Tivoli. This is Tivoli, and I'm going to put it in with the wrong league ID, 'cause it is not in league eight, and based on the teams in league eight, we can surmise that Sri. It's not in Syria, so we have to do that kind of update, right? So let us say that this was erroneously entered by some user, and then another user picks up on that error, and they are setting out to write that wrong. So they would have been presented with the form and then they put in the corrections, and then they submitted the form. So the object that they would have submitted would look something like this where the ID is seven, that is the ID for this team, yes. And the name having been corrected is Tivoli Gardens FC with an O instead of AW, that's a correct spelling. And then the league ID would now be the Red Stripe premier league, right? So let's use League ID two. We see that we have another duplicate, but that's fine. Let's just use league with ID two. So this is the update that this user is submitting. Now, how do we get this new record into the database? Well, we can say context dot league teams. We're dealing with teams dot update. So we have an update method, which just like the ad is going to take an object of the data type that corresponds with the table. So we have our team object. I note there's no asynchronous there's no asynchronous version of this. We have the single and we have the batch version, and we already looked at the batch range means if we have a bunch of them to update, we just handed the list and it will deal with all of them for us. Right now, we're just looking at the single. And then after that, we have to call our context dot save changes. And we'll use the A sync for that one. And of course, I have to make this a sync and then all our problems are solved. Let us take a look at what gets done when we pass in this kind of update. You'll see that it is a similar kind of operation. No one, it doesn't update once again because it sees the ID. So it knows that there's a team with ID seven, and then it will go ahead find it automatic well, it doesn't have to find it because the update statement says league ID change, name change where the ID is equivalent to the ID that it saw come in on the record. So it's doing all of that automatically once that record ID is there. So a number of let's go through a number of permutations. If we did not specify this ID, what would actually happen? And I'm just going to use another football team, let's Ciba United, and that is also in that red stripe Premier League. So let us try that one and note the difference. This had no ID. We took the ID out, and it took that as a que to do an insert. Take note of that. So the update command or the update function was saying that, I don't see a primary key on this, this clearly doesn't exist in the database yet, so I'll go ahead and insert it. Now, I'm not touting this as an alternative to the insert. I'd like to keep them very separate. If I'm inserting something, I use the add function. If I'm updating, I use the update function, or I just track it accordingly, but tracking is not always an option like we're seeing in our applications. But one thing to note, if that ID is not present, it will go ahead and add it. So that's why we always make sure to include the ID information on a form so that when it is submitted that ID is present in the record so that we can properly carry out that update. So that is a very important point to note. And one more experiment is putting an ID that does not exist. So if that ID value is completely incorrect. I have ten, and we know that we only have well, no eight teams, right, or teams with ID eight. So ID ten is completely wrong. It will try to do the update, but then it's throwing an exception on the save changes, saying that the operation failed, well, it expected to affect at least one row, but affected zero. There might have been some modification to the data therein. So you can see here it's not really telling you exactly what's wrong. We know that this is wrong because the ID value does not exist. And it is saying that it thought it would do at least one change, but nothing happened. So it's not really sure what might have happened. You need to go and do some reading on the documentation. But like I said, I want to see what can cause these errors so that when we get slightly vague errors like this, we can surmise that, okay, there must be something wrong with some of the data that I provided somewhere here. So that's really it for the update. As you can see, it's a fairly straightforward operation. You have two options once again. You can go and find the record, make it changes, and then save the changes, and then tracking will allow COR to know that, okay, this record was modified, so I have to create the update statement for it. Because even if we've got a list of them, if we made changes to only one, it will know that only one needs to be updated, right? And the alternative know or another situation might be where tracking is not necessarily an option. And the record that we get to update is not being tracked by FCR at that point in time, so we can use that update method, which will go ahead and look for the record and generate the update statement automatically. If there is no ID present, then the update method will go ahead and add the new record. Okay. 17. Simple Delete Query: And we're back. So up until now, we have looked at all of the letters in CRD except the D, which is delete. So we've looked at how to create. We spent some time looking at how we can retrieve and the different ways. We just looked at how we update and now we want to know how we delete or remove data from our database. So I've already set up two methods, one to show a simple delete example and then another one where we show a delete when there's related data. All right? So the simple delete. Let's take a look at the options that are available to us. And I'm going to spend some time with the league table because we have to do some cleanup. One, we have duplicates here and here. And then we also probably want to remove Bundesliga. So that's what we're going to be doing today. All right. So when I say context dot and choose a table. So in this case, it's leagues dot, and then start typing Delete, or rather remove my bad, we'll see that we have two options. We have removed and we have removed range. So remove like add and update deals with one record at a time, and then the range operations are for bulk. All right? So when I say remove and just take a look at what it requires to do a removal, we'll see that it needs the whole entity to be removed. All right. So typically when you're writing an q statement, you would say delete from table we maybe ID is equal to one. All right. And then it will just go for that go for whatever record matches the condition I was given and delete that. And without that condition, it will actually just wipe the table. So at least, there's a safety net here where we have to either provide the entity or the list of entities to be removed, which means that we have to make a very deliberate effort to know that this is what we want to remove during runtime. So the risk of wiping the database is far reduced in this situation. All right. So, of course, if we need to provide the entity that is to be removed. And if you just look at the documentation, it says that the remove puts the entity in a state called deleted. So it marks it to be deleted, but as you know, nothing happens until we call save changes. So if I need to provide an entity to this record, then clearly I need to go and find what I need to delete. So I'm going to put in a line right above that where I'm finding the league with the ID four four because that is one of my duplicates, right? And this one doesn't have any related records. So this is a simple delete. Just finding the one with the ID four, when we get it, then we can say, that is the one I want to remove. And then as we know, we say save changes. Of course, using the ASIC to add a s two, the method declaration, All right, and there we go. So when I call that one, and I'm just going to comment on that second call for now. When I run this operation, we see where teared up the select query to retrieve the record, and then it went out and said, delete from leagues the same query that we know and love. Of course, with that were clause make sure we don't wipe the tables in the database. All right. That is how a simple delete really works. Now, the reason I'm differentiating between a simple delete and delete with relationships is that when we have related records, a delete operation becomes a bit more sensitive. Meaning, well, there's a setting called cascade delete, which means that if I remove the record with a primary key, then every other record that has a foreign key to this one will also be deleted. All right, so it would almost be like delete you from a database. So all the cards you've ever owned and all the information relating to you will also be wiped out from various parts of the database. While that might be good in some situations that might be very dangerous in others. All right. So in this situation, if I'm going to go and delete Bundesliga with ID nine, so I'm just repeating all the same code from the symbol delete. What makes a difference is that I know that I have at least one team related to the Bundes liga, right? So that means if I removed Bonds Liga, then that team would also get removed. Just the same way if I removed Syria, then one, two, three teams would be removed. Now, going back to our migration file just so that we can understand the constraint rules when the table four teams was created and the foreign key constraint was put in, by default, it gave referential action do cascade. There are other options that we could set here. So it could say restrict, meaning you cannot carry out a deletion on any record that has related or dependent records. Set Null means that it will set all of the other records, all of the related records, the foreign key value will now become null, right? Say we'll delete the parent and just set all the foreign key values to null. And then you have no action, which is, well, it says, ignore the constraint, and then another one that says default, which would just set a default value afterwards, right? So I'm going to leave it on the default cascade, but once again, this might not always be the best situation. And there are times when entity framework core in generating the migration will actually indicate to you that If I put cascade there, it can run into certain kinds of errors because, you know, this table might have a dependency on this and that that and you get a circular reference kind of situation or different tables relying on the same data. So CO will kind of warn you in situations where your database design may not be optimal for this kind of constraint rule. All right. So with all that said, let us try and carry out our deletion on Bundesliga, which we know has a related team and see what happens. So when we take a look at the qua that's generated. It doesn't look any different from what we saw with the previous one. So you're probably wondering, okay, so where is the second delete for the related record. Let's verify that that happened. So if I refresh teams, Bundesliga is gone, sorry, leagues, and then if I refresh teams, that means Burn Munich is also gone. So the cascade did occur, but our logs only show one deletion. That is because a cascade rule is really in the database engineer. It's not necessarily an ca statement that will get generated. All right. So that migration rule was really set in the database when we affected the migration. The database itself knows that the rule is delete all related records. So that has nothing to do with FCR at that point. So that is one of the dangerous situations that potentially dangerous situations that you need to be aware of when you're designing a database, setting up your migrations, and carrying out your delete operations. Just know that by default, it will say cascade, but you can always override that to say restrict by default or anything like that through configurations. So that's really it for our delete operations, as you can see, it's fairly straightforward. We just need to go and retrieve the record. More than likely we would have the ID for the record that we need to delete anyway, we go and retrieve it and then just tell it that's the one we want to remove, save our changes, and then that's it. 18. Tracking Vs. No Tracking: Hey, guys, welcome back. In this lesson, we're going to take a look at tracking versus no tracking so that we have a better appreciation of what is happening in the background. I've already written some code can pause right here, replicate it, but I'm going to walk you through what each line is doing. We start off with the method called tracking versus no tracking. In this method, I have two statements, one, where we're getting value from the database, and I'm just calling it with tracking, and then another one where I'm getting it without tracking. If you take a keen look at the two lines, the difference that you'll note is that I have dot as tracking in the statement. The first one is what we're used to context dot teams. I want the first record that has the ID two. Right? That could almost have easily been a find except the An tracking does not work when we're using a find. All right? So if we were to try and find like we would have delete and update and so on, we can put An tracking in this kind of statement. Hence my writing of the first or default. And then for consistency I did it both times, right? So with no tracking, I can say, give me the team So don't track after you've given me, please don't track it in memory, but I want the first or default with ID eight. Now, this seems not intuitive the way it's written because you'll probably say, why don't I say, give me the first team with as tracking. The reality is that after the first or default statement, All of this pretty much becomes the object that we're looking for. So, in other words, the only thing I can do after a first star default and well, we're using the acing. So I'm just going to wrap that inside of parentheses. So after this statement executes, right? And I only have to do that because it's acc if it wasn't acing, then I wouldn't have had to do that, but at the end of a first default operation, we're going to start interacting with the actual fields that are in the object. So that is why that Ano tracking just can't come after that statement. So we have to tell the DB context that look into the teams table, don't track the items, but I want the first one that meets this condition. That is the statement. Now, the real advantage to not tracking is that it actually releases memory a bit more and speeds up performance because you can imagine that if you are retrieving 100 records all with tracking, then FCR is going to have to be monitoring 100 records, and that's just on one request. What happens to all the hundreds of records that you might be juggling in the system, right? So then the FCR engine has to work over time to be tracking all of these all the time. So in a simple operation like maybe you're just doing a read on the list, like you're listing things in the database to your user. You can always just say no tracking because you don't have to track. Things that are in a simple list, right? At the time, however, when you're about to make a change and you do a statement like this, then yeah, the tracking will be there. You go ahead and remove it or even in the update, the tracking would be available after the find so that we could sorry, that's a wrong method so that we could, here we go, made the change, and then it is being tracked in that moment to be saved, right? But then for large read operations, you can always use the Ano tracking to kind of reduce the attention that FCR has to pay to each record being retrieved. Now, what I've done after retrieving the with and with no tracking, I've made changes to their respective names. Then I'm going to show you that we can actually look at the entries. So we have this thing called a change tracker as part of the context, which is basically just going to show us information about which entity is being tracked, what the state of it is right before the save changes, and then we're going to take another look at it after the save changes. I only did this as a precaution, but I'm sure that this will get updated afterwards anyway. But we will see All right. So let's go ahead and execute. I've set a break point at the save changes line, and I have the variables in the watch. So I have the entries before save and after save. So if I take a look in the entries before save, it will give me the results view, and it's showing me that only one of the records it is tracking as modified, right? So if I expand, you'll see that the entity state here state is modified. We actually have some enums given to us by FCR, where we can say entity state dot and you have modified added deleted. Anything that pretty much would be able to do in a crude context, we can tell what change is about to be saved or what state it is in right before it is about to be saved. We see here that record with ID two, is in a modified state. Record with ID two was the one that we retrieved with trucking. We made the change, and then right before we save, it sees that it is the only one it needs to save because we were not tracking the record with ID eight. So no matter what change we made to it, it just doesn't care. So that would be the scenario that gave us this situation where we would have the record, we know everything about the record, but then we have to tell the context that this team or this record needs to be updated, at which point it will start tracking it. So while it was here, it was not being tracked. This object is in the same state as this object, just not being tracked by EFC. However, if we were to put in the manual update statement for with no tracking, then by the time it got here, it would also be listed as a modified record to be saved. All right. So I'm going to just step past that one and then do two more steps so we can see entries after save, and entries after save. Now, it only has it's still tracking the fact that it had that entry before the save. No, it is in an unchanged state. So after we save the changes, they move from whatever state they were in added deleted or modified, and they moved to an unchanged state. So that means entity framework is still tracking it right? Once it's listed here in the entries, that means it is being tracked. So there are times when you might run into some concurrency issues when maybe you're carrying out an operation and then you save the changes to the record, and then you probably try to manipulate it again right afterwards, and then you might get an error saying that this is already being tracked by EF course. So that is one of these situations. So sometimes you need to release it from being tracked, but we won't get into that level of complication, at least not right now. For now, we just want to focus on what tracking does differently from no tracking. So like I said, in a scenario where you only need data for read only purposes, then as no tracking creates a very efficient scenario for you. Okay. 19. Review One-to-Many Relationships: Hey, guys, welcome back in this lesson. We want to quickly review our one to many relationship and how FCR makes life easier in defining this kind of relationship and allowing us to interact with related records. So just as a recap, we have our league table defined as this data model, and we have our team table defined to the right. So we know that by following our naming convention, first of all, FCO was able to infer that a foreign key relationship existed between these two. What's that naming convention? Well, firstly, I would have indicated that the field name is league ID. All right. So we have the table called League and the foreign key. Just by calling it League ID, it actually inferred that there's a foreign key relationship. So just as an example, if I did not include that virtual navigation property, this would still know that there is a foreign key just because of the naming convention that I have employed at this point. Now, because of the data type being used, I'm using t and t by default cannot be null. So you'd realize that that migration that was generated would have a nullable. Here it is League ID was the column, and a ullase falls, meaning it cannot be null in the database. Well, C sharp supports nullable data types. If I said in question mark, then that would automatically be nullable. So I'm just going to run a migration just to show. So this is our migration add migration made league ID nullable. And then in this new migration file, it kind of looks different from the previous one, and we will be going through more migration. So I'm not focusing on what we're looking at just yet. I just want to highlight that the alter column is now seeing that nullable is equal to true, all because we put that question mark. I C sharp, if you make the data type nullable, then that is how FCR will know that it's null or nullable in SQL should be true, pretty much, right? So it's saying that the old type new type is TS, but it is now nullable. So that is one of the ways that we can make a foreign key nullable. So why would you want to make a foreign key able? In a situation like this, maybe you can have a team without a league. So later on we're adding more tables, one of the tables that we're going to add is a coach, a coach can be a coach. Well, technically, a coach can be a coach without a team because my profession is I am a coach, but I don't have a team coaching right now. So I'm in the coaches table, but I just don't have a team. So at that point, that team ID would have to be null if I am not employed to a team at that moment in time. So that's just a quick example which we'll look at later on. But for now, I just want to focus on the fact that we can make that foreign key nullable. Now, going back to our team, data model, I've made a slight adjustment where I've removed the integer for the foreign key and I've replaced it with only the navigation property. That is also going to try and generate a leable foreign key field. The only problem here while the foreign key field will be generated in the database without having the property in the class, there's no way to actually get that integer value or interact with that ID value, right? So that is why for me to make life easy, it's just best to interact with them both. Now, I've made that knowable, but I don't necessarily want to keep that chain. So what I'm going to do in the package manager console is remove migration. So we probably saw that one earlier when we're looking at all of the options or all of the commands that we can run. So to do this action, it says it's right here, remove migration. So remove migration will always try to remove the last or the most recent migration done. So there's our most recent migration. I can just say remove migration, and it will kill that file. Please note, however, that if and sees they're saying it reverted and it's doing everything that it knows it needed to. So the only thing though, is that if you have already committed that migration to the database, and I removed migration becomes a tad bit more difficult. But that is something that we will also look at, so don't worry about that just yet. Now, one more thing I want to point out about these foreign key relationships is the fact that on the league side, I can add a property that is a collection of items that I know are related. So when we scaffolded the database, you probably took note of the fact that the league had a collection, and collection. So this can be an collection. It can be enumerable, it could be a list. It really is up to you, but we'll just infer that a collection of team right. And I'm just going to call it teams means that league can access automatically the list of teams related to it. So think about it. In general, qua, if you wanted the league and then you wanted all the teams in the league, you'd have to find the league maybe by ID and then go and curate the teams table to say, get me all the teams with the league ID, right? Or you just get all the teams with the league ID, but then you have to interjoin on the league table to get the details of the league they're in. So we're already kind of doing that because one with team, I can get the details of the league that team is involved in. We'll look at that later on. Right? But just by putting this collection type here, I can say, get me the league with ID one and include all the teams. So automatically, I'm getting the league, I'm getting the name, and I'm getting of 2030 teams that are associated with the league all in one object. So that's another advantage. So I'm going to add this property, and I'm going to leave it there. You can do the same in your model. And the final thing that I want to point out really is I want to reiterate the importance of following the naming conventions. When you don't follow the naming conventions, you're actually fighting against a system that is designed to help you do things better. So in this particular situation, I'm referring to say the foreign key. Creation because you might have other ideas as to what you want to name this foreign key column. And I'm not going to say no. Your business rules may require you to use another column name. But then it becomes difficult because if I wanted to name this league FK, and I know this is going to break some other code that I have in other places. But if I did this, what would happen is that when I run the migration, it is still going to generate a column called League ID because FC is following its own convention, and then this is going to be a random column. A random column called league FK that has absolutely no affiliation with the foreign key. So once again, I just want to reiterate, follow these naming conventions, and life will be much easier for you. Now, in the next lesson, we're going to look at the many to many relationship, and I'm going to delve some more into some of the awesome things that FCR can do for you. 20. Adding Many-To-Many Relationships: Hey, guys, welcome back. In this lesson, we want to start looking at many to many relationships. Now, the case to def many to many relationship would be when you have many well, many records relating to many records. Within our context of our football application, or football database, we have to take into account the fact that they're going to be many matches between many teams. So many teams will play against many other teams over the duration of a season. I have on screen a nice useful app called raw dot IO. It's a web app, and it's completely free for use. And what we're going to be doing is visualizing our data structure. So I'm going to just use rectangles, nice and simple, and I'm going to call this one league and we're going to call this one team. So we know that we have leagues related to teams. Nice and simple just using an r to connect them. All right? So we have league and we have team. No, I need a new entity in the mix, and I'm going to call this one Mach. Now, a match is going to comprise, and I'm just going to write out the fields and the entities. We already know what's in league and team, but this one is going to have an ID. Of course. It's also going to have home team and away team. We can say no, I'm kind of veering away from the naming convention, right? So home team away team, and we're going to probably have time. Like I said, we're going to end up with the same home team, the same team being home team multiple times and the same team being away team multiple times, but they're being pit against each other. So this many to many relationship really needs what we call a linker table, which is a table that's going to sit in between the two related tables. In this particular situation, though, the many too many is really between many teams and many teams. So that means we have we have team being related to this table twice, So, forgive me if you're new to database development, but that's just how it goes. Sometimes you have two foreign keys to the same table from another table, right? So one table has two foreign keys here. We have a foreign key for the home team, however, a foreign key for the way team. But then this is really saying that many teams are related to many teams, many to many. All right. All right. So now that I have visualized what this database structure needs to look like and this new table, we can go over and create the models for this. So I've gone ahead and created a brand new class that I'm going to call match. And just like it's counterparts, it's going to have certain name conventions. Now, before I move on, I just want to point out that every single table or every model basically has this ID. And then there are times when you're going to have of multiple fields that probably repeat across all tables, like maybe when you're doing auditing or, you know, you have date created, those kinds of fields that maybe everybody needs to have, because clearly, all of our fields, following the same convention, all of our tables, sorry, we have a field called ID. Now, I don't want to keep on repeating this in every single because, you know, if we have 20 tables, then that's 20 copies of the same line of code. So what I tend to do is add what I call, let me call it come on, and then I'm going to add a class inside of come on that I call base domain object. This is not a naming convention either. I just call it base domain object. People call it based data object, people call it base object, whatever it is. But this is really just going to be a public abstract class. I'm only making it abstract because when it's abstract, I can't instantiate it by itself. It's really a failsafe for me, but it doesn't necessarily have to be. But I'm going to take this ID property, place it inside of base domain object, and then every other entity can inherit from the base domain object. So that way, I don't necessarily have to repeat the fields. If the field name changes, at least the common fields across them all, I only have to make the update one place, but everybody is inheriting from that one place, so everybody gets the field. All right, so I'm just going to do that for much. So no match. By default has an ID field team. And I'm not going to remove it from team, but I'm going to let team inherit. And then what you're going to notice now is that it's going to start complaining that it's seeing ID both here and in the inherited class. So that means I can safely remove ID from that class. So that's just a little trick that if you're not already practicing that you can put in to make sure that you don't repeat code too much. All right? So let's move on. We have the Match ID. Now I need property that represents my home team. I'm going to call it home team ID. Once again, this is breaking away from the naming convention. In the previous video, I would have mentioned how important naming conventions are. But there are situations where you really just can't follow the naming convention because when I call home team and away team ID, CR doesn't know or it's not going to infer that this means I'm referring to team and this means I'm referring to team. So we'll see how to navigate that in a moment. All right. So just adding the rest of the properties, I've added the navigation properties that will correspond with each foreign key. So we have foreign key, we have foreign key, and then I have the date time. So I changed to date instead of time because date can capture date and time. So we'll know the date and the time of the match as opposed to my initial design, which only said time. Now that we have this new class or model defined. We know that we have to add to the DB context. We have to let it know. I'm just going to duplicate that and add the new model to the DB set and I'm calling it matches. But I have to do something extra. So once again, I have broken away from the recommended naming conventions. I have to do some extra work to let it know that hey, you are supposed to be a foreign key. So let's go ahead and adjust the team model to let it know that it should have two lists, a list called home matches and a list called away matches. Now, remember that we did something similar for a league. We have team referencing the league table. So we know that a team belongs to a league. However, a league has multiple teams. So in the same way, a match can have a home team and an away team. So one, one, one at a time for any one record or row. But then a team, can have many away matches and home matches. So that is why we have to make sure that we put in these list navigation properties. Now, building on this, we have to let entity framework know that all of that wiring up means that there is a foreign key relationship between team and match. Up until now, of course, still oblivious to all of that. So what I'm going to start by doing is overriding our model create method. So we have configuring. This means whenever you're setting up the context, This is what you should do. Well, this one is saying, whenever you're going to create the model or do a migration, the next time you're doing a migration, make sure you have these rules in place. All right? So not to say every migration will repeat the code for these, but it just means whenever you're building up the database and dealing with the database, this is what I wanted to do. So that's what this method is really there for. So I'm going to remove that default line, and then we're going to have to let the model builder know that our entity. So we're using fluent API at this point to define certain rules, and you'll see why it's called fluent API. So our entity called team, And then just I'm just breaking line so we don't go too far out. And we're going to say dot, and then you're going to see a bunch of options here. Now, I've gotten carried away and tried every combination, but not every combination works really. So you just have to kind of know what you're doing. So in this situation, I want to say that my team has many All right. And then we just define a Lambda expression, dot, and I'm going to say home matches. A team will have many home matches. That is true. Then we go to the next line with one. What's going to infer at this point, no, is that you are going to have many home matches with one dot, and then we are seeing properties from the home matches or from the match entity, see that? So that's why it's fluent because each line is based on the line before. So a team has many home matches, and then the whole matches or a whole match a match only has one, and I'm going to say, if I'm dealing with home matches, then it only has one home team. Right? And then I'm going to have to tell it that and it has a foreign key Lambda expression again, M dot, and then I can specify which foreign key facilitates this has many with one relationship that I'm telling it about. I'm going to say the foreign key is home team ID. And I'm hoping that you're starting to see that you know, naming your columns properly. If it can't help FCR to figure out what you want, it helps you to figure out what you need to do later on. I'm also going to add a constraint to say that it is required, and to finalize, I'm going to define the undelete behavior. We already discussed the undelete behavior where we know that it's a configuration for the database. I'm going to say cascade. That means if I delete a team, I want all the matches to go with it. That may or may not be what you want to do because maybe for the archives, you will want to keep the team and all the match data around. That's up to you and your business rules, of course. So I'm going to just repeat all of this for the Away team. And you'll see that both are identical. The only difference really is that I put away matches and away based columns where they were the home columns. And that's pretty much it for defining the rules around this many to many relationships. So there are quite a few times you might have to get your hands dirty like this. In this situation, it's a very unique one because one we're not following naming convention with our foreign keys, and two, it's the same table relating to another table twice. In another situation, and let's say, I'm just going to give you an open scenario here. We had a table to store the products. We had a table to store customers, and then we have another table to store the products that customers have ordered. So many customers can order many products. So that middle table needs to have the customer ID and the product ID. All right? Think about that scenario. Now, in that scenario, it's a nice clean relationship. It's an easier relationship to define than what we had to do here. And then in that situation, really, you just need to put the list navigation property on either table. And then FCR would just infer that there is a customer product table to be generated. Sorry, I started that just now, but there should be a customer product order table, for instance, to be generated because you just told it that customer would have a list of products and products would have a list of customers. So it will just automatically know, I need to create a middle table for this many to many, and literally, it would be many being the list too many. So there are different scenarios and each scenario may come with its own quirks. This is the quirk of scenario, and I think it's good to do the harder scenario because at least you get to see what might have to be in place should you run into difficulty and your scenario is not as easy as it could be. Now, with all that said and done, let's go ahead and run our migration and see what we get. So this is our migration file. It's creating the table called Matches. We have our columns being defined, and then we have the constraints being put on the team which indicate our foreign key relationships, right? So foreign key number one is between is on the column AA team and it's between teams and that ID and deletes cascade, and it's just the same for the home team. All right. So with all of that done, let us update our database. All right. So if you updated your database just like I did just now, then you would have gotten this error. If you didn't, then shoot me a message and let me know that you didn't. But this message just came up, and like I said before, I'm not going to shun errors because these are the kinds of things that make people afraid of this kind of technology. So It is saying to us that it failed to execute the DB command, create the table. And if I scroll all the way down, it's going to say, I'm introducing a constraint or introducing this constraint, it may cause multiple it causes cycles or multiple cascade paths. So in other words, what it is saying is that having this referential the delete action cascade, on these two foreign keys can be problematic to the database structure. So maybe in designing the database manually, you probably could get around it because I have never been warned by SQL server directly about something like this, but FCR is just letting you know that that can be problematic and it has an error trying to do that for you. So What we can do and what we will do is just remove this migration for no, and following EFC suggestion that this delete behavior is problematic, I'm just going to do a restrict. So in other words, you cannot remove a team unless you have removed all of the matches before. All right. And I actually see where that is reasonable because when I said it earlier that you delete a team and it automatically wipes out the matches that could be problematic, right? And especially where a team could be on the home team ID or the A team ID. And then, you know, that is going to end up deleting data for teams that are still in the system. So that was a poor design choice on my part. No problem. I R has warned us, but once again, I just want to help you to understand what that error really means. So if you see that kind of error coming up, it is maybe because we need to change that delete behavior. Remember that it's going to be cascade by default. So even if we didn't define cascade here, you probably would have still gotten that error. So it's important to understand how to mitigate against that error should you get it. So let us try and add that migration again. And this time you see it saying that it is restricted. That's fine. Let us update database. And this time, I get done and looking at it in our SQL object server explorer, we see that we have our ID and we have our two foreign key columns that are not leable. That is one way of setting up a minute to mini relationship. Like I said, it may differ based on your situation. Given the structure of the database, this is what we had to do to accomplish all of this. These are general guidelines, of course. When you are creating your many to many relationship, you can basically follow these guidelines and you would be able to create it no matter how complicated or simple it might be. 21. Adding One-To-One Relationships: Hey, guys, we'll come back in this lesson, we'll be talking about one to one table mappings or one to one relationships. So I've already gone ahead and created a new entity model, and we're calling a coach, and a coach will be used to depict the one to one with a team because in our situation, in our database, a coach can only belong to one team at a time, and of course, a team. Has a coach, right? But then if the coach gets fired tomorrow, then let's just say that team might not have a coach and this coach might not have a team. So, you know, those kind of unique business rules or constraints can drive the rules that we put in or our database design. So let us go ahead. We already have coach inheriting from base domain object, like we said, base domain object is providing that ID property by default, so we can go ahead and focus on all the other properties that are more unique to the coach. So the first property for a coach, as you probably would have guessed would be the name. Now, I'm not getting too complicated with the database, but we know we have a coach, a coach is going to have a name. At this point, I'm not going to include any more details about this coach. But I will include the fact that this coach has a property called Team ID. So this team ID as the name suggests, will be the foreign key to the team table. Now, our team or team's table needs to have some representation of a coach. And what I'm going to do here keeping it very simple is just reference the coach. So just like that, the coach is going to be a part of a team or, you know, related to a team, and the coach also the coach table also knows that it is related to a team. I'm going to make this nullable. Now, when I was talking about the rules and so on, I went through the scenario that the coach can exist in the table without having a team. So If I don't make this nullable, then it's going to be required. It's not always required because if he gets fired, he's still a coach just without a team. So I'm making it leable so that the migration will know that in the database, this can be null. At the same time, in our team table, just by doing this, it knows that, okay, this can be nullable, so it won't have to make any additional effort for it. Now, navigational properties once again, help you to get the details of the related entities. So when I get a team, I can get the details of the league it's in. I can get the details of the coach. And I can get all the matches if I need to. From the coaches perspective, if I'm looking at the coach, all I have to go on is a team ID. So if I wanted to I could include a navigational property, I'm just going to copy and paste to move along more quickly. I could just include that team navigational property right here so that if I get a coach, I can also include the details of the team and all the matches and everything, right? So let us take a look at the migration that we can generate from this. All right, so my migration added coach team one to one. So our migration is generated, and we can just take a quick look and see that we are getting the new table called coach. And if you've noticed, we've actually skipped one of the more vital steps. I don't know if you noticed, but we skipped what I have taught it as a vital step towards creating a table, and that is to include it in our DB context. We did not include our new table called coach or coaches in our DB context. So let's look at that now. The fact that I have told a table that is in the DB context, that it references this class or some class, the migration or of course, is just going to go ahead and create a table that represents that table name. So that name is being generated based on the property name, which is coach. So this might not be the best approach if we're going to be standard with our naming convention because up until now, we've always pluralized every table name. So I'm just pointing out though that by adding that navigation property, EFCR is automatically going to insert or create that whole migration around creating a table for that navigation property. So I'm just going to remove the migration, however, because we want to kind of keep standard. And I'm going to add it to the DB context. So we have a table called Coach. Redo the migration, and then we can feel a bit better about what is being generated. So I'm just pointing out certain things that FCR will kind of do behind the scenes even if you miss a step. So you want to be kind of deliberate, but FCR is going to make certain assumptions for you based on how it knows it needs to operate. Let us go ahead and update the and that is done. So I just want to point out also that the create index kind of looks a bit different from what we might have seen up until this point, and it has a filter. Team ID is not null. So just looking at it, you're probably wondering, okay, what does that mean, especially since we told the table that team ID is nullable. Well, if we look at the statement that was generated for the create index, we are creating the unique index on that column when it is not null. So once there's a value there, that means you can't repeat that value on any other coach. That's pretty much what it is saying, but it is allowed to be null regardless. Okay. So that's really it for setting up a one to one relationship. Once again, the scenario for that would be when you know you only want an entity to be associated one time with another entity. And there are different scenarios when you have one to one relationships. It could also be that one is entirely dependent on another, meaning, it could be a scenario where we only want to have a coach in the system when he's associated with a team. Outside of being associated with a team, he shouldn't be in the database. So you do have that scenario also, but once again, your business rules and your requirements will drive the decisions you make during your design. 22. Generate New Entity Diagram: Hey, guys, welcome back. This is a quick video. I just want to show you how you can update your database diagram. So we've been making quite a few changes. We've added new tables, added new relationships, and I just want to show you how you can update the visual representation of your database. And it's pretty simple. The same way it did the first time, you just repeat that step, and it will create a new diagram and change it for you. So you just right click your project. You go ahead and go to FCR Power Tools, add DB context diagram. It will just go ahead and generate a brand new one for you, and you will see it here. So we have our new entities in the form of coach, and you'll notice that the arrows are showing you the cardinality of these relationships, so you can tell off the bat that this is being seen as a one to one by FCR. This is a one too many, and this is also a one too many. You'll also notice that the navigation properties have been updated where a match has an away team and a home team. However, team has lists of away matches. And of course, if you hover over any one of these blocks or properties, they will show you what category is navigation collection, dependent on match, and its type is list of match. So this is a great reference diagram to give somebody who might not necessarily understand or want to comb through each class to see exactly what's happening. This nice overview diagram is a great way to get up to speed with what reference is what and how these relationships are formed. 23. Inserting Related Data: Hey, guys, welcome back in this lesson. We will be going through some examples of when we will need to add records that have relationships. So earlier when we were looking at our insert scenarios, we actually looked at one and I've repeated it below, which is to add new teams with league. In that scenario, we had a league that didn't yet exist, and then we had a team, which also didn't yet exist, hence the create, right? And then we added this team while passing in the league object. And then we noticed that when we called add and save changes, what happened is that it created the league that didn't yet exist, and then it automatically inserted that foreign key relationship with the team. So you can revisit that insert video and review what happened when we did this operation. Now, I have a few other scenarios that we would want to go through. One is when we add a new team with the league ID. And I think this one is probably more indicative scenario of what would happen in a software situation. Now, when we're adding a new team, add new team with league ID, this is probably what's going to happen maybe from a web interface, right? Somebody is typing in a team name, and then they would probably indicate which league this team belongs to through a dropdown list. So you would have this scenario if you required the user to enter both at the same time, enter the team name and the league name. Then, yes, you could go ahead and manually create the objects and then do one save changes and both get committed. Another more practical scenario, though would be that you have the list of leagues and when they select from that list, they would have you know, sent over the ID of the league that was selected as well as the name of the team that they're entering. So, we already have some leagues in our database. Let me just go and fetch them quickly. So we can easily simulate what IDs would be the potential IDs for a user to select from. So let's say they wanted to add a new Serre football team. Then our code would look something more like this. We wouldn't have to create that league object, but we would create the team, and then we would pass in the league ID of eight. So in this scenario, the league already exists. We know it's ID. We're adding it to the team. We're adding the name to this brand new team, and this team is going to Fiorentina, and then we can go ahead and add it, and then that relationship will be strong. Now, one of the benefits of having relationships and well, relational databases are designed to kind of enforce this consistency in its data. We lessen the risk drastically if it even still exists of having a league that doesn't exist being associated with a team. So I already looked at the fact that we do a cascade delete. If we delete the league, then all the teams would get deleted, right? But then I cannot add a team that is related to league with ID 50 when we saw that eight was the max. CO, well, the database itself will reject that COR will attempt it, the database will give an error and then it bounces it back. So that's one of the benefits of having this kind of strong referential integrity enforced in your tables. There are people who don't like it, but you're using a relational database. Use it to your advantage. All right, so that scenario one. When Orwell two, scenario one was when we could just put the whole object in and add both and the relationship would have been created or that relating data would have been created in the background. Our other scenario, which is more practical would be when we get the ID of the related record and then we pass it up into the record that we intend to create. Now, our next scenario would be when we want to add a new league with teams. Once again, that might be a scenario where you're creating the league and, you know, you give the user the opportunity to add a new league add all teams and then they submit one time. So that means in this situation, you need to create the object for the league and you need to tell the teams that it has, and much like how the ad context would have added both objects new and then set up the relational values, it would be the same scenario here. So I've already done that function where I'm adding a new league with teams, and then a R teams is equal to a new list of teams. So let's just say that this is what the user has submitted, as well as the name for this new league. So you can tell that I copy and paste it, right? So This new league is CFA, short for man Island Football Association. Let's work with that. And these are some of the teams that would be in CFA. So when we add this new league, which is an object of type league and it has its name and its list of teams, entity framework will do the rest when we save changes. In this next scenario, we're looking at creating records for our many to many tables. So remember that matches represents our main too many table where many teams are going to play against many teams, right? So this function simply defines a list of type match, and it has a few matches with the Away team ID, home team ID, and the date of the match. All right? Now, I'm kind of doing a two for one here because we're looking at one how to create the record for the many to many. And remember that our match or our many many table generally would have the ID as well as the navigation property. So the same way that we could put in the object for the navigation property and have it added to the database, it's the same way here. But in a more practical scenario, these teams would already exist by the time we're adding a match. So what we need to do is make sure that we're referencing the IDs properly. With a user interface, of course, you restrict the values that the user can enter to values that are more than likely going to be valid values to reduce any mishaps by the database trying to enter the records. All right? So that is part one of the 241 in this scenario. The next part is the fact that I'm using this add range. So Ponto, we've always just been doing ad. Right? Add represents one. We're only passing in one object when we say add or add a sync. However, when we say add range or a range a sync, then we can pass in a collection of values, and all of them will just be added once we save changes. So, traditionally, in older versions, you'll probably put this in a for each loop and for each one in the list, you add add add add then save changes. Now, Now, in our final scenario, there's nothing really special happening here. We already know how to add one record. And the fact that we have a one to one relationship in this scenario does not change the fact that it's the same bit of code to just add one record. So we have a coach and this coach's name is Joseph Marino, and he is going to be coaching team ID three. Now, remember that this is actually a leable field. So let us say we had two coaches and one did not have a team. So I'm going to say Antonio Conte, and this coach does not have a team, right? There's the same way that I can add Joseph Marino with the team ID, I can add Conte without the team. The only thing is that he will kind of be orphan he'll exist in the table, but he has no relationship to a team. So as soon as he gets hired, then we simply update his record and he'll have that related record for the team. So as it stands, if I execute this We see both coaches being entered with no errors anywhere. Antonio without his team ID, as well as Joseph Marino with his own. So I had commented the other functions. Let me uncomment, and then we're going to just execute all of them and see exactly what's happening in the code. So let's try that again. All right, so everything has executed without any Earth. So we see we're adding Bundesliga at the top. We're adding Barn Munich, we're adding Florentina, we're adding CFA. So you can see that even when we clumped the objects, the FCR, once again, knew intelligently, which one is the dependency, Insert that get its scope identity, and then use that to insert whatever else is appended to it. So here we see we're adding Rivoli United alongside CFA. So CFA has an ID of 11, and that's why Rivoli and Waterhouse both have 11. That scenario was we're adding one league with the list of teams. For the one with the matches where we added range, you see that it just went through and added each one individually. You're probably wondering why didn't it just create one insert statement and do it line by line. Well, that goes back to the bulk operations. The FCT decided that unless you're dealing with a certain number of records, that batching those commands is probably not worth it. So that is why for smaller commands or smaller number of records, you'll see individual SQL statements being executed for them. So that's really it for how we can go about inserting related data. Once again, this might seem unintuitive because we're hard coding the data and we're using a console application. But in a web application scenario, just remember that you would have given your user form, which would limit or allow you to restrict them to insert only data values that you know you need to carry out the save operations. So when they fill out those forms and submit, you would extract that data. If it is that they were creating a new league with a list of teams, well, we see how we can accomplish that quite easily. Right? These are just guidelines as to what can happen in the back end, once you have the data that the form or the user rather would have submitted via form, you know how to build it and then send it over to FCR and let FCR do the rest. Okay 24. (Eager Loading) Including Related Data: Hey, guys, welcome back in this lesson, we're going to be looking at how we can retrieve data from multiple tables using one command. Now, a scenario for that would be if you have a report or some display of data that you need to accomplish, but then the data that you need to display is spread across multiple tables. Now, one, this is where referential integrity comes in because you know that you can always go and get the related data from another table. But then if you're familiar with traditional qua, you would also know that you have to do either some form of join. It could be right, it could be left, it could be ineric but you have to do some form of join in that qua query to retrieve the related data. It is not very different from what we would have seen with our simple select queries, except we're going to be looking at a few more things. So I have the simple select query here for reference, and we know that we execute with that to list, and that is how we get our data. Let's take a look at some scenarios that I have put together to see how we can play around with the whole concept of including related data or eager loading. So firstly, we're going to look at how we can get many related records. And the scenario here is that what if we wanted to get all the leagues and all the teams? And just think about displaying this data to a user, right? You have the list of leagues and maybe when you click on the league, you see the teams. All right? So you want to get back all the leagues and all the teams related to them, maybe all in one call, for whatever reason, your scenario may determine why you would need to write this kind of core. There's no problem. Entity Framework Core allows us to do it all. So what we would do is just like a simple select, we're going to say leagues is equal to, and then we await our context, which is going to call our leagues, and then we're going to simply say to list. So that would be what we do to get the leagues. Now, we want the teams that are associated with the leagues, right? So before our to list, we have another function that we can use called include. Include allows us to put in a Lambda expression. So you see how Lambda expressions work. No, you see that they're not unique to filters and so on. There are certain functions that use Lambda expressions. And you can always tell a Lambda expression is going to be used based on the data type, which is expression funk, and then you'd see the league or the object of whichever table you're on. All right? So we're basically saying, What do you want me to include? That's what this is asking right now, and I want to include Q dot, and then I would say teams. So right off the bat, we're going to see this quarry being run to give us back basically select star from the teams with the inner joins on the team IDs matching the league ID or the league ID or in the team table, matching the league ID from the league table. So let's just quickly take a look at that generated SQL statement. And there we see select, and then it lists out all the columns from both tables. So leagues is L, and teams is T, so it selects all of the columns between L and T, and then it's going to left join teams on the league ID matching the team league ID. So because of that referential integrity that we have enforced, F C clearly knows how to formulate that query to know which columns should map to each other in that left join. Notice it's using a left join. That means that if there's a league in the database that has no teams because we're querying the league table, it's going to bring back that league. However, the team's object will be. So let me just put a break point there at the end of that execution. So you can see what that will look like. So this is the data coming back in that league object. And if I expand, then you'll see ID two, Red stra Premier League and the teams that are in that Reds Premier League and all of the details. So this team has no coach. It has the ID seven. You see that it is related to League with ID two, and you can get the name. So right there, you can say league object dot teams dot and it just access anything you want from the leagues object. So that's the power of our eager loading. So we have some other examples that we want to go through just to show you how you can mix and match and the different things you can do based on your situation. So, we looked at getting many records with their many related records, right? So we're getting all leagues and the list of teams per league. That might not be the case. Maybe you only want to get one record and a related record or, you know, a list of related records, but only one record. So in this situation, we want to get one team and the coach details. So for this one, I'm going to say our team is equal to, and we await or context. As it calls our methods here. So if you look at it context dot teams do include. But then as we established, even when we say context dot teams, it does nothing until we put on that executing command. Now, I did say I only wanted one. And if you remember, the way to get one would be either single or default or first or default. In this case, I'm going to put first or default. So I want team with the ID, let's say, two, and the coach for the team with the ID two. Or let's say three. I think I put in the coach with ID three. We can just go back and check. So team with ID three. There we go. So I'm going to say dot include then put on my executing command, which is, in this case, first star default because that is the executing. I only want one. So first star default takes the Lambda expression where I'm going to specify that I want the team with ID being equal to. Three. All right. So that's how you chain these commands along. I'm just breaking the line so you can see where each function really starts, right? So context dot teams, get me all the teams. Please include the coach, but then I only want the first or default one where the ID is equivalent to three. So let's take a look at what we get when we execute that one. All right, so Q that gets generated is pretty straightforward. Select top, and we see the same kind of join that's going on. So because of the first default, we're selecting the top one, and then we have that were clause to filter down to where the team ID must be three. So I hope you're seeing the common theme going through. So I'm back in the code or in the watch window. And you see here that coach, the navigation property has all the details of the coach. So that is the power of our include. And one thing I want to point out, which is something that can be frustrated if you don't realize what's happening. Where you place the first or default has a lot to do with if the statement will work or not. It will just give you an error. So if I put context dot teams then first or default, I cannot do an include after a first or default because a first or default really turns this into an object of type team, and then all I can access are the properties. Because I'm using the A sync that is not being shown, clearly, so let me take off the ASIC and show you. That when I say dot, I'm really just getting the properties, home matches ID, right? So if that's what coming if that's what's coming after first or default, then clearly include is not in this list. And so we end up with that syntax error at that point. Now, in my early days, I did not appreciate this much, and I used to think that entity framework was buggy, but really and truly the order matters, right? So you want to do all of the database related things, and then you leave your executing statement for last. All right. So once again, we're going to go get the teams, include the coach, and then first or default, and those red lines are because I need the A sync, and there we go. Alright, guys. So for our next scenario, we're going to be looking at grandchildren inclusion. So when we talk about grandchildren, it's just a matter of the hierarchy. The first table that we're querying, let's say that's the parent. Then the first table or the next table that we include is the child. But then we can have multiple includes. So all the children are ones with direct foreign key relationships to the main table being queried. So those are children. So you can have multiple includes for just the children. However, there might come a time where you need additional data from the child, and then you'll have to look into another table that is related to that child. So in this situation, we're going to have to get team then matches. And then when we get the matches, you know, we either get home, or away matches. But then we need the details of the opposing team. So if I'm the home team, I need the details of the away team. If I'm the away team, I need the details of the home team. So I'm going to have to go into the grandchildren because matches will have the navigation properties for the home team as well as their away team. So once again, we leave our executing method for last, and we do all of our includes before we call that final part of it. So I'm going to start off with an include four, four our away matches. All right. So I am team number one. We want to see all the AA matches. And then we can see that. But then when we include the Away matches, all we're seeing are the details for the very team that we got, but I don't know anything other than the ID of the home team. So I can chain and say, then include then include gives me access to the properties of the child. So include gave me access to the navigation property of the table or the object that represents the table. I gave me access to that navigation property. Now, I want to include a navigation property in that child. So I can say then include and use the Lambda expression once again and say Q dot, and I can look for the home team. There we go. So I have Day matches and from the Away matches, which is of type match. I want to include details on the home team. Okay. On the flip side, and I did say earlier that all the children can be included side by side. So I do have this include, and then I have this then include, right? So, what I'm going to do, I don't want to confuse the issue. Let me show you multiple includes for multiple navigation properties before I show you the grandchildren. So we just saw an example of the grandchild, right? But I'm just taking a step back and showing you that you can say include as many times as you need to include directly related properties. So I can say, get me the teams and for every team you get me get their way matches and all their home matches. Once again, though, when it's a home match when it's an away match, I need the details of the home team. When it's a home match, I need the details of the Away team. So then I can say, after you include the home matches, I want you to include the details of the home team. And the same way, when it's a home match, I want you to then include the details of the Away team. And then we can call our executing. So whether we call it first or default or to list, but the point is that is our query to get our grandchildren. And then the thing is you can actually chain this across because, you know, based on your database setup, you may have multiple tables with children upon children or foreign keys upon foreign keys. As long as you have a foreign key involved, you can say then include, you can include. But then remember that the include allows you to directly reference the foreign keys related to the main table. And then for every include, you can say, then include, and then you can continue to then include and include. And notice that they then include does not stop me from doing an include afterwards, because even if I did all of that in one line, it's a little less readable, which is why I broke it into two lines. But you see I'm saying, get me the teams. Include the way matches. Then include this. Then I'm going back to include. But obviously, the then include can only follow the include because I can't then include after a table that doesn't have the navigation property I'm looking for and expect to find it, right? So I include the way matches, then I say, when you get the way matches, then include the home team for the way matches objects. Then I go ahead and say, also include pretty much the home matches. And when you include that, I want you to then include the Away team details. After you've formulated all of that, I only want the one with ID of one. So let us take that for a spin and see what we get. So firstly, let us pay attention to the SQL being generated. We have all the tables that we're getting. So you see, select T zero, T four, T two, all of those are there. And then we have from, but then it's using a sub query. So we query the teams where the ID is equal to one, and we call that t zero. Then we left join that on, and then we're selecting from matches, and then we're interjoining it back onto teams. Right? So the complexity of the SQL will vary based on how the relationships are really set up. In this situation, it's almost like a circular reference because I'm looking at one team, and then I'm saying, you know, when I'm looking at matches, please go back and bring back the team. So it's kind of going back to the team table, which it just queried. But that's just a situation that we have to work with when we're doing this kind of thing. All right? But then once again, you don't have to hurt your head over trying to work this out because FCO generated it for you. Now, what it returns is the team with matches and opponents, and that should have been team apologies. But then we have the AA matches, which is only one. So this team with ID one, which is Juvento only has one AA match and one home match. Okay, fine. The Away match, we know the way team. That's implied because we included the Way matches. So we know we are the Away team. Jovents is the way team. We don't need that one. However, we did need the details of the home team, and we got that. So here we see that it is InterMilan. And I think Jvento stands a good chance because they don't even have a coach, right? So then Javent may just win that away match. If we look at the home matches, it's the same situation. We are the home team. This is the home matches. So obviously Javent is the home team. But if we look at the Away team, we see that the Away team is Asma, and the coach is null, but then if we had included the coach. So what if we wanted to include the coach alongside this? So that's another let's just do that one. So what if for every team that we're including, we want to see who the coach is. We want the coach details. So I can say, once again, do then include So when you include the home team, I want you to then include Q, and then you see it's just going down the chain and giving me the navigation properties according to what is next in line. So I can just say then include the coach. And then this will bring back the details of that team's coach. So let me just do that again. And we're in the middle of looking at the home matches, so we see that we are the home team. A A team is A Roma, and the coach for ASRma is Joseph Moreno. So that's a good coach. We're going to have to bring out our A game to win that match at home. So that is how you can go ahead and chain your includes to make sure that you're getting all the data from all the related tables. And this is another strength of having relational integrity properly implemented and working with FQ. Of course is trying to make your life as easy as possible. So this just cut down a whole bunch of joins and inner joins for you because if we look at that SQL, it just got a bit bigger because now we have to inner join or left join the coach on the table. I notice that it's saying inner form and left for sum. So it's automatically knowing that if it's nullable, then it's a left join because that means there might not be anything on that side of the table. However, when it is not nullable, it will just do an inner because it knows that they have to be an absolute match for me to bring back something. I had these two other examples earmarked, but I think we kind of exhausted them all with these three because these three are quite comprehensive in terms of what they are depicting that you can do when it comes to includes. This one says with filters, we already filtered here. But the main idea behind showing you one with filters would be that if I was to say get all teams with home matches. So it's a simple of query. We await context dot teams, and I'm going to add the where clause for my filter, and then here I'm just saying home matches, which we know is a list dot count is greater than zero. So that means, get me all the teams that have at least one home match, and I want to include their coach. And of course, it's A. So that is the SQL statement that gets generated for us. You can sit down and take a look at it, of course, if you need to. But then we see that we have three teams coming back that have no home matches scheduled. So you might be tempted when you look at the data, you might be tempted to say, Well, why didn't we just check if home matches is null. And I mean, I would understand because we're saying home matches.com is greater than zero. What if I had said, ho much is equivalent to null, since that is the data that we see, but how exactly would FCO be able to translate this list object into null? Because remember that SQL doesn't know anything about lists and lists being null. So if we look at the SQL statement being generated for this, well, firstly, there is no data coming back, right? So that query, whatever query is generated, is not bringing back data that we expected. No, if we look at the SQL query that was generated, we see it's a regular select query with the left join, but then this filter is just throwing it all the way up. So that's of course, way of saying, I can't I can't make out what you want me to compare in this situation, right? So, while we may not have gotten a syntax error or any form of warning, we just want to be careful when we're adding our filters, and, you know, we might get a little over zealous with how we're doing it, and that's fine. It's good to experiment, but just be careful. So that's really for us exploring how to query related records. And as usual, I'm going to just leave that method there so you can reference it later on. I'll just clean up what we didn't go through, and you have those examples to review afterwards. 25. Projections and Anonymous Data Types: Hey, guys, welcome back in this lesson. We'll be taking a look at projections and anonymous data types. Now, you're probably looking at this topic and wondering, okay, what exactly are we talking about here? A case study for this would be that you have all of your models sure, but then you want specific data from a query, and even more so that is the only bit of data you want to return. All right? So you want to have a custom object with only bits of data from all of the dataset being returned, and that is all you really want to tax it across in your system. So let us go through some examples of, you know, when you would probably need to do something like this. All right, I'm going to show you three scenarios when you need to know how to handle a select. The first one is when we want to select one property. Like we said, maybe, we're querying the entire team table. So let's say teams. And whether it's one team or many teams, the principle would remain fairly the same, where we have to say context, dot and get the teams. And then let's work with a list in this situation, right, which of course we have to await. No, I don't want everything out of the team, because we know that what we're going to be getting is a list of objects with name, league ID. And then if we include, then the other things, right? I don't want all of that. What if all I wanted were the names? I just wanted the list of names. I didn't want any complex objects or anything else, right? So in that scenario, what I would have to do is add a select. So I would say context do teams dot select And then when I do that, I can use my Lambda expression to specify which property I would want to select. So if I select name and then execute the two list, then this is just a list of string. All right. Because name is a string, and I said I want the list of all the names of the team. So I get the list of type string. And that goes for pretty much anyone that you do. So if I wanted all the league IDs from the team table, then I would be getting a list of integers k on disappearing. There we go. A list of integers because league ID is an integer. And that's just how it would go for anything. List of coaches. All right. So that is how you go about selecting one property. Now, the scenario might be that you don't only want one property. You want multiple properties, and even more so you probably want multiple properties from multiple tables. All right. So, let us look at anonymous projection. What if I wanted to select all the teams and I wanted all the coaches included? And all I really wanted to return, however, was the list of Well, I list containing the team name and the coach name. That's all I really want. So we know from our previous experience with the includes that would end up doing something like this, let me say dot include. And then I'm going to say, make sure you give me the coach details or the coach object along with the team. But then I want to select multiple things. I can't separate this. This doesn't work. I can't say q dot. So we know in SQL when you want specific columns. All you have to do is say column, column one, come column two, et cetera, et cetera, all before the fm. That's not really an option here. So that's why we talk about projection into another data type. So on the fly, what you are allowed to do is something like new, and then you can open up a brand new object directly in that select statement. All right. And then if you hover over it here, it says it's an anonymous type. We know that C sharp is strongly typed, right? So everything in C Sharp is strongly typed. It's either string, it's an int or something. But in this situation, I'm just saying new, there's no there's nothing after the new to say a knew what, right? So it's just a new blank. But then notice there are no errors, and it is assigning it an anonymous type. So it doesn't know what data type it is. It just knows that it's some type that I want to define, and it has a property of type string called name. So it's already inheriting the name given. By the field. So in this situation, Q represents the team. So if I wanted to be specific, I would have to say team name is equal to Q dot name. And then if I hover over it again, you see that it evolved. It says, This anonymous type has a property called team name. All right. So, what if I wanted the coach name also. So I can come or separate because no this is an object. So let me just break this out into a new line so that you can see where everything is happening, where everything starts and ends. All right. So here's our new object being defined. So we have Lambda. Then we're projecting into this new anonymous type, and we are defining on the goal, which field names it has and what values it gets. So team name gets que name, and then coach name, And don't be intimidated by the fact that what you're typing doesn't come in intelligence because once again, we're doing this on the fly. And it's just going to work with us, dot, and then can say dot coach dot name. So, there is going to have two properties, team name and coach name. If I hover over teams, then you see just a list of this anonymous data type, all right? And then we're just projecting it into a list. So then after this, I can say four each, and I'll just do a four each here to print them out, for each item in the list of teams. I can console dot right line. And print team and put item dot team name, and I'll just do a pie as a delimter coach and item dot coach name. So even after this projection and this very random and spontaneous creation of this anonymous object, we can easily access the properties that were defined. So if I modify its definition, then item loses sight of the team name was a property, right? So I guess that's as flexible as C sharp gets, and it can be very convenient to do at times. All right? Now, There is a case study for this, and it can be very useful, like I just said, but personally, I prefer to have a strongly typed projection, meaning I always know the object types that I am interacting with anytime I run a query. So like I said, there might be a case where you need a custom object, and this is good for an on the fly scenario. However, in a bigger project, you want to have a bit more control than just having these new anonymous objects all over the place. So what you would want to do is have a strongly typed class, and what I'm going to do is I'm going to create a new folder inside of domain. It shouldn't go inside of domain, but let us just work with it. I'll just call it models. By right, you'd probably want to create a new project, but this is a very small demo project. I'm not going to go to Wild West with a number of projects. But in a bigger project, you don't want to have this in a dedicated space not mixing with your domain objects. But I'm calling the models because really they're models of the data. So this is a model of the database data, but then what I'm going to be creating is a model of probably like custom data that I know I expect. So let's say a model would be a cla called Team detail. All right? So Team detail is going to have a few properties. Let's say team detail has the name of the team. It's going to have the coach's name, and it's going to have the league name. Those are three different data points than we would ever be able to get just by running a regular query. We'd have to include everything about the coach and we'd have to include everything about the league name. And then it can get kind of annoying on the receiving when we have to say team coach teams. So you want to just have one object where everything is just there for you. So that's why I call it a model. All right? So we can project into this strongly typed query. So I'm just going to copy this initial query. Let me just state the entire thing. And I'm going to add and include for the team. So remember, it's your query, whatever data you need, you go get it. So I need team sorry, the league team. So we're looking at the team table and we're getting the coach details included, and we're getting the league details included. And then I'm going to select into a new instance of team detail. So we were anonymous up top, but now we know what we're all about below. Team detail has name. It also has coach name, and then it has league name. I'm going to say Q league dot name. Of the bat, when we look at what is being returned, we know for sure that we're getting objects of type team detail. So we don't have to guess and spell and say, what data type can I expect this time. Or we don't have to necessarily go to the definition to see what were all the fields in the anonymous type at the time that developer did it because when we have a strongly typed class, we can always just look at this class definition. And once we know that we're getting this data type, then we just know how to work with it off the bat. All right. So I'm just going to wire all of these up and then we're going to take a look at the different QO statements being executed for each and what data is coming back. All right. So looking in our console, we can just go through this slowly. Our first command, which was selecting one property. We see that it's saying select T name from the team. All right? Straightforward. You want one property or one column. That's how it's done. No problem. In the next one, we said that we wanted name and the name of the team and the name of the coach. All we did was to say, select into this anonymous type, and we specified the columns that we wanted. FCR generated exactly the statement it needed to get exactly that data. So it selected t name as team name, which is the alias, right? So we called the field name differently inside of our or anonymous type. Well, it gave the alias to the column inside of the SQL, and then it's left join accordingly. We're familiar with the include already. So I'm just really pointing out the targeting of the columns that we want. And then these are the teams that came back. We see Juventos has Conte, Roma has Joseph Marina, and all the others are blank. That's fine. Now, for the next one with a strongly typed projection, it's pretty much the same thing. We're selecting T name, C name as coach name, and led that name as league name. So notice I said name, name, right? So it didn't have to give this one an alias because that's already the name of the column. So the SQL didn't give it an alias, but then the alias was in place for the other two, and then it went ahead and did the joints. And then when we printed it out, we see team name, coach and Syria, Syria, and we're seeing all the league details. So here we're targeting the columns that we want. So it's going to be a much smaller query, a much smaller payload than just getting every team and every detail of every include to just bring back one time. It is just going to say, what are the fields that I am selecting or I'm interested in. Let me get those and then bring them back as a list. So once again, while this is a good facility, in a project, especially when you're working with others, I would recommend that you stick to the strongly typed models. You maybe make a different model per type of data you'd want to display on the page so that when you run the query, you extract it directly into that model, and that page is modeled off that data. And if you need to extend it to you simply extend your model and extend your query accordingly. 26. Filtering on Related Records: Alright, guys, so we're back, and we're looking at another topic, and this is going to be a fairly short lesson where we're going to be talking about filtering with related data. Now, what is a scenario for filtering with related data? I have a query here, and my variable name is wrong, so let me correct it to leagues. And I am going to query the leagues table, but I am querying the league's table on something that the team might have. So think of a scenario where you're showing the leagues, you're showing the user the list of leagues, but then you allow them to filter based on the team name. So I know the team name or part of a team name, and I want to see which league this team is in. So that means when I click submit, you need to go and get me the list of leagues where there might be any team that has a name containing whatever search term. So just imagine that this was a search term coming in from the user, by short for Barn Munich, maybe. And then we're saying here. So let me just take the time to rewrite this query just so it doesn't look as as intimidating as it probably does right. Now, we say it context dot legs where. We are all familiar with that. We know that we have the Lambda expression. And then I'm going to say where some property meets some criterion. In this case, the criterion is against the navigation property, which is teams. So I'm going to say Q dot teams And then it's a list. So I can't very well just say teams that name. It's a list of teams. So I'm going to use any because any returns a boolean based on some condition. So I'm going to say because I'm already inside of a Lambda expression, I can't re use the same Lambda token. So Q here is already tied up representing a league record. So I can't use Q in the again. So that's why I have the X. It could be S. Once again, the Lambda expression, the token doesn't really matter, but I'm just showing you why I have X in one and Q in the other because this one is a subset of the larger Lambda expression. So x dot and then no I can access the properties of the team table. So I'm going to say where the name, and then we know the dot contains already, and then the search term, and then we have our executing statement. So that's really all there is to query against related records, right? So once again, teams is not the main table. This is the main table. However, operation requires us going into the child table to match some condition. So if we look at this, our SQL statement that gets generated is just going to give us the league in for where exists and it's just going to try and select the team based on the search term or based on the criterion that we have specified. And then that would give us back one league, which, if I'm not mistaken, would be Bundes Llega. 27. Adding Views and Other Data Objects: You guys welcome back in this lesson, we'll be looking at how we can add SQL objects that are not tables to our database through migrations. Now, a case study for this, we have been using migrations up until this point to control most of what goes on in our database. We wouldn't necessarily want to have two separate operations, one where we're going to manually script in the reviews and another one where we're scripting in the tables. So it would be good if we just had a central, like focal point to our database so that we can always roll back and know that everything that was done in the previous migration can be undone through this particular procedure. Remember that migrations kind of act like a source control for your database. So in this particular lesson we'll be looking at how we can add a function, how we can add a And then by extension, the same technique that we're going to be using to do all of this would kind of apply to stored procedures and every type of function. I'm not going to get into the details of what the scripts are. At this point, I'm assuming that you're already familiar with what function is, the scalar function versus the table valued function, as well as how our views are constructed. So I'm just going to focus on how we get this Qule into the migration and by extension into our database. Now, as with any journey into anything with the database, it starts off with us adding a migration. So we're going to come to our package manager console, add a migration, and then I'm just going to call it adding team details view and early match function. As I always say, you want to be clear with your migration messages. Don't be vague at all. So I'll just go ahead add that migration. No, I haven't made any change to anything with any of the database related elements. I haven't changed the context. I haven't changed any of the classes. So you notice that these are going to be empty because I made a migration. I didn't say anything to do and it didn't say anything to undo. So we have to manually put in the code for it to know what to do for the up and what to do for the dom. So let's look at adding the function. So I'm going to get that functions SQL. And I'm going to say migration builder. So what would have happened really and truly is that I went into SQL and made this. I went into the Management Studio, made the function manually. That's fine. But like I said, I don't want to add it there because I want one point of entry to all data base based modifications going forward, right? So I'm going to say migration builder dot, and then I can say SQL. Which is going to take a parameter of type string. So it's expecting the cual command as type string right there. So what I'm going to do is use my a sign to turn this string into a literal string, and then I just paste that qual statement right in there. So that is what that is going to look like. So migration builder Q and then you pass in any Q. So although it's Q to create a function. It's Q to create a view like we're about to do or a stored procedure, whatever it is, that's all you really need to do. So I'm just going to copy that, and I'm going to repeat that step with the view. So in this particular migration, I expect to create that function and create this view. Now, I did say that and I said this from before, so we should be familiar with the fact that the up means the change I'm about to make. The down means the changes that I undo or the things that I do whenever this migration is being rolled back. So if I have the create statements inside of the up, that means I need to put the drop statements inside the down. So I'll just do that migration builder dot SQL drop view, and then I give it the name and then drop function with that name. So this is our first time getting our hands, you know, dirt here inside of the migration files. Let's go ahead and do an update database and watch the magic happen. And my experience was not very magical because I have an error. And as I'm looking at it, I see that I have an error in that line. So let me just correct it. I had gotten overzealous and repeated the word matches. So let's try that again. You probably detected it and didn't get that error that's perfect. And at that point, everything is done. And if we look in our database real quickly and look at our views, then you'll see the view appearing there as well as on the programmability and functions and the scalar functions there we have our function appearing. So this migration was a success. Now, when we come back, we're going to review how we can interact with a view. Later on we'll look at how we interact with functions and other scalar operations where we make a call and expect data, but then with a view, it's not really a command, we're querying, and it's going to be slightly different from how we query our tables, and there are certain rules that we have to know about. So when we come back, we'll look at the modifications needed for that. 28. Querying Keyless Entities (Like Views): Hey, guys, welcome back. In this lesson, we are going to be building on what we did in our previous episode where we created two non table qual objects in the form of a function and a view. So we're going to continue with interacting with the view because a view in practical terms is really like a read only table from the qual side. So that means we would like our application to be able to query those views similar to how we can query regular tables. So what we're going to have to do is create a new data class that corresponds with the view and add it to the DB set. So I've already kind of done this where I created a new class. I put it in the domain project. I call it teams, coaches, leagues, and I just appended the word view so that you know, at first glance, we can tell, this one is a view versus the others. Whatever prefix postfix, that's up to you. I'm not being prescriptive. I'm just saying that this is my convention to know the tables different from the views. All right? You could also create a whole new folder and put in a call it view, sorry, and put all of the view related models in there. However, you want to separate them, that's entirely up to you as long as it's clean and easily identifiable. Now, after creating that class to represent the view and the data going back from the view, what we want to do is let the DB context know about this view. So I'm going to add a new line in this DB context where I'm going to say DB set, give it the data type, and I'm going to just call it the same name as the view in the database. In our program dot CS, we're going to follow suit with what we've been doing up until now, and I just created a method query review, which is just going to have the sole purpose of calling context, the teams, coaches, leagues, and putting it to list. However, we're going to see if we will really get back results and if we will get any errors along the way. So let us take this for a spin. Now, as soon as it hits the code, I'm greeted with this exception, and it's saying the entity type requires a primary key to be defined. If you intended to use a keyless entity type, you have to explicitly you have to explicitly let it know, sorry, that it has no key, right? So that is expected, and that's an error that I wanted us to see together, because sometimes you're blindsided by that error, and you're not entirely sure why you're getting that error. So FCR, like we've seen thrives of relational integrity. Primary keys being defined as primary keys, foreign keys being defined as foreign keys. And with that, it knows exactly how to make the queries efficiently, how to track, if anything is being changed, and how to just monitor everything happening in the context during our request. So by that standard, our new table or new entity rather. It doesn't know if it's a table. It doesn't know if it's a view because we added it to the DB context, much like how we added every other table. So as far as entity framework is concerned, it's going to treat it as though it's a table. However, that exception was saying, I don't see a key on this table. We can't put a key on it. It's not a table. It's a view, and yes, it has no primary key. So the exception that in the model builder, we have to let it know that it has no key. So I have to say model builder dot entity with the data type, and then we just specify has no key. So when it's creating it knows that, all right, I'm aware that I shouldn't try to track this. If I find a table or anything that matches this, then I know exactly what to do. Now, another thing that we would want to do at this point is say two view and two view basically allows us to specify the name of the view in the database that it should look for. So this is like an extra precaution to make sure that it doesn't see any of this as any new elements or car subjects or new work, it has to do the next time we do a migration. I'll just know that, Okay, well, this data type, this class type, or this model directly maps to the view in our database by that name. And it has no key, so don't do any tracking. So what I'm going to do is execute that a bit of code, again, and I'm going to leave that break point so we can look at what's happening in the change tracker. So this time it hits the breakpoint. When we look in details, we would see we're getting back details because the view is executing correctly, and we're getting back coach, we're getting the league, and we're getting the name as we expect. And then the context really doesn't know anything about anything to track, right? As far as it's concerned, it did its job and it's done, and it's moving on with life. So that is how we handle situations where there's no primary key present because you might you might end up with a table without a primary key or an ID element like that. Perhaps you're forced to deal with a legacy database where those things aren't being enforced, like how we are encouraging you to enforce them when you're using entity framework core from scratch, because those situations may exist. But then when you have that has no key, battle to fight, then this is the remedy. You put it in the model builder as soon as I can find a context, you put it in the model builder, let it know it has no key, and then you can directly map to the view, or if it's a table, you could actually say dot to table and specify the name. So there might be times when you have a mismatch between the table name given in the DB context and the actual table, you can always say to table and then give it that name, much like we did to view. Okay. 29. Querying with Raw SQL: Hey, guys, welcome back in this lesson. We'll be looking at how we can run queries using raw SQL. Now, up until now, we've been writing everything using our link, syntax, and our C sharp and that's all been perfect. But there might just be a situation where you need to write some raw QL, and especially now that we're dealing with non table related objects, and we start to see that it becomes a bit more complicated. You know, how do you execute that stored procedure or that function? How exactly do we do this, but maintaining the whole use of entity framework core. So here we're going to look at two examples as we work our way through these different scenarios. Now, we have two functions that allow us to execute raw cual commands, and that's from Q R and from Q interpolated. Now, note the syntax, it context do teams. So we still have to specify our table, and then we say from QL R, then we can put in our raw cual statement and then to list. Now, I must point out some of the limitations here and the dangers associated with this raw QL. Once you are using this function, you actually open up yourself to the potential of SQL injection if you're not being very careful. So that's why they gave you from QL raw and from SQL interpolated, right? So we'll see the difference in a few moments. So let's look at what we get back with from Q R. I just comment out the one that we're not using just yet. And from SQL raw, we have to say something like select star from teams kind of counter intuitive, right? We just said context do teams, and then I have to say select star from teams again. But let's see what we get back when we test this. Now when we check our results, we see that we do get back the list of teams and we're getting back every single thing. We did say select star, so we are getting them back and everything is all good. Now the thing with the raw SQL is that it has to return columns that exactly match the DB set or the entity behind the DB set. So in other words, if I just wanted but the names of the teams and I said, select name from teams, let me keep it simple. ID not simple. Let me use ID common name. I only want two columns from the teams table. If I attempt that, then we end up with this exception saying that there are certain columns not represented in the query set because it tried to query only two columns, but it's expecting. So that's one of the limitations with this raw SQL command. So, number one, the query or the result set must return all the properties of the entity type. Another thing is that the column names must match. So even if I was to list all the column names, I can't give them aliases that don't map back to the original entity type in our class called team. And then another thing is that we can't inter join, can't directly have related data. However, I can, at this point, say, include Okay. So after writing out the raw SQL, I can still do my include statements, and it would work just like how we know that it will work regardless, right? So if I do that and include the coach, then we'll see for our teams, and I think team number three has a coach and there we go, we'll see the coach entity being included in the form of Joseph Moreno. All right. So that is one of the things that you can do. If you ever need to run this from EQ raw and need related data, just know that you can do include just the same way. Now, you're already familiar with SQL at this point. So you know that if we wanted to add a filter, for instance, if I wanted to find the football club where the name is equal to this variable, and we've done it in the past where we actually accepted user input and then use that user input for the filter, right? So let's just say that we want to use this variable for our filtering. We can always just say, where name is equal to, and then we pass in our variable. So interpolation would have us put this dollar sign before the string, and then we can just do our curly braces and then place name in that area. Now, if we take a look at the qL that gets generated for that, we will see which we didn't necessarily look at the last two times That it is generating our SQL statement, and it's doing the left join automatically because of the includes. But then take note of this now. What we're doing here is passing in select star from teams where name is equal to. And notice that it is passing in the value, but it's not passing it in with quotation marks, because in SQL, we would have had to put where name is equal to. Open maybe single the value and close single quote when we're dealing with a string. As a result, it is complaining that it cannot find the keyword as because A S roma is being seen as AS command in ques. So to remedy that, in our interpolated string we have to put our single quotation marks around the values. So let's try that again. And this time we are hitting our breakpoints. Our query was successful. But once again, if you look at this query compared to previous queries where we did filtering, You notice that there are no parameters. It's passing the literal value directly into the query, A K a bad practice, QL injection. All right? So if you ever have to end up taking some parameter and using a raw QL command to execute that, then this is what's going to happen, and you have to be very careful because if somebody passes in some malicious command as their input, then you would have gone back to square one before the days that entity framework was developed to help you prevent Qual injection. In a situation like we employ the services of its close cousin from qual interpolated. If you look at this one, it's asking for a formatable string. From qual is only asking for a string, but then from sc interpolated is asking for a formatable string, which means that it will only accept one interpolated string. And two, we don't have to treat this car statement as literal as we did just now because it is going to interpolate it. So it's automatically going to handle the parameterization of the command, so we don't have to put in the single coats around the interpolated or the injected value into the string. So let's take a look at that qual and compare them. So you see, we looked at this one just now, where it passed in the literal value. Whereas the second one is actually going to say select star from teams where name is equal to parameter zero, and it has defined the parameter up top. And that is the difference between from raw from scle raw and from escule interpolated. So I would always recommend that if you have to mix and match with variables to pass in a raw cual statement, use the qua interpolated for your own protection and peace of mind. 30. Add and Query Using Stored Procedures: Hey, guys, welcome back. In this lesson, we'll be taking a look at interacting with stored procedures using entity framework core. Now, the case study for why you would need to interact with a stored procedure comes from the fact that maybe you're dealing with legacy systems where a lot of the logic is in stored procedures in the database. So in the rebuild, you don't want to double the work, rewrite them, you just reuse them. So in that situation, you probably need to just know how to make stored procedure calls from FCR. So I have a very simple one that I've constructed for the context of or database where I just have SPG team coach, and it gets a team ID, and then it returns from everything from the coach's table. Now remember that when we're dealing with RwQUL we have to return everything that corresponds with the matching entity type. All right, so you can't just say name. You have to if you're going to list them out, you have to list them out in the order of the column names with the same column names, no Ss and anything. So like we did with our views and functions, the first thing that we're going to do is create a new migration. I added a migration saying added SPG coach name. I messed up the name of the migration, but we'll continue. And then I put in the migration builder to create the procedure and similarly to drop afterwards. So you can go ahead and do the migration and do those steps, and then we can just update our database. And then that allows us to start interacting with our stored procedure. Now, in our program CS file. I've already gone ahead and written some sample code, and the new method that we're working with is stored procedure. So in store procedure, I just hard coded a team ID, and then I'm going to pass it into the raw SQL statement and await the result. Now, let's take a look at the syntax. I say await context dot coaches do fc R then I call my execution to get me SPG team coach, and then I have a placeholder. Now note that I'm using from SQULRw In the previous lesson, I stopped short of condemning it saying that it was bad because it opens you up for cual injection. Reality is that it's based on how you use it. So I showed you the bad way to use it initially where I used an interpolated string and pass valid directly in. So all of this got translated into literal QL, which is the worst thing that you can have between user input and going to the database. Parameterization is always best, hence our preference for the interpolated. But then let's take a step back and not condemn from SQL raw too much because if we take a look at its overload, it really says, give me the string SQL and then give me a list of parameters. So that is exactly what we've done here. I say DBO dot SP get team coach, and then I use a placeholder. And then after that placeholder after that string, I have now included the parameter. So when you do it in this manner, it will actually handle the parameterization for you just the same way that we saw from Equal Interpolateed do it. So it's not the worst method and it's not completely usess because you probably left the last lesson wondering. So why did they put it if it's so dangerous? There's a good way to use it and there's a bad way to use it. So, I showed you the bad way. Here is the better or more recommended way to use it. So let's take a look at that. Now, when we take a look at what we get back, we see that our query is being generated and parameterized, like we said, or expected it to be. So there it is taking that parameter of three, executing a stird procedure. And then our result is Joseph Marina's information as the team coach for team with ID three. So that is how you can execute a stored procedure. And pretty much that's how we execute commands against a database that are well, query commands. We're asking the database for something and we see getting returned. So in that situation, that's why we have to execute these raw QL commands against a table. In each situation. Now, what happens when we need to execute a stored procedure that is not associated with a table or query is something that's not directly associated with any entity type. So we need raw QL against some object that is not associated with an entity type. That's what we're going to look at in our next lesson. Okay. 31. Executing Non-Query Raw SQL: You guys will come back in this lesson, we'll be taking a look at how we can execute non query commands against the database. So an example of this would be when we want to delete or update something that manipulates or augments the data, but it doesn't necessarily return anything to us because we're not selecting. So up until now, we've been selecting and selecting and selecting. Let us look at what we do when we're not selecting, but we have to execute this kind of command. I have on screen a new migration delete team by IDP, SP short for stored procedure, and I have the code to create the stored procedure called delete team by ID, which takes a team ID as the parameter, and then it deletes the team accordingly. We have the up and down methods I can pause, replicate those, and then go ahead and update the data. Now, after you've completed that successfully, you can head over to the program dot CS and you see already created the method execute non query command. Now, when we're going to execute the non query command, there's a few things that are going to be different between this and when we know that we're expecting a result set and I'm trying to bring up both sets of code on the screen so we can make a comparative analysis. Number one, we are only going to be told how many roles have been affected. Unlike when we looked for the teams, we knew that we were getting back a result set of teams. In this situation, we're only going to get back some variable that says, number of rows affected. All right. So that's all we're getting. That's one. Two, when we were doing the other ones where we knew we were getting back data, we knew which entity type or DB set to execute it against because you wouldn't expect to be querying select star from coaches but context that team. We know that you would just get an error when it tries to execute that method altogether. However, in this situation, since we don't know what the stored procedure is going to be doing, we don't know which table it's directly interacting with. We say context dot database instead of context dot DB set name. So context dot database, and then the methods that we get are going to be slightly different. In the case of the DB set, we get from CLR and from Q interpolated. In this situation, we get database dot execute SQL raw and it has an asynchronous version to it. So we have raw and we have R A sync. Hence the wit, where is in the Async one. And then similarly, we have executes our qual interpolated Async. So we have the interpolated version and we have the raw version. Now, we already explored the bad use of the raw method and the more acceptable, the safer use of the raw method. The same thing applies here. We don't know what the stored procedure we'll be doing, but we do know that we need to pass in some value, which is more than likely going to be coming over from our user. So we want to protect ourselves and use a formatted string with a placeholder, not the interpolated string when we're using the execute Q R A sync. And then, of course, if you don't want that additional responsibility of thinking so much, no problem. That's why they gave you the alternative for you just pass in the interpolated string, and it does the same for you. So let us take a look at what we get when we execute these methods. All right. So I'm looking at the kill that has been generated, and I see here it has been parameterized with team ID two, and everything looks like what I expected. However, it's telling me that it failed. Why did it fail? That's because of that foreign key constraint that I have on the record. So I have related matches to team with ID two. So I can use that one. That's my bad data. Let me try again. All right, so I'm adjusting my values because I just want to look for some teams that I know don't have any matches. So we shouldn't have any foreign key constraint errors again. Let me try that again. And we hit the break point this time, which means that everything got executed properly. Now, if I look at affected roles LC, one. If I look down here, I see one. So it only tells you, that we executed this successfully, and this is the number of rows that got affected. So if you wanted some flag to say, was it successful or not, then you could always say, is the number of affected rows greater than one, then we can say it was successful. All right? So that is really it for executing non query QL command statements using raw QL. Okay. 32. Seeding Data: Hey, guys, we'll come back in this lesson. We're going to take a quick look at one of the roads less traveled, but essential to know about, which is how to seed data. Now, data seeding, if you're not so familiar with what I'm talking about, is the act of putting data into the database at the beginning. So as soon as your application is installed, there might be default data that you need in there, maybe like a list of countries or certain roles or like a default user, things like that, you probably just want those in the system at the time of creation. As soon as the database is created, these things must be in there. COR allows us to hardcode that so that whenever our database is being generated with this scripted or we're running the update database to get the database up with all of the migrations, we can actually put in code that will be seen as a migration so that when that migration is executed, that data is automatically put into the database from the get code. So that is what we want to take a quick look at in this lesson. Now, the simplest way to get seeding done is to do it from the model creating method. Under all of this, we can say model builder dot, and then we specify an entity. Let us say we have teams, we have leagues, we have matches, we have coaches. Let's say I wanted to seed some coaches into the system. I'm going to say entity coach and that entity coach has data, and then this has data allows me to specify As many coaches as I need. That's not ci brass that's parentheses. And then in these parentheses, I would now start giving it new coach objects. So I can say new coach. I can specify the ID from the get go. I'm going to use some IDs that I know won't clash with the existing ones. ID 20 name is equal to I'll use my name in this situation. And the team ID. Well, I can leave those as ll because I'm seating the coach. Now, that brings another important point. If you have hierarchical data, then you need to make sure that you're seating according to the order or the level of dependency, the same way that we want to create the tables with the dependencies the same way that we need to seed the data with those dependencies. Because I can't be expecting to put in Trevor Williams, the coach of Team ID five, but then I'm defining the team with ID five after all of that. All right? So let me show exactly what I mean. If I say team, I want a new team, I'm going to be seeding a new team and this team is going to have let's say ID 20. The name of the team is Trevor Williams Sample team. All right. I can't be telling this coach that its team ID is ID 20 because the order in which I have defined these seed commands is the order in which the car statement is going to be generated. So I can't be creating coach and inserting coach with team ID 20 when team ID 20 doesn't yet exist. So I have to make sure that I maintain my ordering according to the levels of dependency. No, like I said, you can put in as many as you want. So I have new coach here, and then I can just separate as many objects of new coach as I need. So if I wanted three coaches in the system initially, of course, the IDs can clash. Then let's just keep that name, sample one and sample two. And then for the teams, well, I can't be the coach for the same team over and over and over. Because we know the constraints. So whatever constraints exist on the database, of course, will govern and the limitations you have when you are doing your seating, right? You just want to be mindful of all of that. Of course, once again, this is probably when the system is just installed. So I'm using IDs 2021, and 22 because I have teams and coaches in the system already. I don't want to clash. However, in a brand new system and a brand new paradigm, you would probably start off with one, two, three, four as your IDs because those are the IDs that you definitely want in before anything else. Now, If you were to do seeding for multiple tables, you can see that it can get quite cumbersome and Honestly, I don't like seeing all of that in the model creating. That's too much code. I want to keep it a bit cleaner. So what I do or I would recommend you do in that situation is extract these into seed configuration classes. So what I tend to do is I create a folder and I'm going to do it in the data project near the migrations. So I call it configurations, and then I have another Folder in there because there might be multiple configuration types that we might want to do. I'm going to call that one entities. And then inside of that, I'm going to have the different configuration classes per entity type. So for instance, I want a coach CD configuration. Now, in this class, which I'm going to make public, I'm going to inherit from entity type configuration. So I'll just go ahead and grab that reference, and I'm going to tell it it's for the type coach. And then include anything that is missing. So for the type coach, I want that. So now I'm going to have to implement this interface, which gives me this method configure and a local builder. So now that I have this builder, this builder basically resembles the same purpose as this model builder object, right? So I can just say has data. I'm just going to take this part as for team, let me just take the has data part of it. I'm going to cut I'm going to erase all of this from the DV context. And then over here, I want to say builder, dot and then put on has data. So everything I had has data and beyond fits in perfectly in this method. So Builder dot has data, and then you can put in as many see records in this dedicated class. Now, of course, if I'm moving into a dedicated class, I need a way to make it reference or make reference to it in the DB context. So back in the DB context, I'm going to say model builder dot, apply configuration, and then new and just pass in the name of this method or sorry, this new class that we created, which is a new configuration class. So I just pass in a new instance of this, and that is it. So as many configuration classes as you create, you just need to pass in these lines. Once again, maintaining that order. So I did that for coach or coach seating. Let's do the same thing for the teams, right? So I'm just going to kind of play lazy here and just copy and paste the existing file and just change the name to team seat, configuration. And then I'm going to update the references in the file. So this is four type team. This is for team. And then our has data section is going to come directly from this. I'm just going to cut and paste over in its dedicated configuration file. And back in the DV context, I can just duplicate this and say team seed configuration, and there we go. So that looks much cleaner to me, and it is just as effective as having everything in the file. But of course, this method keeps a bit more kosher and looks a bit better when we do it like this. So let us take a look at what we get when we try to add a migration. And when I do that, I call it added default teams and coaches. I'm now seeing that the migration builder is doing something different. We've never seen it do that before. So it's always create table or alter table. Now, it's insert data into the table teams with these columns with these values, and it's doing that for everything. And once again, that order matters. A see it took care of all the teams. And then it's going to take care of all the coaches which have dependencies on the teams, right? So imagine if these were mixed up, we'll be trying to insert a coach with a team ID 21 before the team with ID 21 was even created recipe for disaster. So just remember that ordering matters. And in the D, the delete data is just reversing all of those inserts with that delete statement. So if I do an update data base, I'm going to get this error, all right? I'm getting this error saying that I have a conflict with the team and the league ID column. All right. That's fine. So in that situation, I have to modify what's happening with the team because I didn't complete the data, so you have to be aware of that. You have to be mindful of the fact that your constraints will govern if the data can go in or not. So league ID cannot be null. That is a constraint that is on the database. A team has to be in a league. So I can't be seating teams with no leagues. So I'm just going to quickly seed a sample league. So following the same steps, and I'm going to encourage you to pause and try it yourself. But what I've done is to create a new class that I'm calling league seed configuration, following the same steps inheriting from entity type configuration of type league. And then we're just building one league with the ID 20, once again, to avoid any clashes in the database, and the name is sample league. Then I retroactively go and update my team seed configuration to add that league ID to each of these teams, and by extension, the DB context where I now have that league seed configuration happening before everything else. Now, I made a mistake, all right? I generated this migration when the data was incomplete. Now we see that this migration is failing. How do we roll back? Well, the first step is to remove the migration. So that will always remove the most recent migration. And then I can just go ahead and generate it again. And this time, if we look, we see it's creating the league first, then it's going to create the teams and then the coaches. So let us take another stab at the update database. And after levels of logging and being very verbo, we see done, and we know now that we have this data. So once again, this is now a migration. So it is expected to happen along the chain from initial migration to the very last migration that you might have for your database. So there might be times when you have to introduce different lookups and different tables along the way, and you need these values in as defaults from day one. Well, this is perfect for that. You just go ahead add in the configurations, and then you can just put them in the DB context. So the model builder will know that when I'm doing my build when I'm creating the model, I need to be aware of these configurations for seating. 33. Rolling Back Migrations: Hey, guys, welcome back in this lesson, we'll take a look at rolling back and managing migrations in general. So we've done quite a few activities where we've made a few changes to our database, and with each change, we made a migration and then updated the database. But what happens when you make a migration that you don't quite want or in retrospect, you want to roll it back, make an adjustment, and redo it. Now, in this example, I've already made a migration. I'm going to show you the changes I made to trigger this migration. I changed the base domain object to have these four fields. And these fields are usually used for auditing purposes. You know, when you have people entering data into a database, you want to know when it was created when it was last modified and created by whom and modified by whom. Now typically you'd want these to be kind of clear. So I deliberately kind of name them badly, just to show you like a mistake that probably would warrant a correction or a rollback or something like that. So in this situation, if you just look at the column names, they're not really very cohesive, right? Date created last modified. If I'm just looking at that name, I don't know if this is last modified date or this is last modified by whom. I don't know if this modified means modified date or person who modified it. I don't know. You understand what I'm saying. So these are the things that if, you know, if you are obsessive with these details, then these things will kind of look ugly to you. However, The point is that I've made these adjustments to the base domain object, which, of course, is being inherited by every other domain object. So when I generated that migration, which I just called added audit fields, you can go ahead and do that so, you know, you can work alongside me. But when I did that, you noticed that it just added those columns to every single table that exists. However, up and down. And then I went ahead and updated the database, which was a successful operation. So as of now, every field in the database has these audit columns. Now, what happens when I am no longer satisfied with this? So we have seen already that when we generate a migration and we realize that there is maybe a mistake with it, we can always remove the migration. But look at what happens when I try to remove the And if I go to the command and say, remove migration, everything builds and is successful. But then I get this error saying that the migration with that name has already been applied to the database. Revert it and try again, and if it has been applied to other databases, consider reverting its changes. You see all of that. In other words, this migration has already been applied. So it cannot just remove migration because remove migration actually deletes the reference or the record of this migration file from the folder. So it is a fail safe it's saying, I've already made note of this change to the database. So I cannot just go and delete this from the history without you modifying the database. So that's what we're here to do to understand how we handle that rollback. And it's a pretty simple procedure. I don't know, maybe this a message could have given us a bigger hint or a better hint as to how we could revert the database, but let us do that together. So reverting the database really is an update database activity, right? Kind of sounds contradictory, but just work with me here. So to revert the database to an earlier point in time, what we need to do is run our update database command and actually tell it which migration in time we want to update to, which if it is in the past, it's really revert to. So I want to revert to the migration that I did where I added the default teams and coaches right before I added the audit fields. So I'm going to simply just double the file very slowly, or you can rightly can say rename, and I'm just going to copy that file name. I don't need the CS. I just need the name because this is the name of the migration that is actually stored, that timestamp underscore and whatever verbiage you put behind it. So in package manager console, I'll say update database again. And then I will just pass in the name of the migration in quotation marks and press enter, And then you see that it has actually done what was in the done. All right? So that's all we talk about when you have the up and you have the do. So the commands that executed just now were to remove from everything there. It's altering and it's dropping the column modified. If you look in the do portion, well, the migration file, sorry, if you look in the done portion of this migration file, that's all it does, it tells it to drop the columns. So that is a practical example of what happens, we one revert and two when this down actually gets called into action, it actually undoes everything that the up did. So now that we have reverted or updated the database to a previous version, I can actually go ahead now and safely say remove migration. It will do it willingly happily and without any queries or qualms, and there we go. It has removed that previous migration. I'm just going to make adjustments to my based on main object. There we go. I've updated these column names to be a bit more descriptive of what is going to be in them. Create a date, modified date, created by, modified by. Now after doing all of that, we're just going to add our migration again add the date audit feels. Okay. And when that's done, I just want us to take a look at what happens with our date column. So these are date time columns and they're not nullable. What happens is that when you have a nollable date time field in the database, or it has to put in a value. So that's why we get that default value everywhere that this date time column is going to be added, right? So we can't have no date in a nullable date field. So it's putting in that default date, which in the database, you'd probably see some 01010001. It's just a default date, I guess, since the beginning of time. All right. So with all of that done, we can go ahead and update our database. And I'm going to run that command without specifying any migration, and it is going to be a successful operation. There we go. So now, our database is equipped with at the very basic level some auditing fields. So when people are entering, of course, we want to know when did they create that record? When was it last modified, and then who created it and who last modified it. 34. Manipulate Entries Before Saving Changes: Hey, guys, welcome back in this lesson. We'll be looking at how we can manipulate entries before we save changes. Now, a little background to why you would want to do something like that, would be coming from maybe the fact that we just added audit fields for all tables, right? So we just added all the audit fields in the base domain object. And evidently, this is a bit more work to do because we've seen that when we're adding data, we have to formulate the objects and then add them and add them to the context and then save changes. Now, that means that every time somebody adds a team or a league or a match or a coach or anything to the database or modifies it, we're putting extra responsibility on ourselves or our developers to say, always put in the created date, always putting a modified date, at least if you want the data to be standard. Now, doing that in a bigger system can get very cumbersome because we are only working with four tables, and I'm annoyed already just thinking about it. Imagine when you have 2030 and more tables dealing with. So at this point, it's good to understand how the context actually gives you access to everything that is about to be saved, and you can actually manipulate what you need to manipulate before you save the changes. I'm going to hop over to our data context here, and it's going to be a quite interesting experience now because we can override the save changes, right? So we see quite a few options of save changes to be overridden. We have the default one, which is when we call context dot save changes. We can override it. And then we can carry out some operations here before we say actually save the changes. You'll notice here that save changes really returns an integer, which is usually more than one when it is a successful save and less than one or zero when it wasn't successful. So if you need to, you can probably incorporate that into your checks to see if it was a successful save operation or not. But for now, we're not going to focus on that what we want to do is to know how we can intercept everything that is about to be saved and manipulate of these values before they get saved to the database. There are quite a few things to be aware of when you're in this area. Firstly, there is an object given to us called change tracker. I think we looked at this earlier, but let's just look at what it allows us to do. So we have changed tracker and then we can say dot entries. This actually gives us the list of entries going in to the save changes are being tracked by the context in memory. I can say VR entries is equal to change tracker dot entries. Okay. Another cool feature with being able to see the entries is to interrogate what we call the entity state. So that's an enum given to us. A enum is just a constant, and this constant has different states that generally represent what state your entry would be in by the time it hits the save changes. So we have unchanged. Meaning maybe you did a query, the tracking was on, so it's tracking it, but then you didn't update it, you didn't do anything to it, so it's in an unchanged state. Added, I think that's self explanatory. You're about to add something to the database. So anytime you create an object and say context dot add and then put in the object, it's now in an added state. Detached means that it is not being tracked by the context. All right? Modified means that well, you took it, you changed something in it, and then you said, here it is. So it was either being tracked when it was changed. So now, it sees that it's different from what it was tracking initially, or it wasn't being tracked, and you explicitly called context dot update and passed it in. And then there's actually another way to update where you just mark it as modified, right? So you can actually just put it in the entity state modified so that the tracker will know that, I should be tracking this as a modified object. Deleted self explanatory. Anytime we say context do delete and give it the object, it is now in a deleted state. So those are really the entity states available to us, right? But then in certain situations, we may need to track some and not track some. So with the entries, I don't want to see every single entry. I don't need anything that hasn't been modified or hasn't been added, at least for my auditing purposes. Now, once again, I'm just giving you a blanket concept that you can probably adopt for different reasons, right? So you might have some complex audit logging or a second database that you need to write logs to. You may need to Track, when was this added. When was this modified? Is this are to be deleted and write that to a separate data store, whatever it is, the change tracker allows you to intercept these entries, do what you need to do all before the save changes gets run. So let's take a look at that. What we want to do is get all the entries that are about to be modified or added and then update the respective columns, right now, we're just going to focus on the dates. Okay. Right? And then we can go ahead and save the changes afterwards. So I can extend this enumerable because this function is just returning an enumerable, so I can just extend that and say where, and we can use our Lambda expression right here. So I can say where Q or my Lambda expression, and then I'm going to look for the state is equal to, and then I can use that num to filter on added or Q state is equivalent to entity state that I just did added, so this would now be modified. Right here, we're filtering out and getting all the entries that are either about to be added or modified. Okay. Next, I'm going to have a four each lobe going through these entries, and I'm going to be converting them into the base domain objects. So take a look at what's happening here. Entry in entries. Now, if we look at entry, entries of type entity entry. All right? So when I'm converting it, I can just convert entry, but I have to convert entry dot entity. So entry has a few objects, a few properties, you can see the current values in the object, you can see the original values. So even for audit logging purposes, you can see where the properties before or the values on the properties rather before and what are the values now. You can look at the context, and the entity, of course, embodies the actual entity type that the context knows about. Now, because we use the base domain object and every other entity type inherits from this, I can bring it down to this level so that I can modify the object fields, because at this point, I don't know if I'm saving a football team. I don't know if I'm saving a match. I don't know what entity type is really coming over in the entry. I'm just casting it into the base domain object so that I can start interacting with the fields. For each one, I'm going to say auditable objects do create a date, nice and simple is equal to date time dot because I'm about to save the changes, it must be know that modification sorry was done. Modified date, it's the same time. But then think about this. I am looking for added and modified, but every time something is added. Of course, I want to have the created date, but I don't want to have the created date every time something is modified. You see how sensitive that is. So I'm looking at both added and modified, but I am putting in the modified date every time that something is going to get modified. Or, sorry, I'm putting in the created date every time something is getting modified, which is wrong. All right. I'm going to take that one out. So every time we hit save changes, I want to say that, it was modified because it's true. Whether it was being created or it was being modified, it's modified date is no. However, I only want to set the created date if The entry dot state is equivalent to entity state dot created or added, rather, right? So when it's going through, it will say, for this entry, I'll set the modified date. So whether it's being created or actually being modified, we're setting the modified date. However, if it is being added, then set the created date. Otherwise, it will just skip this. It won't do anything. We'll just go to the next entry and do all of that until it's done. Now, when all of this auditing is finished, the last thing that we want to do is finally save the changes. This is like a final Hurrah. Whatever data came over, we don't know, we don't know what entity DB set type it is. We just know all of these have base domain object in common. And once that is in place, we can go down to the audit fields, regardless of the higher level type, I'm going to say, and then make the adjustments and then save the changes. Now, before I moved on, I just noticed that there are zero references to this method. In my program do CS, I have dusted off a few of the old methods that we would have used, and each of these methods does call save changes, but then look at that, save changes a sync. However, we have overridden just save changes. That's my bad slip of mine from me, right? So the override method that we're really looking for is save changes. So what I'm going to do is rewrite this override because I think that there are several. I just want to make sure we get the right one. So we have save changes, save changes, and save changes a sync that takes two parameters, and then this one that takes one parameter with a default. So I think that's the one that we want because if we use the other one, then we have to provide a bullion, and well, that cancellation token is there by default, but then we want the one with the default where we know we don't have to provide any parameters. I'm going to use that one and just organize my code here, right? There we go. So we're public override task in Save change is a sync, and that is the parameter. You can go ahead and make that adjustment, and then in addition to that we need to make this syn because well, it's a syn, so we need to make it an asynchronous method. And then if it's calling a synchronous, it's an asynchronous method, which means that we want to use the asynchronous method down here also. I can just change that one to save changes a sin and it requires an weight. So I can just pass that a weight, and that should be it. So I have consistency. I'm going to use the same cancellation token in the parameter right there. All right. So with that adjustment, no, I'm seeing all 12 references being made to the save changes. So that's a little better now. All right. So that means when from our program.cs when we call the context, it's really going to hit our new Custom one, and then it's going to do all of this before it calls the base. All right? So that's the power of overriding all of this. So let's go ahead and take this for a spin. All right. And what I didn't point out which methods I dusted off, apologize. So we're using the simple insert operation methods where we're adding new league and teams with league, and I'm doing the simple update league record, we're updating the league record and the team record. So Taking a look in the console and the SQL statements being generated. You'll see here that I have P three, and I'm getting that date time value right there, right? And when it's being inserted, create date, modified date, P two and P three are going in. Accordingly, here's P one. There's P one, which is our created date. And then we have P two being null because that would be created by or modified by So we see that our dates are going in into our SQL statement properly, right? So once again, that SQL statement gets generated at the time you cause save changes. Now, we've overwritten it to do some additional things before that Q statement. So that is a nice little way to inject your own little logic and a little consistency or cleanup or anything like that you need to do before the data actually gets committed to the database, then you can always override the context methods like that. Now, there is a way that you can extend the DB context even further to facilitate certain things. So in context, we want to put in maybe modified by, like a user name or something to say, who modified it. Obviously, we can't do that here because there's no way for me to pass in a string that represents the user name as or as any form of value here because safe changes is only looking for this. So what we can do is extend our context to accept additional data that the default context would not, and then we can massage the data before we call the base context. So we can look at that later on, and I think that will be a fun activity. Okay. 35. Extending DbContext: Hey, guys, welcome back. Now the last time we were here, we were extending RDB context to handle a bit of auditing work. In other words, we extended the save changes or overrode it actually to be able to massage our data a little before we get to save it in the database. Now, we saw the benefits of this because now we see that we're getting the audit dates for the modified and created date. However, a limitation is that we don't know the person who has created or modified the record at any point in time. So that is one of the limitations we're just overriding because we're still kind of confined to what the default safe changes has in store for us. I cannot override this parameter account ask for a user name. So for context or program dot says would represent the application that the user is using, right? When somebody is using the application web or desktop, they add a new lead I would like to log who did that. I want their user name from the system. I don't want to put their responsibility on them, the user to tell me who they are. I want to know who was logged in when this action was taken against this record. So It would be nice if I was able to pass over a user name into the save changes so that we could use that in the auditing before saving. So with all of that limitation that we understand the limitation and what we want to accomplish, I'm going to get right into it. What we're going to do is extend the DB context to have another version quote and quote of it that is going to take over the save changes role or take over the overriding of the save changes and then hand it over. So that sounds a bit more complicated than it really is, and actually started the work already. So I've created a brand new class, which I created right in the same data project as the original DB context. I'm just calling it a auditable, sorry, the football league DB context. Now, this audit football league DB context file, it's one abstract, so it wouldn't get, you know, instantiated on its own. And it inherits from the DB context. What we're going to do is let the original DB context or football league DB context inherit from our football league DB context. Now, the benefit of this is that just by using this one, we get all the functionality available in the auditable version, but then the auditable version allows us a bit more flexibility because we're not confined to the actual DB context and overriding it in that regard. So with all of that said and done, I'm just going to cut this save changes AC method from our football league DB context, and I'm going to place it in the abstract version, and I'm going to make a few modifications at this point. One, I'm going to take out this cancellation token because I don't really need that I'm going to take that out altogether, and I'm going to allow it to call the base saving changes because that's the DB context, right? But I'm also going to extend this method to take a string parameter called user name. Now, what this is doing is letting every calling method know that they must pass over a user name in order to call this method properly. And I'm going to remove this overide because we're no longer overriding. We only have a method called save changes, which is just going to call the base. So it's just like an extension or a method before the real method. Now that we have this user name parameter coming in, I can now say auditable object dot at this point would be modified by, and then the modified by would be the user name. And similarly, the created by would be that user name. Now, zero references. Why is that? That's because in our program, it's still calling the default save changes. Let me find one of these methods. There it is. It's still calling save changes async, that is the default one for the context. However, if I look at the overloads available to me, I will see that there is a new overload, which allows me to pass in that string user name. There we go. So I can pass in a user name. Let's just say test team management user. I'm sorry. I'm just trying to be explicit enough so that we can see in the database query where that save changes is going in. This is a simple update. So we can look for that or test up date user. Let me keep it simple. Test update user. This record. We don't need that. I'm going to leave this one without any user name. This is modifying the league. This one is going to not have a user name. This one is audit. So I can change my league name. So you don't have to do that. You can use your old data or put in your own data, but that's fine. But I'm just going to put in some overrides test audit create user, and then I'll leave this one without any user name, right? So I'm just going to run this at this point and let's see exactly what happens. So when I'm looking at some of the statements, I'm seeing here in the save changes that I have that test audit create user. There we go. So that test audit create user was the one who created that audit testing league. All right. There we go. So this person, this user is going in as P zero and P two. And if we look P zero is the created by and P two is the modified by. So that is how we can get those users into the system. I note, while I may have hard quoted it here, once again, in a real application where you have people logging in and so on there are ways to get the user names, and just plug it in in the save changes. In.net core web applications, you can even inject the CTP context along the way and forego the whole grabbing it and sending it over, but that's not for discourse. I'm just trying to show you how you can extend your DB context and manipulate your data in more and more creative ways to kind of make sure that you have that data integrity by the time it hits the database. Okay. 36. Implement Full Database Auditing: All right guys. So the last time we were here, we were just concretizing how we do our auditing. All right. So upon to know auditing is just a modified date and modified by creating the attend created by. But then you may have another necessity where you may need to audit the entire roll. So you could be on separate database or it's a separate tables, or at least that's always traditionally done. Sometimes what people do is they put triggers in the table so that every time something is saved, edited, or deleted, it will automatically writes are required to another table that is trucking. What activity happened on a particular table is take a copy of the table. So we're going to take a copy of the original values prior to the operation, the new values after the operation, and serialize them into a string and store that inside of the database. How it can be read. That's up to the application and the developer at a time. But I'm just showing you how flexible EF Core is in accessing data before and after the fact and how we can manipulate it during. So let us start off by creating a new class and I'm going to put it in the domain. You could probably put it in common, but it really is going to be a domain class. Creates a new class. I don't want to call it IT audit. So audit is going to have a few fields, is going to have the standard id, a string for the table name. So we make it public. And then we put in the fields. So it's going to have ID table name, datetime, meaning that the time, of course it was entered, the key value. So whatever primary key is off the record being audited, the old values of the record and the new values of the record. So after you've replicated that audit class, then you can go over to our auditable DB context. And we're going to add that DB set, right? So it's going to be in the auditable DB context. Do you need set audit? And I'm just calling it audits. So then we're going to go into deceive changes. And I want to actually do some stuff before save changes, because before we save the changes, we need to take a copy of the data that is coming in, right? So right above where we get our truck changes and know whatever happened and so on. I'm going to see on before save changes, right? This method doesn't exist, so I'll just control dot and generate that method stub. And there we go. So this is going to return a list of another class that I'm about to create called audit entry. So if I do Control dot, Let's see what Visual Studio can do for us. It says it can generate a type called audit entry in its own file. So we'll just go ahead and create it in its own file. No harm, no foul. Jump over to audit entry. So an audit entry is basically going to be an obstruction of what exactly we need, right? So let's settle this class for a bit. So I'm going to say public audit entry. So that's basically the constructor. And I want entity entry. So we know what entity entry is. This is coming from EF Core. And we'll just call it entity entry and sure. And then we are going to use that constructor to initialize our property. So I can control that and say Create understand property called entity entry. And it does that for me automatically, right? Then I'm going to have other fields that kind of much what we had in audit, right? So I'm going to have string table name. I'm also going to have well, old valleys and new values and well Key Vault is all about isn't involves what I'm going to change the datatypes of those. So we have the table name, we have the key value which is really going to be like a dictionary. Someone to change the datatype to dictionary of string and object. So if you've never worked with dictionaries is just like a key value pair so that I can be able to solve JSON. And that's what a dictionary is. A string would be the key and the objects would be the data that's going in alongside the key. So control dot include that. And I'm going to just initialize it so it's never know. So just take this and c is equal to a new dictionary of string, an object. And pretty much that's all we're going to do for old values and new values. And we do the same initializations and you see all that kind of looks consistent and little scarier bullet soon you'll see what we're seeking to accomplish. The next line is going to be for temporary properties. So I'm going to say a public list of property entry. Which is also from coming from entity entries library of freeware trends change tracking, right? So you see a list of property entry, temporary properties is equal to a new list. I'm going to have a quick method That's just returning our property rather that's returning a Boolean to see has temporary properties. So as soon explain what temper properties really means or will be used for. But then we're going to have another method that's going to actually build out or audit required on I'm going to call it to audit. All right. I would just have to include any namespace there. There we go to audit. And then we can start building all the functionalities. So when we talk about to audit, we're talking about building up the actual entity, her record. So var audits is equal to new audit. Initialize a new object of type audits. And then we'll start filling out all of the fields of the audit. So I can use this object initializer. And then I can say, okay, Did time we know that you are datetime dot nl no-brainer, right? So this is the time dots. No. Right? Then table name, that's easy enough and there shouldn't be using a semicolon, apologies. Table name is equal to the table name coming from the local. Alright? Then we have the key values, which would be a serialization of the key values coming in, right? So I'm just seeing key values is equal to JSON convert, see serialize or object and key values, right? So pretty much remember that this is a dictionary. So we're going to have the key, which is going to be like ID. We're going to have the value is stored as an object, so it's a string ID, or in our case we're using int ids. Whatever it is, is just going to have that key value pair and we're just going to be storing it as JSON. And frankly, that will be the same thing for the old values and the new values. But then there's sort of a twist with those. So we're going to be seeing something like all the values is equal to all values dot cones being 0. If it is 0, then we can store null. Otherwise we want to convert the volume coming over from the key value appear. Know the relevance of that. If we are doing certain operations like inserting our record, then there is no old value for that record because it's a brand new record. So there's no way to get the old values. So it will be logging to see were there any old values to be stored in this record if not then null because then we can surmise that it was an add operation. And then we can go ahead and serialize the values if in case it was I didn't eat or an update. So the same way we did all the same, we will do new values. So I'll just duplicate this line and changes so two new values. And wherever there was a split, new buddies. So if there are no new values, store null, although eyes stored in the new values. I think I'm going to add one more field and that is the axon. So I'm going to, in the audit table add actions so that we know what kind of operation was being carried out, what's right. So action, and I'll do the same here, called you action. And then in order to audit, I'll say action is equal to our local action. I heard. So we know what was happening now after we've built all this whole audit object, this method is supposed to be returning that audit object. So we just return audit. That's it for our audit. Enter at least for null as we go along, we might see all the adjustments that are needed. No, we'll jump back over to our auditable DB context. And to me this easier, I'm just going to take all of this out. I'm going to cut it and I'm going to put it inside of our new method. So on before save changes, these are things we wanted to do anyway, right? Alright, so I'm going to refactor this a bit instead of trying to get only these entries on Wednesday, instead go by a process of elimination. So I went to see if they get the entries from the instructor where the state is not equal to detached and it is not equal to on cheaters way would be capturing more scenarios in between, right? So get me the entries that were not detached or unchanged and then those are the interests that we're iterating through. You see here that's it's complaining about username. Let's see what controller.js for us, we can generate a property that's fine. Okay, this just add the parameter. Generated perimeter username. There were all sorts I was looking for. So now we have the username i, we can Boston that username on before, see if changes and everybody's happy. All right, So nowhere getting all the interests are not touched, not unchanged onto our setting those audits columns for them. Now before we get into that for each, I want a new list of audits entries and we will be compiling this list as we go through. So for each entry, after it does pass through these initial tests, I am going to try and see I want a new instance of audit entry policy in that entry coming over from our THE trucker. Right. So remember that's when we had the constructor and audit to ensure that Tolkien entry as its parameter. Then I'm going to get the table name through seeing audit entry dot table name is equal to and then you just hurry up and sit that it's equal to entry.metadata. So I'm just showing a whole much data. You can actually get a boat, something that's a bolt to be saved to the database. I can get the metadata, not relation. Let me see your relational event need to include missing reference or just spell it properly. So dot relational or actually I think that's a dotnet Core three-point one. If core 3.1, here it is, get table name. There we go. So in court dotnet Core 5, it's even easier to get a table name. I just see GET table name. All right. And then we're going to add this new audit entry to our list. So I'm just going to say Add and audit entry as ensemble fact. There's another field that I would want to sit from this point on, and that would be the action. Alright, so we need to know what action is being carried out. And then I could just see the entry dot, state, dot to string, that is the auction. Know that we have that part of the way and let us focus on the properties. So we need to evaluate certain properties, one, to copy their values, whether they're old valleys or new fathers or the primary key equals and remote. That's our audit record, does have a specific field for the primary key. So what we can do easily is start off with a foreach and we can say var property in entry, Thought properties. So this is using what we'll call reflections and all which is one of those great allowance is given to us by more recent versions of C sharp. So we can see if property, sorry, if property is temporary. So this would allow us to evaluate if there is prettiest hold of value inside of the property. So back when newer, experimenting with adding records and so on, you'd see that the id value has a minus, I think that's int Min minus 2 billion and something value in it. That's actually a temporary value until the value is saved. Right. So we want to see if property is temporary than audit dots. Audit entries are temporary properties at this property. Alright. So this is all we can know if this is going to be added or not. You'll see later on why this is so important. So after that we can continue. We don't need to do it any other operation on this property. If it is a temporary property, we just add it to the record and then we just continue. No need to worry about it. No other things that we may need to do if it is not falling into that category is get the property name. Which once again, I'll have to seize property.metadata and we just use my clipboard property dot data dot name. So I'm getting inflammation and that's what metadata is. If the property.me is primary key. And that's a method. So we're seeing if we're dealing with a primary key, then we want to store the key buddies. So it's going to look similar to what we did here. We're always seeing IS audit introduced key values and then we get the property name as the key or the subscript off the IRI. And we're storing the value. So we're going to just go ahead and store that. So this is all we get that key value appear, right? So that's string in the dictionary, that's as a property name. And then the value or the object would be the current value of the primary key. Then we go on to a switch where we see Get me the entries state. So switch entry state. And then we're going to do a few cases. We're going to check if it is being added, then we need to store the new values with the property name on the current value. Alright? Although was if it is being deleted, we need to store the old values with the property name and the origin of us ECF current value and the origin of value. Current value is whatever is being putting know and the original values, whatever it was before. So that comes in handy know when we're checking if the NTT state is in a modified state at that point where we're seeing if it does modify it on a property has been modified, right. So we're seeing if you change this particular property that we're looking at, then we want to know what the original property of Ardi was and what the new property value is. All right. So that's what that switch statement is for. And right, No, that's all I'm doing on this for. So once again, we want to store if it is appropriate if it is a temporary property, if it is, that means it's a record that's a bolt to be added and there's no primary key because this is on before saving changes. So nothing is really happening just yet. Primary key ways. However, if it does up primary key which only would be present if it was at deleting or a modification, then we go ahead and make a note of what the key value is. Otherwise for the rest of the values where logging what the new values are, what the old values are, fair deleted, and both if it is being modified. So moving on, no, we need to actually save these audit entries or at least build up a list of audits to be sieved later on, right. So I'm going to do a for each eye again, I don't want to save for each audit entry in and I'm going to get that list audit entries docked. And I want off them where we have our Lambda expression, q-dot has temporary properties. We can see is equal to fall as meaning it doesn't have any or I can use. I'm not saying I've been doing this for readability purposes. So we're there are null temporary properties and I can't use audit entry here again, let me just see audit pending audit to ensure let me use that pending audit entry. All right. So for each pending audit entry in the list that we have compiled to this point where there are null temporary properties, then we want to add it to the audits DB sit for lead to injury. So pending audit entry dot and then here's why we had that to audit method. So remember that the two audit noise when to take whatever value was Boston and whatever was sit here and actually create on audits record for return. Right? So we're just seeing everything that we just compiled, all the key value pairs on. So on, converts it to an actual audit record and edit tool or DB sit. After all of that, I want to return the audit entries that do not have or sorry that dual have pending properties, temporary properties. So I want to return where they have pending properties. And of course that's us to be a list because I need cells returning a list. There we go. So that's what's happening on before save changes. I'm still getting that, Aaron. I think it's because I did it's inside of this for each and that's his card. So this should happen outside of that for each I apologize. So let me just call it quickly. And at the end of the method, we go through compiled audits and return the ones that are temporary. There we go. Everybody's happy with that. No, we've done that before. Saving changes. All right. And then we go ahead and save changes. Now what's going to happen after we save the changes often at this point is that we're going to have all the audit interests that's had the values that it wants being added where they didn't have an ID yet. They're all still stored inside of this null. I want to go back through and update the ID valid know that they have been stored. So instead of returning at these points, I'm going to put this in a variable. I'm just going to call it. Result or result. So we're just storing what this would be, right? And then we're going to have another method that I'm going to call after saving changes on where policy in the audit entries. Right. So we get the audit entries that were in a temporary ID state. We see that g and g is and all they've been updated. Now we need to do something after the save changes. So I'm going to collapse the on before and I'm just going to generate this method. And with that done, what we're going to do is go through the audit entries and check for whatever it needs to happen, right? As a matter of fact, this may not even need to happen. So I'm going to say if the audit entries is not equal to null, for whatever reason it might be null. Or audit injuries dot cones is actually greater than 0, meaning we actually have stuff to process afterwards. Then you can call this method. Alright? So inside of the method, what we need to do is go through because at this point we're assuming that something is inside of the audit entries, you would only get here. If something was, then what we are going to do is evaluate each audit entry and set off this list. And then we're getting the properties in each one of these, so two for each listed for each. So I'm going to say get me each prop in audit entry dot temporary properties. So remember that we compiled a list of all the temporary properties. Then I went to have an if statement to see if the property is our primary key, then we add the key value to be the current value. All right. So we're just doing a quick updates of that audit entries value to be the current value. Otherwise, the new value is our current value. So we're just seeing if it's a primary key, which is more than likely why it would end up being temporary. If something was being added, updated to the current volume, know that everything has been saved. Otherwise, if it wasn't our primary key, which as I sit there I can think of a scenario where you'd have a timber value for XOM. That's not the primary key. But in that event, we still update it. No problem. All right, so after that, we do the for each on, for each one that we are saving, that we're going to definitely add it to the audit. Just I call we added the audit interested in the before saving changes. So we just audits dot add under the two audit so it can be converted. Then we return a save changes. So we have to call a sea of changes at that point. All right. Now after we've saved those changes, we can return result here or save results rather. So that is really what I have implemented for auditing in multiple applications. And it works pretty well because like I said, it's really just storing string representations of what the entire record would be like. And we have the old values and the new values for a side-by-side comparison. So on a user interface you could easily print result because it's JSON, it may not be readily human-readable. So you may want to tweak it a bit so the details presented a B, it's more readable, but at least you can see everything that's happening. Keep a log of everything that is being deleted, modified, or added throat application, whatever they are valleys where at that time. So he can go ahead and do some tests, do some protists, and look in that audit stable, of course, you know, with all of those changes, we have to scuffled auto new table. So if the ad migration and we say added audit table. And then after getting that migration we have to see the data is. And with all of that done, you can go ahead and test it out and let me know how it works for you. 37. UPDATE: Implement Full Database Auditing - Fix: Hey guys, This is a quick fix for our previous video. You would have setup your auditing and the full database auditing. And we'll see how it serializes everything to JSON, stores it in the database. However, if you try to do multiple operations, but to buck, you might run into a difficult situation where it complains about a typecasting for an audit field or an audit record when that really should not be happening. And I'm just here to show you the fixed for that. So in our on before saving changes or save changes, what should happen is that this should filter out anything that is unchanged already touched upon to know we know that the entity state, I bet So anything be insert is going to get the added. And then we know that we have the entity state for deleted and modified or so. But then the reason we're excluding these two is that we don't need to audit anything that is detached, meaning it's not being trucked, our shouldn't be tracked or unchanged, meaning well nothing happened. So read essentially what happens is that once the Save Changes is really called, whatever was being tracked automatically goes to an onchange state. So if you do multiple operations, then you're going to end up with audit entries that are being saved. And then you go and try to say something else into the framework is still tracking the old objects that were most recently saved, including the audit record. So that is why it would need to be filtered out at this point because it is knowing an onchange state. However, for whatever reason this filter is actually not working as I would have liked it to the maybe working for you, Amy anatta of this program, that's good. But I have seen this problem happened more than once. So I'm just going to show you this fix for it. And it's less of a sophisticated fix. All I'm going to do is invert the condition and that works better for some. So what we're going to do is say, give me the entries where we know we want to try them, meaning the state is equivalent to dead, or it is equivalent tool modified. And then one more. Or it is equivalent to those BreakLine deleted. Right? Because we know that we want to track those. We don't want to track the other two. But so when we're trying to exclude them for whatever reason, the exclusion might not work as it should. So that's fine. We're just going to work our own that still maintaining the quality and the integrity of our code. We're nowhere seeing give us the ones that we know we will definitely want to audit. So we're getting those entries and then everything else would fall in line because there are they have or case statement based on which state it is that needs to be added to the audit entry. So that's a quick fix. If you had that difficulty, we can do that and I'm sure you get a better experience. And if not, of course, we will continue to investigate and look because we're all learning red. This is a very complex library and we're just doing our best to get the best out of it. 38. Data Validation with Data Annotations: Hey, guys, welcome back. In this lesson, we'll be looking at putting constraints and limitations, general rules around the properties and the values that they're allowed to have. Now, I would have used the word constraint more than one. And that would have been more specific to the migrations where we saw that constraints were getting added for foreign key relationships or for uniqueness and stuff like that. But then those are all migrations based on some of the rules that we're setting up in our model creating and based on the data types and what we set as a foreign key reference and such. Now, besides those specific situations, there are times when you want to be a bit more calculated and have a more hands on grasp of what is getting stored and how it is being stored. So a practical example. When we look at team, we see that we have a string for name, which gets translated into a RCR in the database, but then it's VRTR Max. Do we really want VRTRMx for a team name, right? So right there, we're opening up the database to the possibility that somebody could put an entire essay in that field and call that a team name. We don't necessarily want that. Other constraints might include default values. So we had added these base domain objects. And I'm just using this an example where we have the datetime. But then there might also be situations where you want default values in these properties. So if a value is not provided from the interface or from the user, you still want it to have a value. Let us take a look at limiting some of what our string columns can take, for instance. So from the DV context, and this is using fluent API. So we would have looked at fluent API earlier when we were specifying certain rules around what each entity can have. All of these lines of codes or blocks of code really are fluent based on fluent API. So what we're doing now is using fluent API for some validation. So I want to say model builder dot, and then it is going to be on team this time. So I'm going to say dot property, which then opens up for another Lambda expression. I'm going to use P as my tokens. I'm going to say P dot, and then I get to choose the property I'm interested in. So I did say name. And then after that, I can say what constraints it has. So I can say to the database that it is required, or it has a specific column type. What if I didn't necessarily want it to be VT? What if I wanted it to be VT. For instance. What if I want to set a max length, which is what we're about to do. So I'm going to say max length, and then all it needs is an integer value. I'm going to say no team should have a name that's more than 25 characters long. I think that's reasonable. There are some teams with long names like Barca, Munch and Gladbach over there in the Bundesliga. But I think 25 is sufficient or let's just bump it up to 50 to be on the extremely safe side. So no team should ever have a name that exceeds 50. Now, doing the rule light, this really sets up the constraint for one property, and it's pretty much one property at a time. But I can do this for multiple entities. So I want that same constraint on a league. I don't want any name of a league to be an essay, and I don't want any name of a coach to be too long. All right? Other things that I could do, I could set up this property as an index. When we talk about indexes, they're really high speed lookup points. I'm going to remove, I just duplicated that line, and instead of saying dot property, I'm going to say dot has index. And then this is now going to ask me for a Lambda expression. So I'm going to just use H like we're doing index and I'm going to say the index is on the name. That means if we search by the name, it should be a high speed. Point for the data, so the query should run relatively quickly. I can actually just do the same thing for our league and for our coach. Notice what I'm doing is just copying and pasting and replacing the names accordingly, because they all have similar structures. All the things that you probably want to do would be to specify that these columns should be unique or whatever value goes in them should be unique. So a case study for that, well, you wouldn't want two teams with the same name, though it is very probable. You wouldn't want two leagues with the same name, though that is, I guess, less probable. But in a more practical setting if you're doing a database for a school management system or a book, Storage. You have the ISBN, which is a unique number for every book, or you have a student's ID number, which is different from the default incrementing ID, but just that ID that they use in school, you have that ID number, then you'd want to specify that it is unique. So I could actually extend the has index to say that this index should also be unique. All right? And there are other things that you could apply. Some of these I've honestly never used. I've never had a situation where I needed to use them, but that's just me you can explore and find use for it. So here, I'm telling the database that this is a high speed lookup area on the table team and it is unique. If you wish to have an index on multiple columns, then you can actually just chain this along. So I can make an anonymous object type. We looked at that earlier, so I can just say in the Lambda, let's use coach. I'm going to say that the index in the coach table is a combined or both the name and the team ID should be indexes, right? So I can start my Lambda expression, say new open curly braces, so it knows that it's an object type. And then inside of this object, just specify multiple columns, H dot name, H dot. Team ID, comma, separate, et cetera, et cetera, and then by extension, I can make all of that unique. So that means that combination should always be unique. So that's more like making a composite key at that point. All right? So I'm just showing you a little tidbits. I don't know how practical they are necessarily in this particular situation, but you might just have these challenges in designing your database, and if you're coming from a strict database background, then you know how easy it is. Or relatively straightforward it is to do it in SQL Server Management Studio. However, in the situation that you're dealing with a code first database, you would like we said, want to manage all of the changes from our entity framework and let them trickle down to the database instead of mixing and matching. So when I've made all these changes, if I go over to the package manager console and add a new migration, and I'm going to say added validations and always remember to select the correct project. So I just change from console to the data, apologies. But now, when we look at our alter statements. You see here for the name in teams, it's now VRCR 50, and the old type is NVCR Max. All right? So there are a lot of performances performance inhibitions, that can come about by just leaving your data types as VRTA Max. So when you start putting in these constraints, you're actually making your database more compact, more efficient and well, just saving the stress of, you know, design considerations down the path in the future. So we have the alter column for the names. So each one is generated nicely, then we have the create index. So we see here, create index on team's name, and it should be unique. And then we have create index on the name for the leagues, which we didn't specify to be unique and create index on the coaches table, and the columns are name and team ID, and we looked at that filter code earlier. So I'm just going to go ahead and update the database, and I don't anticipate any issues with that command running. And there we go. We have a red line, right? Let's see what this red line is. So it is saying that create unique index statement terminated because a duplicate key was found. So we're seeing that we have data in the teams table already, and we're trying to say that create this unique index on the team name column, It should be unique. So you can see my team stable here, I have a lot of things repeating and repeating and repeating. But the point is also clear that I can't be saying that the name column should be unique when I'm having a CMLN so many times in the database. All right? So we see that the constraint works. I'm just going to delete everything that is not in the top three. Let's see if I get any other errors. Yes, I anticipated that I would get some foreign key. Let me clean this up. All right. So in order to clean this up, I had to go over to the coaches, make sure that nobody was coaching any of these records I was to delete. Also go to matches and make sure that none of the records I was to delete had matches. So that is a practical example of our constraints in operation, right? Because we did set the referential integrity to not be cascade, but to restrict. So those things just cannot happen once those constraints are in, and we're learning how to enforce them using EF Core. All right. So with all of that cleaned up, let's go back and attempt our update database again. And this time, I believe we're going to be successful, and there we go. Done. All right. So that is how you can go about setting up validation for your tables using FCR. Okay. 39. Fully Using Configuration Files: Hey guys, In this lesson we're just going to do a bit of refactoring and get to understand the full power of our configuration files a bit more. So when we did our configuration files, there were really designed with the intention of facilitating our seed configurations. So we named them league seed configuration. Well, the reality of the matter is that this entire class can be used for all the configurations relative to the target domain objects. So in other words, just that I call in the DB context, we actually have League related configurations. We already have this league configuration class. We can actually place all of this code inside of that class to further keep our whole DB context kind of clean. All right, it goes then we'll end up with a lot of these blocks of configurations and the loop. As many tables you may have many configurations. You want to kind of keep everything in trunks so that you can see them or find them very easily when you need to. So then just start by refactoring our team. So suddenly first one, Let's start with teams. So we see here that we have this configuration for team, this configuration, we have two others here. And then we have this whole configuration with the seeding. So first order of business, I'm going to rename this from being teams seed configuration to just team configuration. And I'll just let that refactor all the references, throw the code. Next up. What I'm going to do is bring over all of those configurations from the DB context. So I have ModelBuilder dot n TTT map, all of that. I wanted to cut that. And I'm going to come over to this seed configuration, what we're going to rename the file and nephew, don't worry about that, but right, no, I just wanted to move over the configuration. So this is the builder has data configuration. I'm going to go underneath that's still in the configure method. All right, and then I'm going to paste all of those configurations that I just put. Then you'll see an error appearing with ModelBuilder. And that's because we don't have anything called ModelBuilder in this file. However, if you look closely, ModelBuilder is like a generic version on our implementation That's allows me to say ModelBuilder dot entity and then imply the entity. Our builder object on this type is specific. It's entity type builder for the specific entity. So this whole implementation kind of embodies this entire line. All right, so all of these sets as ModelBuilder dot entity, that team, I can replace that with builder. And builder knows that it is relative to team. So everything that we do here is relative to team. All right, so I can just say builder dot Tasmania and then just list out all the rules. Replace that also with builder, just moving it up so we can see where it starts and stops. And then I'm going to continue with the other bits of configuration for the team, someone to cut that and then went to place it right there. And once again, I'll just use builder to replace that model.py in ModelBuilder dot entity, that team stuff. And there we go. All right, and then that's our configuration. So we already have that configuration being called right here. So once it hits this line is going to jump boards or the configuration file and see everything that needs to be done for a team. So I can't actually move these configurations above the has the eta. So let me just rename this file quickly before I forget. And then we're going to do the same thing for the other configuration files. So league seed configuration, it's no longer specific to seeding. So I'm just going to go ahead and rename that. Her friends go ahead and rename the file, and then I can bring over league related configurations directly into our builder. Go ahead and replace what I need to replace. And then I went ahead and did it also for the coach. So you can do that, know that you have the gist. And at the end of the day we see RDB contexts looking much neater. And then all of the messy configuration stuff there in specific places, either owned or project.