Introduction
This multi-part article demonstrates the mapping of C# enum
values to string
values in database tables via EntityFramework Core 2.1 (EF). It addresses the mapping of enum
values in one-to-many and many-to-many relationships with application entities. It does this in the context of an ASP.NET Core Razor Page application.
EF is an Object-Relational Mapper (ORM). In applications such as this sample, there are two "worlds". One is the object world that exists as an object model in C#. The other is the relational world that exists in a relational database, like Microsoft SQL Server. These two worlds are not consistent with each other. The function of an ORM, like EntityFramework, is the bridge these two worlds and facilitates the transfer of data between them.
In Part I, we will review the following:
- The conceptual and working object models for the sample application
- Creation of the Visual Studio 2017 (VS2017) solution containing an ASP.NET Core Razor Page project
- The initial creation of the application object model in C#, the
Customer
class - Setting up the application user interface (UI) layout and home page
- Scaffolding the Customer CRUD (Create-Read-Update-Delete) pages with EF initialization via the
QuantumDbContext
class. This includes the creation of the application database in a localdb
SQL Server instance. - Introduction of the configuration of object model classes for interaction with EF in separate classes to facilitate the design of database tables
- Implement the Customers/Index page
Part II. Complete CRUD functions for Customer
s
Part III. Create Project
and ProjectState
entities and implement a one-to-many relationship between ProjectState
and Project
s
Part IV. Add Skill
entities (Skill enum
, SkillTitle
and ProjectSkill
) and implement a many-to-many relationship between Project
s and Skill
s
Background
An enumeration is a data type consisting of a set of named constants. The names of the constants tend to be mnemonics. A mnemonic is defined as a device to assist in memory. However, there is a tendency to use mnemonics as constant names in enumerations. As a developer, the name that I might use would be cryptic to users. Part of the motivation to move from command line interfaces to graphical user interfaces (GUI) derives from the use of mnemonics as commands. Furthermore, the default mapping in an enum
value is to an integer. The meaning of the integer value defeats the concept of a mnemonic.
There are some advantages to using an enum
. The first is the set of named constants and is relatively small and facilitates self validation. If we attempt to use a value that is not defined in the enum
, we generate an error.
The sample application implemented here is for a fictitious engineering and technology company, Quantum Engineering Technologies. Quantum primarily serves the oil, gas and chemical industries. The key entity is a Customer
. Quantum serves a number of Customers
. Each Customer
, in turn, has a number of Projects
. Each Project
has a state defined in an enum
, ProjectState
. There is a one-to-many relationship between ProjectState
and Projects
. The ProjectState enum
permits the definition of a State Machine to assist in managing the Project
workflow. The Project State Machine is beyond the scope of this article.
A project requires a number of Skills
. This application defines a Skill enum
. There is a many-to-many relationship between Projects
and Skills
. These relationships are shown in the following figure representing the conceptual object model in C#.
Conceptual Object Model
The enumerations contain essentially static data. The values are mnemonics, especially in the Skill enum
. Most users do not readily recognize that a "CtrlE" is a control engineer and that a "DataSci" is a data scientist. One objective here is to implement a scheme that presents these values in the user interface (UI) in a way users can more easily understand. To accomplish this, we use the working object model as shown below.
Working Object Model
The ProjectStateDescription
entity maps the enum
values from ProjectState
to a Description string
describing the Project
state. The SkillTitle
entity maps the enum
values from Skill
to a Title string
in SkillTitle
. The many-to-many relationship between Project
and Skill
is expressed via the ProjectSkill
entity. We will discuss the significance of this object model later.
Development Environment
The sample solution was created in the following environment:
- .Visual Studio 2017 (VS2017)
- ASP.NET Core 2.1
- Entity Framework Core v.2.1.4
- NuGet Package Manager v.4.6.0
- SQL Server LocalDb
- SQL Server Management Studio
Using the Code
We begin by creating an empty VS2017 solution, QuantumEngSolution
. There is an option to create a project, but a solution will be created anyway. As a mater of personal style, I prefer to create and name the solution explicitly.
Create QuantumEngSolution, an Empty Vs2017 Solution
Click "OK" to continue. Next, add an ASP.NET Core 2.1 Web Application, QuantumWeb
.
Add QuantumWeb ASP.NET Core Web Application to QuantumEngSolution
Configure Project as an ASP.NET Core 2.1 Web Application
This configures the application without authentication and to use HTTPS. If you encounter problems in debugging and testing, you will need to enable HTTPS in your VS2017 installation. (See this link.)
After clicking "OK", the initial Solution Explorer is shown below:
Initial Solution Explorer
Next, we add a new folder, Model, to the QuantumWeb
project. This folder will contain the files in our object model. For a larger application, we would create a Class Library project to hold the object model. Doing so would be consistent with the practice of separation of concerns and would facilitate the use of the object model in multiple applications. However, since this is a sample demonstration, we will keep the object model in the same project.
Now, we will create the model classes in the Model folder starting with the Customer
class.
Create ~QuantumWeb\Model\Customer.cs
Initial Customer Class
namespace QuantumWeb.Model
{
public class Customer
{
#region Constructors
public Customer()
{
}
#endregion // Constructors
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public string CustomerContact { get; set; }
public string CustomerPhone { get; set; }
public string CustomerEmail { get; set; }
}
}
Start Building UI and Data Access
Now that we have the beginnings of an object model, we can begin building the user interface (UI) and data access functionality. ASP.NET Razor Pages can be configured to use layout pages. The primary layout page in the Solution Explorer is shown below.
Edit the Primary Layout Page
Initial _Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Quantum Application</title>
<environment include="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet"
href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position"
asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse"
data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a asp-page="/Index" class="navbar-brand">Quantum Web Application</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-page="/Index">Home</a></li>
<li><a asp-page="/About">About</a></li>
<li><a asp-page="/Contact">Contact</a></li>
<li><a asp-page="/Customers">
Customers</a></li> <!-- Link to Customer pages -->
</ul>
</div>
</div>
</nav>
<partial name="_CookieConsentPartial" />
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© 2018 - Quantum Engineering & Technologies</p>
</footer>
</div>
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<environment exclude="Development">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
crossorigin="anonymous"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
@RenderSection("Scripts", required: false)
</body>
</html>
Next, we create the Customers
Razor Pages and also setup data access. VS2017 provides a way to combine the scaffolding and setup of the UI for Customers
and initialization of the data access. Right-click the Pages\Customers folder, then select "Add" from the popup menu then "New Scaffolded Item..." on the next popup.
Scaffold Customer Razor Pages
A Razor Page scaffold option dialog is displayed. Select the "Razor Pages using Entity Framework (CRUD)" option.
Choose "Razor Pages using Entity Framework (CRUD)"
This configures a set of Razor pages to Create, Read, Update and Delete (CRUD) Customers in a database via EF.
Configure Customer Razor Pages - Create QuantumDbContext
Click "Add" on each dialog to build the solution and configure the Razor pages and EF. The _Viewstart.cshtml file specifies the use of _Layout.cshtml as the default layout.
_Viewstart.cshtml
@{
Layout = "_Layout";
}
The scaffolding using the CRUD pages option produces a number of changes in the project.
Solution Explorer After Scaffolding Customer Pages
We first discuss the modified and created files. There are some problems with the generated code that we will discuss shortly.
Modified appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"QuantumDbContext": "Server=(localdb)\\mssqllocaldb;
Database=QuantumDbContext-268e3954-a42e-460a-97a2-cff0a2cb9dd3;
Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
The scaffolding process added a "ConnectionStrings
" property to the application settings. This shows a database, QuantumDbContext-268e3954-a42e-460a-97a2-cff0a2cb9dd3
, will be created on the localdb
SQL Server instance. The scaffolding generates a default name from the name of the data context, QuantumDbContext
, that was specified. This database will be used for initial development and testing. It is not intended for production.
Modified Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Models;
namespace QuantumWeb
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<QuantumDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("QuantumDbContext")));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
}
}
The Startup
class contains code that is executed on application startup. Its details are beyond the scope of this article. A detailed discussion can be found in Microsoft documentation here. The significant lines are shown in bold above. Here, the startup references the ConnectionString
in the appsetting.json file and tells Entity Framework how to find the database.
Generated Pages\Index.cshtml
@page
@model IndexModel
@{
ViewData["Title"] = "Quantum Engineering & Technologies";
}
<div class="jumbotron">
<h1>Quantum Engineering & Technologies</h1>
<p>
This is a sample application for a fictitious engineering and technology company.
</p>
</div>
<div class="row">
<div>
<h2>Application illustrates</h2>
<ul>
<span style="display: inline !important; float: none; background-color: rgb(251, 237, 187);
color: rgb(0, 0, 0); font-family: Consolas,"Courier New",Courier,mono; font-size: 12px;
font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400;
letter-spacing: normal; line-height: normal; orphans: 2; text-align: left; text-decoration: none;
text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px;
white-space: pre; word-spacing: 0px;">
<li>Sample pages using ASP.NET Core Razor Pages</li></span>
<li>Entity Framework Core (v. 2.1.4 used here)</li>
<li>
Use of C# enumerations with members expressed in database as
strings as opposed to numbers
</li>
<li>One-to-many and many-to-many mapping between entities and enumerations</li>
<li>Use of the Fluent API and IEntityTypeConfiguration classes for configuration</li>
<li>
Theming using <a href="https://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a>
</li>
</ul>
</div>
</div>
We replaced the Pages\Index page with the HTML show above. It gives some information about the sample demonstration. The key item is the "@page
" string
in the first line. This specifics that this is a Razor page.
Generated Pages\Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace QuantumWeb.Pages
{
public class IndexModel : PageModel
{
public void OnGet()
{
}
}
}
Each Razor page consists of a .cshtml file, which defines its UI that is rendered in the layout and a .cshtml.cs file, which contains C# code that has the handlers for HTTP commands, GET
and POST
, primarily. The .cshtml.cs file is analogous to the WebForm code-behind. However, a Razor page has behavior that is more like the Model-View-Controller (MVC) pattern. You can read more about comparing Razor pages to MVC here.
Generated QuantumDbContext Class
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Model;
namespace QuantumWeb.Models
{
public class QuantumDbContext : DbContext
{
public QuantumDbContext (DbContextOptions<QuantumDbContext> options)
: base(options)
{
}
#region DbSets
public DbSet<Customer> Customer { get; set; }
#endregion // DbSets
}
}
This shows the initial data context class, QuantumDbContext
, generated in the scaffolding process with some commenting added to aid readability and to avoid editing errors as the file becomes more complex later. Also, notice the "Error!!!
" comment for the namespace. We will address this later. In EF Core, we are required to have a class derived from Microsoft.EntityFrameworkCore.DbContext
.
With previous versions of EntityFramework
, this was easily done in a Class
Library. As we proceed, we will use EntityFramework
Migrations to create and update the database. As of this writing, there is a problem with using EntityFramework
Core migrations in a Class
Library. If we are to easily separate the data access functions into a separate project and use migrations, it might best be done using a data access service. For now, we will forego that option.
The DbContext
base class contains methods to facilitate interaction with the database and the associated conversions between the relational and object models. We will add to this class as we proceed in development.
As this data context currently stands, the properties of the object, Customer
, in this case are converted to database columns using default transformations. In most cases, the default transformations do not meet the requirements of the application. To remedy this situation, EF defines two techniques to define the desired transformations, Data Annotations (not used here) and the Fluent API. Data Annotations use attributes in the model classes to define the transformations. There are two issues with Data Annotations. First, they are limited and cannot specify transformations available in Fluent API. Secondly, they violate the principle of separation of concerns between object or domain model classes and data access. The Fluent API uses a method, OnModelCreating
, in the DbContext
class to perform the transformations. The transformations can be defined via lambda expressions in this method. However, we can also define transformation classes for the model entities and reference these classes in the OnModelCreating
method as we will now show.
Create ~\Data\CustomerConfiguration.cs
Initial Data\CustomerConfiguration.cs
[Note: The namespace is QuantumWeb.Data
. (See below.)]
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.ToTable("Customers");
builder.HasKey(c => c.CustomerId);
builder.Property(c => c.CustomerId)
.HasColumnType("int");
builder.Property(c => c.CustomerName)
.IsRequired()
.HasColumnType("nvarchar(50)")
.HasMaxLength(50);
builder.Property(c => c.CustomerContact)
.IsRequired()
.HasColumnType("nvarchar(50)")
.HasMaxLength(50);
builder.Property(c => c.CustomerPhone)
.IsRequired()
.HasColumnType("nvarchar(15)")
.HasMaxLength(15);
builder.Property(c => c.CustomerEmail)
.IsRequired()
.HasColumnType("nvarchar(50)")
.HasMaxLength(50);
}
}
}
The mappings between the Customer
class and the database are presented in the following table:
Item | Name | C# type | DB type | IsRequired | Comment |
Customer Class Mappings Table | Customers | | | | |
CustomerId | | int | int | Yes | Primary Key |
CustomerName | | string | nvarchar(50) | Yes | |
CustomerContact | | string | nvarchar(50) | Yes | |
CustomerPhone | | string | nvarchar(15) | Yes | |
CustomerEmail | | string | nvarchar(50) | Yes |
|
First Update to Data\QuantumDbContext.cs
[Note: The namespace is QuantumWeb.Data
. (See below.)]
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Model;
namespace QuantumWeb.Data
{
public class QuantumDbContext : DbContext
{
public QuantumDbContext (DbContextOptions<QuantumDbContext> options)
: base(options)
{
}
#region DbSets
public DbSet<Customer> Customer { get; set; }
#endregion // DbSets
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CustomerConfiguration());
}
}
}
The modelBuilder.ApplyConfiguration()
call injects an instance of CustomerConfiguration
into the data conversion logic.
Generated Pages\Customers\Index.cshtml
@page
@model QuantumWeb.Pages.Customers.IndexModel
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-page="Create">Create New</a>
<!--
</p>
<!--
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerName)
</th>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerContact)
</th>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerPhone)
</th>
<th>
@Html.DisplayNameFor(model => model.Customer[0].CustomerEmail)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Customer) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CustomerName)
</td>
<td>
@Html.DisplayFor(modelItem => item.CustomerContact)
</td>
<td>
@Html.DisplayFor(modelItem => item.CustomerPhone)
</td>
<td>
@Html.DisplayFor(modelItem => item.CustomerEmail)
</td>
<td>
<a asp-page="./Edit" asp-route-id="@item.CustomerId">Edit</a> |
<!--
<a asp-page="./Details" asp-route-id="@item.CustomerId">Details</a> |
<!--
<a asp-page="./Delete" asp-route-id="@item.CustomerId">Delete</a>
<!--
</td>
</tr>
}
</tbody>
</table>
The Pages/Customers/Index.cshtml page lists existing Customers
and has links to create new ones and to edit, display details and delete Customer
records in the database.
Generated Pages\Customers\Index.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using QuantumWeb.Model;
using QuantumWeb.Models;
namespace QuantumWeb.Pages.Customers
{
public class IndexModel : PageModel
{
private readonly QuantumWeb.Models.QuantumDbContext _context;
public CreateModel(QuantumWeb.Models.QuantumDbContext context)
{
_context = context;
}
public IList<Customer> Customer { get; set; }
public async Task OnGetAsync()
{
Customer = await _context.Customer.ToListAsync();
}
}
}
This is the handler for the Customers/Index page. Notice the lines flagged with the comment, "// Error!!
". This reflects the problems mentioned above following the scaffolding process.
- Notice the line, using
QuantumWeb.Models
. This namespace does not exist. The correct namespace for the model is QuantumWeb.Model
. This line should be deleted from all .cshtml.cs files that have been generated. -
The QuantumDbContext
class is referenced from the QuantumWeb.Models
namespace. The file is located in the QuantumWeb\Data folder. We changed this namespace to QuantumWeb.Data
. The reason is to implement the separation of concerns best practice to separate the data access entities, like QuantumDbContext
, from the object model entities. To enforce this concept, we add a reference to the QuantumWeb.Data
namespace in all .cshtml.cs files that use the QuantumDbContext
. Note the namespaces in the First Update to Data\QuantumDbContext.cs and Initial Data\CustomerConfiguration.cs above.
First Modification to Pages\Customers\Index.cshtml.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using QuantumWeb.Data;
using QuantumWeb.Model;
namespace QuantumWeb.Pages.Customers
{
public class IndexModel : PageModel
{
private readonly QuantumDbContext _context;
public IndexModel(QuantumDbContext context)
{
_context = context;
}
public IList<Customer> Customer { get;set; }
public async Task OnGetAsync()
{
Customer = await _context.Customer.ToListAsync();
}
}
}
We made similar changes to the Pages\Customers\ .cshtml.cs files.
EF Migrations & Database Creation
At this point, we can address EF migrations and create our demonstration database. First, we need to install a NuGet package, Microsoft.EntityFrameworkCore.Tools v.2.1.4
. This package allows us to use certain commands in the Package Manager Console. Normally, the Package Manager Console is displayed in the VS2017 IDE as a tab in the Output Window.
Package Manager Console in VS2017 IDE
If the Package Manager Console is not visible, you can open it using the Tools > NuGet Package Manager > Package Manager Console menu command.
Opening the Package Manager Console in VS2017 IDE
Much of what is done here uses the Package Manager Console. You can explore details about the Package Manager Console here.
There are two ways to install NuGet packages. One is to use a command in the Package Console Manager.
[Caution: In a solution with multiple projects, make sure the correct project is referenced in the Package Manager Console.]
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 2.1.4
The other is done using the Solution Explorer.
Installing NuGet Package, Microsoft.EntityFrameworkCore v.2.1.4 in the VS2017 IDE
Click "Install" to install the package.
Now we can create the initial migration. This will take the code in the QuantumDbContext
and CustomerConfiguration
classes to generate a Migration to handle the transfer of data between the application and the connected database.
Add-Migration Initial-Migration
This command is executed from the Package Manager Console. This will modify the Solution Explorer as shown.
Solution Explorer after Initial-Migration
Generated 20181019171417_Initial-Migration.cs
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace QuantumWeb.Migrations
{
public partial class InitialMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Customers",
columns: table => new
{
CustomerId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn),
CustomerName = table.Column<string>(type: "nvarchar(50)",
maxLength: 50, nullable: false),
CustomerContact = table.Column<string>(type: "nvarchar(50)",
maxLength: 50, nullable: false),
CustomerPhone = table.Column<string>(type: "nvarchar(15)",
maxLength: 15, nullable: false),
CustomerEmail = table.Column<string>(type: "nvarchar(50)",
maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Customers", x => x.CustomerId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Customers");
}
}
}
The Migrations
class defines two methods, Up
, which handles transformations from the application to the database and Down
which handles the reversal of those changes. It is a good idea to examine the generated code for the migration before taking the next step.
Update-Database
Again, the Update-Database
command is executed in the Package Manager Console.
If these commands succeed, changes are propagated to the database. If this is the first migration against the database and the database does not exist, the database is created. This assumes you have permission on the database server to create a database. We can now create a connection to the database in the VS2017 Server Explorer.
Create a Database Connection in the VS2017 Server Explorer
Simply right-click on the "Data Connections" node and select "Add Selection...".
Specify Server & Select Database
Once this is done, you can click the "test" button to confirm that the database exists and that you can connect to it from VS2017.
Test & Create Connection
Click the "OK" buttons to create the connection. Once the connection is there, you can click on its node in the Server Explorer open it. Then, open the "Tables" node and right-click on the Customers table and select "Open Table Definition". You should then see the table definition in the database like below.
Customers Table definition in QuantumDbContext Database
At this point, we can build and execute the application in the debugger.
QuantumWeb Application Home Page: https//localhost: 44306/
Click the "Customers" link to see the Customers
pages.
QuantumWeb Application Customers Page: https//localhost: 44306/Customers
There are no Customer
records, so, our next task is to complete the Customer
UI.
Summary
At this point, we have initially created an ASP.NET Core Razor Page application, QuantumWeb
, which displays an Index page for Customers
. We have completed the initial data access function via the QuantumDbContext
class and created the application database in the localdb
instance.
Points of Interest
In Part II of this article, we will complete the CRUD functions for Customer
s.