4.1 Setting Up the Dependencies
Now it's time to choose how we store data. We're going to use a Microsoft SQL database and interface with it using Entity Framework Code First. This will also affect how we manage users. We need to prep our project with all the extra components (made easy with NuGet) and write our database context.
1.Introduction2 lessons, 05:56
2.Managing Posts3 lessons, 38:13
3.Tag Management2 lessons, 27:29
4.Storing Data3 lessons, 47:22
5.Testing Functionality2 lessons, 33:06
6.User Management3 lessons, 1:11:48
7.Security and User Authentication2 lessons, 34:17
8.Enhancing the User Experience2 lessons, 36:38
9.The Front-End2 lessons, 36:01
10.Conclusion1 lesson, 01:22
4.1 Setting Up the Dependencies
The time has finally come where we need to decide how we are going to store our data. And there are many things that go into this decision, because we can store our data anywhere that we want. But we have to think about everything about our application. Not only where we want to store our posts, but we also need to think about how we are going to manage users and authenticate them. Are we going to roll our own solution or are we going to use ASP.NET Identity? And if we choose Identity, are we going to use the default behavior given to us with Entity Framework? Or are we going to write our own storage provider? So there's a lot of things that we can do, but I'm going to make it simple. We're going to use the tried and true solution of using a SQL database as our data store. We're going to use Entity Framework to interface with that database, and for the user management and authentication we are going to use ASP.NET Identity. Now, we first of all should set up our application to use Identity because we need a database context. This is an object that we will use to interface with our database. And, if we don't start with Identity then we can end up with two different contexts. And we don't want that. We just want one context that we use for authentication as well as getting and manipulating our posts within the database. So let's go to our references for the project, and we want to add some new GetPackages. So click on manage new GetPackages. And we want to do a search for Identity. Now there are many packages with Identity in their names, but what we are concerned with is this Microsoft ASP.NET Identity Owin So we want to install that. It's also going to install of the dependencies for that package and then we also want to install this Microsoft ASP.NET Identity Entity Framework. And this should give us everything that we need and if not, we can always come back and install the packages that we need. So, now we need a start-up class for Owin. This is a requirement for Owin. So, let's right-click on our project. And let's go to, Add New item. And we want to do a search for startup. This is going to have a template called, Owin StartUp Class. Let's change the name of this to simply, startup.cs. And this gives us this configuration method. Inside of here we want to configure our authentication. So we're going to use this app object that we get as an argument and there's a method called UseCookieAuthentication. And we want to new up CookieAuthenticationOptions. And if I typed that correctly, I did not. It's within Microsoft.Owin.Security.Cookies and you can see why I don't want to type all of that. So, we have cookie authentication options, and there we go. Let's add a using statement, and then we need to set some properties for this CookieAuthenticationOptions object. So the first one is AuthenticationType. And we want to set that to DefaultAuthenticationTypes. We need a using statement for Microsoft.AspNet.Identity, and this has application cookie. And then there's a property called LoginPath. This tells the authentication system that if a user is not logged in and we tell them that they need to be logged in, it's going to automatically redirect them to whatever is specified within LoginPath. So for right now, let's do /account/login. We can always come back and change this whenever we need to. But this is going to be everything that we need inside of this start up class. So, we can close this file. And next, let's go to our Models folder. Because we have our PostFor model, let's also add a model for our user. Now, we're going to use the Identity User class as a base for our User class. Because this gives us basically everything that we need, but we also need a display name. So let's add a new class. And we'll just call this CMSUser. And this is going to inherit the IdentityUser class. We need a using statement for Microsft.AspNet.Identity.EntityFramework. And then we will simply add a new property that is a string called DisplayName. Next, we need to create our database context class. This is what we're going to use to, essentially, interface with our database. So, in our data folder, let's add a new class called CmsContext. And we want to inherit from IdentityDbContext. This is a generic class. And we specify the class for our user and that is CmsUser. And then we need a property that represents our posts table. So public DbSet. We need a using statement for System.Data.Entity and we need to specify post there. So we have a DbSet of Post called Posts and this is going to generate a table called Posts and all of our posts are going to be set in there. Now we should also override the OnModelCreating method because we need to do some configuration. So protected override OnModelCreating. We first of all want to call the base classes OnModelCreating method, but then we want to use our modelBuilder and the Entity method. And we want to configure our Post Entity. We want to say that we have a key, so we will use the HasKey method. And we will specify the Id property as our key, but we also want to say that this is not controlled by the database. This is something that we are going to do. So we are going to use the Property method. We will use the same expression where we specify the Id property on our post class and we have a method called HasDatabaseGeneratedOption. And there's an Enum called DatabaseGeneratedOption that is inside of the System.ComponentModel.DataAnnotations.Sc- hema namespace. Man, these namespaces. And we want to specify that this is none so that the Entity Framework won't do anything as far as creating this table with anything special for the id. It of course is our key, but it's not going to be an auto-generated field, so we simply specify none. Now, we're not done with configuring our Context class. We have one other thing that we need to do. But before we can do that, we need to go to our Post class and make some modifications. Let's first of all mark the properties that are required with the Required attribute. Those are the Title property and the Content property. And this is going to give us two things. From an Entity Framework standpoint, it's going to generate a column called Title, and another one called Content, that are not nullable. So for every Post record in the database, we will have a Title and Content. And from a model standpoint, from validation, it ensures that the user has to supply a Title and a Post content, otherwise they're going to see the form again and they have to fill in that information. Now notice I'm not putting required on Id. The reason being, because as far as Entity Framework is concerned, it is already considered our key. So it cannot be null in that case. But from a model standpoint, we need this to default either empty or null because we're going to automatically generate the Id based upon the title. But if the user wants to supply an Id, then, they can. We, of course, need to do some checking to ensure that it is a valid Id, and it's not currently in use but we will get to that later on. So the date created doesn't really need a required attribute from an entity framework standpoint it's going to generate a date-time field that is not nullable, because in a .NET DateTime is a value type. It cannot be null, unless if it a nullable DateTime, in which case we don't want that to be required either. First of all, because we want it to be nullable within the database. And every post may or may not have a published date. Now, we do need to make some modifications for our tags, because currently we do have an IList of string. And that cannot be related to a single column within a database. So we are going to create a string property that is going to be basically the serialization version of our tags property. So let's first of all create a private IList of string called _tags, and we can go ahead and new up List of string, that way we have a collection to work with, and then we need to modify the get and set of our tag's property. So instead of just the plain Jane get and set, we will have a actual getter that returns _Tags and then the setter is going to set our _Tags = value. So very simple as far as our tags property is concerned. But ,we need a property that we can use with in the data base. So we need a public string field which we can call CombinedTags and for the getter and the setter we are going to be working with our underscore tags. Let's start with the getter. We want to return a string representation of all of our tags. So we can use string.Join, we want to join all of our tags separated by a comma, and then the collection is _tags. So that getter is very simple, and really the setter is simple as well. We are simply going to set _tags = value, but we want to Split that on the comma because we are taking a string and turning it into a list. But we should also trim any white space. So let's use the select method and then we will call the Trim method. And then finally, we need to return a list. And then finally, we need to modify our AuthorId. This is not going to be an integer, this is going to be a string. But we can also have a navigation property to the User object. So programmatically, whenever we are working with a post we can have post.author and then .displayname if that's something that we wanted to do. So let's add a public virtual CmsUser and we'll just call this Author. We simply need a get and set, but we also need to specify the foreign key. So, we will use the ForeignKey attribute, we need a using statement for System.ComponentModel.DataAnnotations.Sch- ema. And then we simply want to specify AuthorId and really it's this change that we needed to make in order to make the final change inside of our context. So here we want to use our modelBuilder once again, we want to use the Entity method and specify Post. And then we want to say that we have a required e.Author. And that should be everything that we need for our context class as well as our post class. Now the last thing that we're going to do in this lesson is seed our database with a user. Because now that we have a relationship between our posts table and the user's table we can't create a post and add it to the database without specifying the user. So let's go to our app_start folder. And let's add a new class. And let's call this AuthDBConfig. And this is going to have a static method that we call RegisterAdmin. So, this method is going to ensure that we always have a user called admin. So, if one is not there, then we are going to create it. So, the first thing we need is our context. So, we will new up Our CmsContext, and we will add a using statement for our data namespace. But that's not the only thing we need. Next we need our user store. So let's call it userStore, and we want to newUserStore of CmsUser. So we need two using statements, one for the useStore class. The other for are CmsUser class. Then for this constructor, we pass in our context. And then we need a manager. So let's call this userManager and we will new up UserManager of CmsUser and we need a using statement for userManager and then we will pass this constructor our userStore. Now this is a lot of typing. And this is a pattern that we are going to repeatedly use. But we can simplify this by creating our own classes that inherit from UserStore of CmsUser and userManager of CmsUer. But we will do that whenever we focus on our authentication and user management code. And in this case it's nice that we have our UserStore because we are going to use that to ensure that we have an account called admin. Otherwise we would have to use the user manager. But in that case we have to specify both the username and the password, and hopefully we won't always know what the password is for the admin account. So the first thing we need to do is check to see if we have a user called admin. So we're going to use the userStore. And we have several FindBy methods. And notice that they are all asynchronous. And ideally we would be writing asynchronous code, but for now we're just going to stick with synchronous. The one that we want is this FindByNameAsync. And then we want to check to see if we have an admin account and then we can use dot result to run this synchronously. Then we can check to see if user is null and if it is then we want to create this user. So, let's create an adminuser variable. And we want to new up our CmsUser. And we can use initializers in text to set the UserName = "admin". The e-mail really isn't important in our case. But let's do email@example.com. We're not going to be sending an emails so this is going to be fine ideally it would be edit a valid email address one that would go to us or something and then our display name for this user is administrator. So after we create this user we simply need to add it to our user manager, so we will do that. We will call the Create method, we pass in this adminUser object, but we also pass in a password. So let's do Passw0rd, with a zero, and then 1234. This should be long enough. It also has an uppercase and lowercase character, as well as some numeric characters. And that should be it, although we need to call this RegisterAdmin method. And we can do that inside of our global.asax. So right here, after we set up the route, we can do the AuthDbConfig and we need a using statement here, and then we recall the register admin method. So let's run this, and everything should be created for us although this does not build, so let's look at the problem. Cannot implicitly convert type string to a path string, so what do we have here. We need to call the path string constructor. And pass in our string literal as the argument. Okay, so now let's try to run this and hopefully everything is going to work. Now it may take a few seconds for Entity Framework to create our database table and all of that but since we are seeing this here, and we didn't get any exceptions. Then it's almost safe to say that everything worked fine. But let's go back to Visual Studio, let's go to Solution Explorer, and let's click on one of these icons that is not displayed. I guess we need to stop debugging. And there's an icon that says, show all files. So if you click that, and then let's go app_data and refresh that. Well nothing is showing up there. Well it's probably because I have SQL Server Express installed. If you don't have it installed, it will create a database file inside of app_data. And then you can double click on that and it will pull up the server explorer so that you can look at all of the data. But since I have SQL server express it probably created the database here and yes this default connection database is it. So if I go down to tables and we can see we have the ASP.NET tables, one is for the users, which we can look at that one row that we have. You can also see that we have the Posts table as well, and if we look at that columns, we see that we have an Id which is a string, which can not be null, Title cannot be null,, Created cannot be null, Published can be and CombinedTags can be as well. Now let's look at the information inside of our user table. We have this user Id. We also have the display name of administrator. The email's firstname.lastname@example.org and the password is automatically hashed for us, so we don't have to worry about that. And then the user name is admin. So, we have our database all set up and ready to go. Now, we need to start flushing out our repositories, and we will get started with that in the next lesson.