Last time then we began
with the first of what's going to be a series of videos looking into
Entity Framework Core. And we dipped our toe in the water with what is known as code
first database generation, which was to say I wrote C# classes - a couple of them one for Author, one for Book - and from those got Entity Framework to generate for me the database tables
and put in the primary keys, put in the associations, the relationships between them and that's certainly for me, as a developer, the way I tend to start out and with Entity Framework Core, it seems pretty much like that's the way
that Microsoft is pushing it. With Entity Framework 6, the older Dot Net Framework version, there was much more facilities
for doing database first, where we have an existing database and we generate our classes in C# from them. And that's also available in EF Core, but it doesn't seem to be quite as much in
the forefront as it used to be. But that's what we're going to look at this time, so we're going to switch it around and go
for this Database First, or what's actually specifically called Code First From Database. So we did Code First last time,
now we're doing Code First From Database. It's a bit odd, the naming.
It's for historical reasons which we'll come to in a later video, but for now
just accept it: Code First From Database is when we've got a database and we want to create the code. And we already have a database because I
made one last time. So last time I did the code first then the database so let's just reverse that with exactly that database. So you'll
remember I created an Author class, related to a Book class we had a BooksContext derived from DbContext.
And then I chose - although I could have used all sorts of databases -
to do SQLite. And so we've got this MyLocalLibrary.db which was our SQLite database. And if we just take a look in there, we could see there it's got the Authors table and the Books table.
So what I'm going to do now - slightly silly, but it just shows exactly what's going on - I'm going to reverse engineer that, so
now we're going to assume we've got that database and i'm going to create the tables. So I've put in a new library so it's just a DotNet Standard library, DBFirstLibrary. I've added a reference to that from the main program that was
previously using the BookLibrary. And so let's go and get that working. And this is something that we have to start using the command line for, and the command line for Entity Framework Core is actually very commonly done through
the Package Manager console. There are other ways you can do it. The package
manager console - certainly within Visual Studio - is the way that it tends to be done. So we're go into Package Manager console,
and then we need to make sure that our default project is the library
in which we want to actually create the classes from the database. So at the
moment it just goes to the startup project, EntityFramework,
but we want to make sure that is now on our DBFirstLibrary. If you just had one project - just the console or whatever -
then it would be easy, but that wouldn't be very good practice
in terms of manageability of the code. We want to keep our database separate from our console or whatever the program is.
Before we can type any commands, we've got to also make sure that the appropriate NuGet packages are installed, and can be a bit tricky, so this is essentially what I've got to do.
In the library we've got to add a few packages. So if i go to my NuGet packages and then if we type in here EntityFrameworkCore then the ones we've got to have are EntityFrameworkCore itself - so we'll install that - and then one called EntityFramework Tools. So if we just do a search for that 'tools'
then we can see that one in there. And thirdly we need to have that 'Design'.
So if we just install that. So those are the three that we need to
have in our library as a bare minimum for this. Core itself, Designer and Tools. And then also, in our main project - so that's our entity
framework project - we've also got to put that Design library in there. So if I just go to the Package Manager for there
and do a quick search for Design, there's our EntityFrameworkCore.Design. Install that ... and then that should give me the bare
minimum that I need to make this all work. Then if I go back to my Package Manager console, then what I need to do
is use the command 'scaffold-dbcontext'. So 'scaffold' means 'create', 'code generate' - whatever you want to call it - and DbContext, remember, our DbContext is the connection point to the database.
So that's why it's called that. But it will be generating a little bit
more than just the DbContext. The first parameter that I then have to put in there is the connection string. Now remember we
said this could be a SQL Server, it could be Oracle, but we're using SQLite, and so the connection string for SQLite just consists of 'Filename=' and then we can see
down there that's the name of our database, so we're going to have './MyLocalLibrary.db'. And then the second parameter which we need is the actual provider, the type of database we're going for
- so again Oracle, SQL Server - but in this case 'Microsoft.EntityFrameworkCore.SQLite' so that's the provider we're using.
And if we hit return there, it'll do a build and then it will
read that database that we created with our Books and our Authors, and it will generate classes based on that. And we can see that in our DBFirstLibrary, it has now added those three classes. So all of this it's used default names, which we could have changed if we'd wanted to with various additional command parameters. I'm not
going to get too bogged down in that, but if we compare it with what we wrote
for ourselves with the Code First, we had a BooksContext; it's got this
MyLocalLibraryContext, but you can see if you look in there, it's pretty much identical. So there it's got the constructor with our
options that we're going to be using, the two Db Sets of Authors and Books, and then a few other bits and pieces. These things we
could have put in for ourselves and I'm not going to worry about them now.
In terms of our simple application they don't really matter, but they can matter
later on, so we'll see what those mean in later videos when we
get into more detail. But basically it's done the same thing. And then also,
if we take a look at the other two classes it's created, we've
got a Books class, so Id, Title, YearOfPublication
and Author. And then Authors has Id, Name and a collection of Books,
which is almost exactly the same as we had there. It's worth highlighting some of the differences, and the most
obvious difference is that when we wrote the classes, we gave the classes singular names, which is the normal convention, not just in C#
but in all OO programming when you're dealing with classes.
And it, in the database, generated tables with the plural name,
which is the normal convention when you're dealing with database tables.
That didn't happen automatically on the way back, so on the way back when we just generated those classes, it just made the class names the same as the table names, which is an irritation. There are ways around it - they're quite
complicated. I'll try and give a link to some websites that explain that, but I
don't think I'll waste time going through too much myself.
But it does just show how Microsoft are kind of not putting the emphasis
into this database first that they used to. In EF6 that was very easy to do: just click a check box when you're doing this.
But not too much to worry about. Other things that it's put in there, it's just been a little bit more verbose about various things, but other than that, that was the main
difference. And just to demonstrate that that's all going to work,
let's go back to our program now and in here rather than having
our Books Context which is the context I wrote for myself, let's change that to the generated one, which took the default name based on the
database: MyLocalLibraryContext.
And get hold of the namespace. And then also I'm just going to have to
change what's going on there so that it's a DbContexBuilder for the right thing. That all adds up okay. And actually,
quite luckily, because I've made extensive use of 'var', the difference between the fact that previously I had classes in the singular - 'Author', 'Book' - and have now moved to 'Authors' and 'Books',
actually that's all dealt with. So we can now see our 'recentBooks' is an IQueryable of 'Books' - plural not singular - so it's all coped
with that quite nicely. And that just means it's really easy, because now if I run this code we can see one slight problem there
actually, in that in the DbContext it's actually put this
code in that we don't really need so for now.
I'm just going to delete that. We will look at other ways of doing the configuration,
it was just a reference missing there. And so now if we run that then we can see that it's not actually working correctly.
What we can see it says is 'DBFirstLibrary.Books', 'DBFirstLibrary.Authors'. And the reason for that is, the code I'd written simply relies on both Book and Author having a ToString. And if you remember from the previous
video, I'd put those in fairly simply, but those aren't generated for us.
You can see in Books and in Authors we don't have a ToString. Now
we've got to be a bit careful here, because these two classes
are generated for us by that 'scaffold-dbcontext' tool,
and if we ever made an update to the database and then wanted to regenerate our classes, we would basically be splatting over completely that generated code.
So if we, having generated the code, start putting any functionality into it,
we're running the risk that that may be lost. And that possibility has been anticipated by the fact that all of these classes - so
the DbContext and Authors and Books - have all been declared as 'partial'. 'partial' is a lovely keyword in C#,
designed for exactly this situation. It means that we can put
a bit more information about this class into a different file. And in that different file that's where we can put the ToString,
and that will be safe. And if this file's ever regenerated, fine, but we won't lose our own code. So what we then need to do is something like this, is to, say, add another class. Now the temptation is to have a class of exactly the same. And in fact we do have to have a class of the same name,
but what we don't want, what we can't have is a file of the same name. So if I
were to try and say 'Books' and do Add, we'll get an
error that Books already exists. And there's two ways around this.
One is we could just simply have a file with a different name but give it
the same class name - so we could have a file called 'BooksAdditions' with a class called 'Books'. But the way I prefer to do it is to use
folders. So if I just add a folder in here called 'Additions' and then into there add a class called 'Books', that's now allowed because, although it's the same file name,
it's the same file name in a different folder. But then the thing to watch out for is, because this is in a different
folder, by default it has a different namespace. And this Books here has to be in the same name space of that Books there.
So the other thing we have to do - remove that 'Additions'. So they're now in the same namespace. And then say 'public' and we have to have 'partial' on both fragments. But now we can see the magic, because if I just do an override of 'ToString' in here, and then say 'return'. If I were to say
'this.' we can immediately see Id and Title and YearOfPublication
are all just there waiting for us, even though they're actually declared
in this other Books, they're available to us. And so all I need to do here
- in fact let's be really lazy, let's just steal that code there that I had in the original one. Pop that in there and then a similar
sort of thing for Authors, so call it Authors then take off
the extra bit on the namespace, then 'public partial' and then in this case I'm going to do 'override'
of 'ToString' and this one just returns the 'Name' which again is there,
so we can see that that's hooked up to that Authors. And now when we run the code we'll see it works exactly as it did before.
So remember, we're filtering there on authors since 1900, so the ToStrings being called, everything else exactly the same as it was before. So
things like the fact we had to 'Include' the Author, just the same as it was before. But in that case we got the system to generate the classes for us based on an existing database. And we also saw how the fact that since that is generated code, it might get overwritten.
So any additions we want, we put separately using partial classes.
So there we have it for our first look at Entity Framework Core. We've seen how we can do Code First - so write the code get
it to generate the database. We've seen how we can do this slightly
strangely named Code First From Database, where we had the database, but we got it
to generate the code. But it's still called Code First because
the code is the definitive item that tells us what we're doing.
Next time I'm going to take a bit of a step back and look at these features
in EF6, the slightly older technology. And we'll see why there's another option there and that's why we have the strange name of
Code First From Database, because there we've got a genuine DB First. But I hope
that's been helpful. If you enjoyed it, do subscribe. Do ask any questions. And I'll
see you next time with a look at EF6.