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

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

39 Lessons (5h 50m)
    • 1. Introduction

    • 2. Setup Development Environment

    • 3. Setting up the Solution

    • 4. Creating the Data Models with EF Core

    • 5. Specifying the Data Provider and Connection String

    • 6. Migrations and Database Creation

    • 7. Generate Migration Scripts

    • 8. Reverse Engineer Existing Database

    • 9. View Diagram with Entity Framework Core Tools

    • 10. Adding Verbose Logging to EF Core’s Workload

    • 11. Simple Insert Operations

    • 12. Simple Select Operations

    • 13. Filtering Records

    • 14. Additional Execution Methods

    • 15. Alternative LINQ Syntax

    • 16. Simple Update Query

    • 17. Simple Delete Query

    • 18. Tracking Vs. No Tracking

    • 19. Review One-to-Many Relationships

    • 20. Adding Many-To-Many Relationships

    • 21. Adding One-To-One Relationships

    • 22. Generate New Entity Diagram

    • 23. Inserting Related Data

    • 24. (Eager Loading) Including Related Data

    • 25. Projections and Anonymous Data Types

    • 26. Filtering on Related Records

    • 27. Adding Views and Other Data Objects

    • 28. Querying Keyless Entities (Like Views)

    • 29. Querying with Raw SQL

    • 30. Add and Query Using Stored Procedures

    • 31. Executing Non-Query Raw SQL

    • 32. Seeding Data

    • 33. Rolling Back Migrations

    • 34. Manipulate Entries Before Saving Changes

    • 35. Extending DbContext

    • 36. Implement Full Database Auditing

    • 37. UPDATE: Implement Full Database Auditing - Fix

    • 38. Data Validation with Data Annotations

    • 39. Fully Using Configuration Files

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

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.





About This Class


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


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


Class Ratings

Expectations Met?
  • Exceeded!
  • Yes
  • Somewhat
  • Not really
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

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.


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 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. 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 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.