Our app is growing fine, yet it's starting to show its limits. There's no way we can add, update or delete our items, or properly implement our Login View, since it would require handling some sort of user authentication in terms of credential storage and session persistence, to say the least. Truth be told, we can't even say we're actually showing something close to our original plan. Our items don't resemble open-source game entries at all, they're more like a generic container put together in a rather random fashion by a sample, method acting as a Dummy Data Provider.
It's time to get rid of that provisional demo and start working on the real thing. We won't use Angular for that, as what we need to implement has little or nothing to do with the client-side portion of our app. Nonetheless, we're fully aware of the fact that most entities of the Data Model we're about to build will have their correspondence in an Angular model class, just like we did with the C# ItemViewModel and the TypeScript Item classes, as long as we don't forget we're doing this to feed Angular, we'll be good.
We need to do a lot of things here, so it's better to avoid wasting our time by introducing the whole data model concept, as well as the various meanings of these two words. The experienced reader, as well as the seasoned developer, will be most likely aware of all the relevant stuff. We'll just say that, when we're talking about data model, we don't mean anything more or anything less than a lightweight, definitely-typed set of entity classes representing persistent, code-driven data structures that we can use as resources within our Web API code.
We used the term persistent here for a reason: we want our data structure to be stored in a database. That's rather obvious for any application based upon data. OpenGameList
, won't be an exception since we want it to act as a directory of open source games, which is more than just requiring a database, our app is basically a database by itself.
We're going to create ours with the help of the EntityFramework Core (EF Core), the well-known open-source object-relational mapper (ORM) for ADO.NET developed by Microsoft. The reasons for such a choice are many:
You might be wondering why we're choosing to adopt a SQL-based approach instead of going for a NoSQL alternative. There are many good NoSQL products such as MongoDB, RavenDB, and CouchDB that happen to have a C# connector library. What about using one of them instead?
The answer is rather simple: they are not supported yet by the EF Core 1.0.0, which, at the time of writing, happens to be the latest stable release. If we look at the EF Core team backlog we can see that non-relational database providers, such as Azure Table Storage, Redis, and others, are indeed mentioned for upcoming support, but it's unlikely that we'll be able to see any of them implemented within the EF Core's future releases as well.
In order to install the EF Core, we need to add the relevant packages to the dependencies
section of our project.json
file. We can easily do that using the visual GUI like we did for the Newtonsoft.json
package in the following way:
OpenGameListWebApp
project.EntityFrameworkCore
keyword.
Next, install the following packages:
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Design
Alternatively, we could add the relevant packages manually within the project.json
file as follows (new lines highlighted):
"dependencies": { "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Routing": "1.0.0", "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0", "Microsoft.AspNetCore.StaticFiles": "1.0.0", "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0", "Newtonsoft.Json": "9.0.1", "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0", "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0", "Microsoft.EntityFrameworkCore": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.EntityFrameworkCore.Design": "1.0.0-preview2-final", }
As usual, the advantage of doing it manually is that we can keep the packages more organized by separating them into commented blocks.
Among the installed namespaces we can easily see the presence of the EntityFrameworkCore.SqlServer
, a highly versatile connector providing an interface with the whole MSSQL server database family: SQL Server 2008-2014, as well as the Express and Compact editions for personal and development usage.
We're free to choose between using one of them and picking another DBMS engine such as MySQL, PostgreSQL, or any other EF-compatible product. Should we take this decision now? It entirely depends on the data modeling approach we want to adopt. For the time being and for the sake of simplicity, we'll be sticking to it.
Now that we have EF installed, we have to choose between one of the three available approaches to model the data structure: model-first, database-first and code-first. Each one of them comes with its fair amount of advantages and disadvantages, as the experienced readers and seasoned .NET developers will most certainly know. While we won't dig too much into these, it could be useful to briefly summarize each one of them before making the choice.
If you're not familiar with the Visual Studio IDE design tools such as the XML-based DataSet Schema (XSD) and the Entity Designer Model XML (EDMX) visual interface, the model-first approach can be rather confusing. The key to understanding it is to acknowledge the fact that the word model here is meant to define a visual diagram built with the design tools. This diagram will then be used by the framework to autogenerate the database SQL script and the data model source code files.
To summarize, we can say that choosing the model-first option means working on a visual EDMX diagram and letting EF create/update the rest accordingly.
Given the disadvantages of the model-first approach, we might think that the database-first approach might be the way to go. This could be true if we either have a database already or don't mind building it beforehand. That being the case, the database-first approach is similar to the model-first one, except it goes the other way around. Instead of designing EDMX manually and generating the SQL script to create the database, we build the latter and then generate the former using the ED Designer tool.
We can summarize it by saying that choosing the Database-first approach means building the database and letting EF create/update the rest accordingly.
Last but not least comes the EF flagship approach since EF4, which enables an elegant, highly efficient data model development workflow. The appeal of this approach can easily be found in its premise: the code-first approach allows the developer to define model objects using only standard classes, without the need for any design tool, XML mapping files or cumbersome piles of autogenerated code.
To summarize, we could say that choosing the code-first approach means writing the Data Model entity classes we'll be using within our project and letting EF generate the database accordingly.
As we can easily see by judging the advantage and disadvantage listings, there is no such thing as an overall better or best approach. Conversely, we could say that each project scenario will likely have a most suitable approach.
Regarding our project, considering the fact we don't have a database yet and we're aiming for a flexible, mutable small-scale data structure, adopting the code-first approach would probably be a good choice. That's what we're going to do, starting from the following paragraph.