One of my bugbears when it comes to software
development is overuse of the word 'model'. Because we see the word 'model' used again and
again for all sorts of different things which are only vaguely connected. Now it's understandable,
because what we're doing when we develop software is we are trying to make a model - a software
model - of the real world, so the term does come up a lot. But I think in certain situations
we do need to be careful, because it can cause confusion. And particularly one place we need
to be careful is in the world of MVC alongside Entity Framework, because the word 'model' is used
in those two different technologies for similar, but different things. Let's just consider the sort
of setup we have. So a typical MVC application, we'll have at the bottom some kind of database,
and then we'll have a controller which extracts data from the database using Entity
Framework, and then we'll have the controller sending information to the view to display it as
HTML. And the classes that are involved in both of those layers of transfer are often referred
to as 'models'. So we have the model classes in Entity Framework representing the data
in the database and then we have the model in MVC - that's what the 'M' stands for - taking
data from the controller to the view. And in very, very simple cases - which is what you typically
get when you're taking a course or reading a book or something like that - the structure of those
two classes will be exactly the same. But very quickly in any serious application they will start
to diverge. So let's suppose we've got a situation where all you want to do is register a user for
login on our site. So in the database we're going to have a table with - at a bare minimum -
two columns: one for the username, one for the password. So the model class representing
that table will just have those two properties. But when it comes to the view, we're actually
going to have the username plus the password plus the confirm password. So typically, just
in case the user mistypes their password when they're registering, we have that confirmation.
So the model that would be taking the data between the view and the controller would
actually have three properties. So immediately, we can see a situation where there is a difference
between those two structures and it will happen again and again and again. So the terminology that
I personally use - and a lot of people use - is this: the model that takes data from the database
to the controller we call the 'entity model', or in fact more normally just the 'entity', and then
the classes that take data from the controller to the view and back, I will simply call those the
'model' classes, because they are the M of MVC, so why change that? So we talk about the 'model'
as the MVC part of it and then the 'entity' as the Entity Framework part of it. Some people will
call the model of MVC the 'view model' to make the distinction, which I think is kind of clear,
but the problem is 'view model' is also used in another related design pattern: MVVM: Model
View ViewModel, and it means something slightly different there. So I don't like to use that
term, because it can cause a bit of confusion. So we're going to stick with 'model' for MVC;
'entity' for Entity Framework. Now, let's take a look at some of the practical upshots of that.
So back to our application that we've seen before, and now we can see in this BookLibrary, we can
be very clear this Book and this Author - these are entities. So those are classes representing
the data in the database and at the moment if we look at our controller, we are also using
those as models, because for example, if we look at this Details we can see we're getting
back a book - and that is of that Book type from the BookLibrary - so that's the entity. And
we're sending that straight through to the view. Works okay, but it doesn't necessarily make a
lot of sense. One of the reasons that you'll immediately see our divergence between models and
entities is to do a thing known as 'flattening'. So at the moment, if we run this up ... we
can see that this is the books index page. We've got the title and the year, but then also -
not really part of the Book class - we've also got some of the Author information. But we're not
displaying all of the Author information. If you look at the actual structure of our Author
entity, you can see that as well as the Name we've got a DateOfBirth on there, but we're not
interested in that because all we're trying to show is the Book and the Author name. But if
we look at the SQL that's generated, you'll see that it has fetched everything, so it's done the
JOIN between Book and Author, and it has read, for example, the DateOfBirth of the Author, even
though we didn't really need that. And that's not going to be a huge overhead, but it is a slight
overhead and it could add up if you've got lots of data that you're not necessarily interested in. So
what we want to do is this process of flattening where we take the combination of the Book plus
the Author as entities and turn it into a single Book model that we can display easily on the page.
So conveniently we've already got in our MVC a folder called 'Models'. To be honest, quite often
in a big application I will have a whole separate DLL for my models, but here we'll just put
them straight into the MVC application. And so what I'm going to do is add a new class and I'm
going to call this BookModel. So although I call my entities just the name of the thing - like
Book and Author - I could call the model just Book because it's in a different namespace, but I find
that will get a bit confusing, so I normally call my models '...Model' just to make a bit clearer.
So we'll add that and then the information we want to have in our BookModel is going to be similar to
what we have in the Book, but slightly different. So what I'll do actually is I'll pinch some of it.
I'm going to take all of that and put it in here, but the big change I'm going to make
is I'm not going to have the Author as an Author, I'm just going to have the Author as
the name, and so we'll just make that a 'string'. So that's our flattened version. We're going to
get the Title and YearOfPublication from the Book entity, we're going to get the Author name from
the Author entity. But we're going to squish them together into a single class for the model that
we present over to the view. Then the next thing we've got to do is obviously transfer that data
over from the entities into the model. And one way we could do that - not actually necessarily the
best one, but we'll look at it - is just to do it with a constructor. So we could have a constructor
for BookModel and we could give that a parameter of type Book - so that's book from
our BookLibrary - call that 'entity' and then we could copy over the
information. So 'Id = entity.Id; Title = entity.Title; YearOfPublication
= entity.YearOfPublication;' and then 'Author = entity.Author.Name;' So
that's got all the data in there. We're of course assuming that we have done the appropriate eager
loading so that the Book does have an Author, but we'll deal with that in the controller,
because in the controller - let's just do this for our Index for now - what we've got there, that
is our List of Books. But if I just in here do a '.Select(' and then 'b => new BookModel(' passing
in the book. So that will call the constructor for the Book and then we've now got 'books 'as
a list of BookModels. What we'll then need to do is delete the old 'Index' because
that was the index based on the Book entity and then generate a new one. Still
going to be called 'Index', still going to use the 'List' template, but now we're going to
have it for BookModel. So if we just add that, we can see it's just generated the
standard list page. If we run it up, and we can see it's listed out the books and
the authors, but this time based on that single flat class BookModel - not the separate Book
and Author. However, if we take a look at the generated SQL, you can see it's still not solved
one of those problems. It's still asking for the DateOfBirth of the Author, even though we never
actually use the DateOfBirth. And the reason it can't work things out properly is because we've
hidden away from Entity Framework the bit of the code that decides what parts of Author we want.
So if we look in there again, we saw our BookModel had just the need for 'Author.Name', but because
that was inside the constructor, it can't really be seen. And so all that work has to be done after
the data has been loaded from the database. We can get around that. What we need to do in fact is in
here we need to do things slightly different. We don't call the BookModel constructor; we have to
initialize everything using property initializers, because those will be visible to the Entity
Framework provider and therefore it will be able to translate those into SQL. So what I'll need
to do, back on the BookModel I need to give it a default constructor and then if I take
all of that code and just pop it in there, and then obviously we need
to change 'entity' to 'b' and we need to change those semicolons to commas,
and that should function in exactly the same way. So we're still getting the same results
but now if we look at the generated SQL, we can see that it hasn't selected the DateOfBirth
from the Author. The only thing we've got from the Author was the Author's name. That's
because it has been able to look at that in the member initializer and decide precisely
what needs to be fetched. And in fact we can take that one step further because we don't now need
to have the 'Include', because LINQ to Entity Framework can see that we want the Author's Name, it can
now work out for itself that we therefore need to pull in at least part of the Author and
generate the JOIN. So if we run it like that still works and still has the efficient
query where we're just pulling in the Name. So to a large extent, we can forget about using
the 'Include' to do our eager loading, because if we use this approach we have an even more
precise way of doing it. We are saying not just that we need the Author but precisely that we
need the Author's Name and nothing else. And although it's not a huge saving, if there's
a lot of data coming back from the database, reducing that by potentially quite a large
amount here would be quite a useful thing to do. There is still a slight downside to it though,
which is now our code is rather ugly. If we look over here there we've got nice encapsulation, nice
separation of concerns. The BookModel knows how to initialize itself, whereas because we can't use
that, now we've got to duplicate that knowledge of how a BookModel should initialize itself into
the controller, which isn't really very nice. And if we're doing this again and again - which we would
be in the other methods of the controller - we would have code duplication. So how do we have
the best of both worlds? Well, the trick we can do is to use an extension method. So what we can do
is in here - put it in the same file because it is so tightly related to the BookModel class
- I'm going to have a 'public static class' called 'BookModelExtensions'. And in there
I'm going to have my 'public static' method and this is going to return an 'IQueryable' of our
'BookModel'. I'm simply going to call it 'ToModel' and then the parameter it's going to take - so
this is the data type it's extending - is going to be 'IQueryable' of the entity 'Book', okay? And
we'll call that 'source'. So what we're doing with this 'ToModel' method is defining a way of mapping
a collection of Book entities onto a collection of BookModels. What we then need to
do is, we're going to say 'return source.Select' - so you'll notice really
very similar to what we were doing in there - and then 'b =>' and in fact let's just steal
all of that, because it is that similar, so right down to there. Let's take that code and
put it into there, so exactly the same code but we've put it into the file that makes more sense.
We've put it alongside BookModel, not put it in the controller. And then in the controller, all we
now need to do is in there, just say 'ToModel()' and you'll see it's picked that up because we
already have the namespace in there. But 'books' is - although it's a DbSet there - it implements
IQueryable. So that's our IQueryable<Book> that we are extending. And then ToModel does the
work for us. And so now if we run that up we can see it's still working, and again we
can see that the query is really as efficient as it possibly could be. And then once we've
done that, we can start reusing all of that. So let's, say, go to our Details. And
the way we do this now is we still need our '_context.Books', we don't need the
'Include' again, instead we simply put the 'ToModel()' there. So we put it early on
before we do something like the 'Single', so that all this work can be done in the
database, and then we have our selection. Once again details previously was using the entity
directly, so let's get rid of our 'Details' view and replace it with one generated for
the model ... Details ... BookModel, and then get rid of the context ... add that. And then
let's just go to our 'Index' and change the link down the bottom here so the Details
is going to work. And so if we run that up and go to Details there, so there we can see we've
got the information. Again if we look at the query we can see that's where it selected the single
Book, but again the only bit of information about the Author it needed was the Author's Name. So
quite a nice way, where we can have reusable code, we can have efficient code because it's optimizing
the SQL and we do that, as we saw there, by creating the model and then each model we use
an extension method that we can call 'ToModel' to convert a collection of entities
into a collection of models. The only slight downside that some
people have with this kind of approach is when you're doing this bit of the mapping -
whether you're doing it there in the constructor or here in the 'Select' - it is a bit repetitive,
because very often you'll find in the majority of cases the name of the property and
the type of the property in the model is the same as the entity. So we're mapping
over 'Id' to 'Id', 'Title' to 'Title', 'YearOfPublication' to 'YearOfPublication'.
The only one that's a slight difference is it's 'Author.Name' going to 'Author'. And so you
could well imagine that that's the sort of task you might want to automate. And in fact there are
a number of libraries available for that. Probably the best known one is one called 'AutoMapper',
which is available through NuGet. So there we can see that's AutoMapper - lots
and lots of downloads, very popular one. Personally, although I've used AutoMapper, I'm not
a fan of it because although it does save you that effort we're talking about here of writing this
very repetitive code, the problem is that it can then risk doing things for you that you didn't
really want it to do. An example is a situation where I'd been using AutoMapper, later on I added
a new property that I only wanted to be updated in certain circumstances. So it was a field that
wanted to be set when an object was created but not when it was edited or anything like that.
And I spotted that it was being changed - even though I hadn't coded this - when I was doing the
edit. And so first thing I did was a search for this particular property name to see where it was
being referenced - couldn't find hardly anywhere in the code where it was being referenced. And
then I realized what had happened was AutoMapper had simply automatically started mapping it,
even though I hadn't explicitly told it to. Now AutoMapper has lots of configuration where we can
say, 'no don't do it for this particular property' or 'do it in a certain way,' but the thing is, if
you don't configure it, it does it automatically. Personally, given the small, one-off effort up
front, I prefer to write my own code to do this and then if I have to change it - if I need to
put a breakpoint, if I need to do a search - then it's all available there for me. But plenty
of people like AutoMapper, so I'm not going to tell you not to use it. You can use it just
like this, in both these ways. So you can use it to generate an efficient SQL query when using
Entity Framework as well. So that's it. We've looked at the relationship between models and
entities - entities relating to Entity Framework, models relating to MVC. We've seen how, although
they start out the same, when you get any degree of complexity they start to diverge. And we've
also seen how we can actually use them to really produce efficient queries. We no longer need
to worry about eager loading in terms of the 'Include' method, because now we can use this
really precise targeted loading that only loads what is required in the model, rather than
loading everything that the entities may provide. Hope you enjoyed that. If you did, please click on
the 'Like' button because that helps promote us in the YouTube rankings and therefore more people
will get to see the video. And also of course, do subscribe so you can see
all the other videos as well.