There are many environments an ASP.Net Core application can be deployed to; IIS and Azure are two popular options.
In this module we are going to deploy the MVC application to Internet Information Services (IIS). In the next chapter, on Razor Pages, we will look at how to deploy an application to Azure.
Internet Information Services (IIS) is Microsoft’s web server for hosting websites and web applications on the Windows operating system.
Prerequisites
Install SQL Server 2022 Express
Back in module two of this chapter, Create the project: ASP.Net Core MVC, we installed Visual Studio 2022 as a prerequisite. In that process we installed SQL Server Express 2019 LocalDB. LocalDB is the important part here as we have done all of our development against this version of SQL Server. Recall that LocalDB lives on the developer’s machine and requires no administration so this version is optimal for development.
However, for production we will need a full version of SQL Server 2022.
Microsoft offers quite a few versions of SQL Server.
Below is a quick comparison.
| Edition | Free | Production | Feature Set |
|---|
| Express | ✅ | ✅ | Very limited |
| LocalDB | ✅ | ❌ | Dev only |
| Developer | ✅ | ❌ | Full (Enterprise) |
| Standard | ❌ | ✅ | Medium |
| Enterprise | ❌ | ✅ | Full |
| Web | ❌ | ✅ | Hosting only |
As we can see from the version descriptions above, the Express version is both free and production ready albeit limited in features in comparison to the Standard or Enterprise versions. But SQL Server Express does have everything we need in this book and this chapter.
If you have not already installed the full version do that now. You can find instructions for installing SQL Server 2022 Express here.
Install Information Services (IIS)
We installed IIS (Internet Information Services) in Chapter 1, module 10b in the section, “Setting up IIS (Internet Information Services)“.
Follow the steps from that section:
“Setting up IIS (Internet Information Services)“
up and until the section entitled, “Configure the website in IIS”.
The steps from that section on will differ slightly for our MVC app then for the static html app in Chapter 1.
Create the Production Database in SQL Server 2022 Express
In order to create the production database on the production SQL Server, we need to do two things.
- Add the production connection strings for the application database and the identity database in the
appsettings.production.jsonfile. - Run all of the existing migrations against those connection strings so ASP.Net Core will create the database on the production server and apply all existing migrations in the project to the database schema.
Add connection strings to appsettings.production.json
Modify the appsettings.production.json file with the code changes shown below.
FredsCars\appsettings.production.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Serilog": {
"MinimumLevel": {
"Override": {
"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "Error"
}
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"FredsCarsMvcConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FredsCarsMvc;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True",
"FCMvcIdentityConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FCMvcIdentity;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
},
"SmtpSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"UserName": "yourAppName.smtp@gmail.com",
"Password": "yourAppPassword",
"FromAddress": "yourAppName.smtp@gmail.com",
"FromName": "FredsCars Support"
}
}
In the configuration code above, we added a ConnectionStrings section. We have the same two connection string names as in the secrets.json file for development.
- FredsCarsMvcConnection
- FCMvcIdentityConnection
And the connection strings themselves have the same database names as in secrets.json.
- FredsCarsMvc
- FCMvcIdentity
These are the databases that will be created on the production SQL Server when we run the migrations in the next step.
The only thing that has changed in the connection strings is the server. Development points to:(localdb)\MSSQLLocalDB from secrets.json,
and now production points to:
[yourServerName]\SQLEXPRESS2022 from appsettings.production.json
where yourServerName is the name of the server you installed SQL Server Express 2022 on.
NOTE: There is actually one more change in the connection strings.
Make sure you add the following to the end of each connection string:TrustServerCertificate=True
We will talk about why when we run the migrations in the next section.
Notice we also added in the SmtpSettings section into the configuration file. You can cut and paste this from the secrets.json file from when we developed the Reset Password feature in the last module, module 32, Add the Reset Password feature.
Apply the Migrations to create the Production databases
Open Developer PowerShell for VS 2022.

Point the command prompt to the FredsCars project folder.
The one with the FredsCars.csproj file in it.
cd C:\Development\FredsCars\MVC\Module33\FredsCars\FredsCars
You may need to adjust the path to your project folder.
Next, we want to run a sanity check before running the migrations to make sure ASP.Net Core Identity can see both DbContexts.
Run the following command at the command prompt to show a list of the current dbContexts in the project.
dotnet ef dbcontext list
In the results you can see the project does a build and displays both needed contexts for the application and identity databases.

Now, let’s set the environment in our PowerShell session to production.
Run the following command from the command prompt.
$env:ASPNETCORE_ENVIRONMENT="Production"
$env:ASPNETCORE_ENVIRONMENT
The first command actually sets the environment to production. The second command confirms the first command worked and we are now indeed running any following commands in the production environment and against appsettings.production.json rather than appsettings.development.json and secrets.json.

Finally, we can run our migrations. The first command we are going to run looks like this (for the application database):dotnet ef database update -c FredsCarsDbContext
Recall that once we have more than one DbContext in a project we need to use the context switch to specify which DbContext we are targeting for Entity Framework migration commands. Here we are using the shortcut -c switch rather then the full --context switch.
Let’s do that now. Type the following command at the command prompt.
dotnet ef database update -c FredsCarsDbContext
After running the command you will see in the results of the PowerShell window that the project migrations have run the SQL commands against the SQL Server to create the FredsCarsMvc database and the tables; Vehicle and VehicleType.

You can now also view the tables from with in SSMS (SQL Server Management Studio).

Now let’s run the migrations for the ASP.Net Core Identity database. Run the following command at the prompt in the Visual Studio Developer PowerShell window.
dotnet ef database update -c FCMvcIdentityCoreDbContext
Once all the migrations have run in the PowerShell window you will then see the Identity database in SSMS.

Dealing with SQL Server TLS/SSL encryption
In the, Add connection strings to appsettings.production.json, section of this module, I noted that we must add theTrustServerCertificate=True
attibute/value pair to the end of each connection string and promised to explain why.
Without this attribute set to true in the connection strings you may encounter the following error when issuing the dotnet ef database update -c [DbContextName]
command.
A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 – The certificate chain was issued by an authority that is not trusted.)

This error may appear from SQL Server TLS/SSL encryption when we run the dotnet ef database update
command.
What happens here is that the client connects to SQL Server, but during login it tries to negotiate TLS and doesn’t trust the server certificate (common with local SQLEXPRESS using a self-signed cert, or when encryption is forced).
This is very common lately because the newer Microsoft.Data.SqlClient data provider defaults to Encrypt=True in more scenarios than it used to.
What is TLS/SSL encryption
SQL Server TLS/SSL encryption is about protecting data in transit—that is, data traveling between your application and SQL Server—so it can’t be read or altered by anyone intercepting the connection.
What this means is that when TLS/SSL is enabled, SQL Server encrypts login credentials, queries, result sets, and connection metadata.
Everything sent over the wire is protected from packet sniffing, man-in-the-middle attacks, and credential harvesting.
What is a Man-in-the-middle (MITM) attack
A man-in-the-middle (MITM) attack happens when an attacker secretly intercepts and possibly alters communication between two parties who believe they are talking directly to each other.
Think of it as someone silently standing between you and the server, reading (and sometimes changing) everything that passes through.
Publish the Web Application
Now that we have the databases in place, let’s begin to actually publish the application to IIS.
In Windows Explorer, create a new folder on the root of the C drive named Production and within it create a subfolder named FredsCarsMvc. The new folder structure should look like this:C:\Production\FredsCarsMvc
From within Visual Studio set the configuration to Release mode by clicking the Configuration Manager dropdown and selecting Release.

We need to make this change in order to optimize performance. During development, we have the configuration set to debug. This disables optimization but includes debug symbols so we can use the debugger if necessary from within the VS IDE. Also exception messages are very detailed in debug mode.
Once we switch to Release mode, optimization gets enabled, debug symbols are no longer loaded, and default exception details become limited. (That is where our logging comes in so we can custom capture the details we think we’ll need.)
Next, right click on the FredsCars project folder and select Publish.

In the Publish dialogue, select “Folder – Publish your application to a local folder or file share” and click the Next button.

Next, the Publish dialogue shows it will publish to a folder path that looks like this:bin\Release\net9.0\publish
on the root of the FredsCars project folder.
Click the Finish button.

At this point a new tab opens up in the VS IDE where the tab name reads, “ProjectName: Publish“, for our app specifically, “FredsCars: Publish“, with a message that reads, “Ready to publish”.
Click the Publish button.

The Output window will open in VS and show the steps and results as it builds the project and publishes all of the files we will need to run the application in IIS. You should see a message that says, Publish: 1 succeeded, near the end of the results.

The bin folder is a hidden folder in Visual Studio. So, in order to see the new publish folder under the hidden bin folder, click the “Show All Files” button at the top of Solution Explorer. Now all hidden folders and files show in Solution Explorer with folder icons in a grayed out color rather then unhidden folders and files in yellow.

If you study the contents of the publish folder, you can see it includes several runtimes that can be used to run the application in any webserver environment such as IIS or Azure including Unix and several versions of Windows. It also includes the FredsCars executable, FredsCars.exe, and a FredsCars dll (Dynamic Link Library) file, FredsCars.dll, as well as other files we created all through this chapter such as libman.json, appsettings files, etc.

It even publishes out our wwwroot folder containing our site.css file containing all of our custom styles for the application, the images folder, and the lib folder where we imported client libraries such as Bootstrap, jQuery, and jQuery Validation.

Finally, we can copy the publish folder’s contents to the C:\Production\FredsCarsMvc folder we created earlier.
Right click the publish folder and select, “Open Folder in File Explorer”.

A new File Explorer window pops up set to the path:
C:\Development\FredsCars\MVC\Module33\FredsCars\FredsCars\bin\Release\net9.0\publish
(Your path may differ depending on how you have been organizing your project throughout this chapter.)
Copy all of the contents, all folders and files, from:
C:\Development\FredsCars\MVC\Module33\FredsCars\FredsCars\bin\Release\net9.0\publish
to:
C:\Production\FredsCarsMvc

Create a local certificate to include custom hostname
In the next section we are going to create a web application in IIS which will point to and use the files we just published. But, before we create the IIS website, we need to stop and create a local HTTPS certificate that matches the custom hostname we plan to use:
www.fredscarsmvc.com
This step is required before creating the IIS application so that we can select a legit certificate that contains the hostname we plan to use when configuring the HTTPS binding.
Create a SAN certificate
We can accomplish this by creating a SAN certificate using PowerShell.
A SAN (Subject Alternative Name) certificate is a digital certificate that can secure multiple domain names, subdomains, or IP addresses using a single certificate.
Let’s create one now. Open VS Developer PowerShell now in Administrator mode.

Run the following command to create the SAN certificate.
$cert = New-SelfSignedCertificate `
-DnsName "www.fredscarsmvc.com","fredscarsmvc.com" `
-CertStoreLocation "cert:\LocalMachine\My"
Then run the following commands to trust the new certificate.
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root","LocalMachine")
$store.Open("ReadWrite")
$store.Add($cert)
$store.Close()
Create the IIS application
In the beginning of this module, under the “Prerequisites -> Install Information Services (IIS) section”, I directed you if you had not already done so in Chapter 1, to go ahead and install Information Services (IIS) by following the directions at, Setting up IIS (Internet Information Services, up and until the section entitled, “Configure the website in IIS”. Let’s pick up from there where we left off and configure the web application. This may seem a little repetitive from Chapter 1 but there are some slight differences along the way.
Configure the website in IIS
Open up IIS. You can search for IIS in the start menu and select the result icon.

IIS Manager pops up as shown in the following screenshot. Expand the top node in the left pane (YourServerName\YourUserName), and from there expand the Sites node. If you worked through chapter 1 of this book, Static HTML – Designing the landing page, you will see the FredsCars website. If so, click it now and then click, Content View, on the bottom of the middle pane. You will see all the files we used in Chapter 1 to create the static html version of FredsCars in the upper middle pane. If not, no worries. You don’t need it for this chapter.

Create the FredsCarsMVC Website
In IIS Manager right click on Sites and select Add Website.

The Add Website dialogue will pop up. Enter the information into the dialogue the same as you see it in the screen shot below.

Make sure the values match for each setting in the Add Website dialogue.
Site name: FredsCarsMvc
Application pool: FredsCarsMvc
Physical path: C:\Production\FredsCarsMvc.
Binding Type: https
Binding Address: 127.0.0.1 (This is called the loopback address. Every computer has an IP Address such as 192.168.54.16 and the loopback address of 127.0.0.1 points back to your own computer.)
Port: 443 (The default port for web applications with an https binding)
Host name: www.fredscarsmvc.com (This is the URL you are specifying should be typed in the browser to find the web application in IIS.)
Require Server Name Indication: checked
SSL certificate: www.fredscarsmvc.com
The last option, SSL certificate, deserves a little explanation.
A SSL certificate is required whenever a website uses HTTPS. HTTPS is simply HTTP traffic that is encrypted using TLS (Transport Layer Security).
The certificate serves two critical purposes:
- Encryption – It allows IIS and the browser to establish an encrypted connection so data such as login credentials, cookies, and form posts cannot be intercepted or read in transit.
- Trust – It allows the browser to verify that it is really communicating with the intended server and not an imposter (a man-in-the-middle attack).
When a browser connects to an HTTPS site, IIS presents the SSL certificate. The browser uses the certificate to negotiate encryption keys and validate the identity of the site.
Click OK and you will see the FredsCarsMvc website appear under the Sites node along with the published files in the Content view.

Configure the Host file
Right now if you type the URL https://www.fredscarsmvc.com into your browser it will not be able to find the website and you will get a “404 Page Not Found” or “site can’t be reached” error. We have to tell your local development computer that the site at https://www.fredscars.com can be found on the same local computer at the loopback address of 127.0.0.1
Note: [Repeated from Chapter 1] Every computer and every web server has an IP address. When you type in www.amazon.com or www.microsoft.com, the request is sent out to the internet and routed to a DNS server that can look up the address you typed in the browser and translate it to an IP Address. For example www.amazon.com might become 108.174.10.10. So www.amazon.com/orders is the same as 108.174.10.10/orders.
In order to fix the error we need to add an entry to the local computer’s host file. (In a real life scenario your company or organization would need to register this entry with a DNS server.)
Open up TextPad, NotePad, or your favorite text editor in Administrator mode.
In the screenshot below, I search for NotePad in the Start Menu, right click on the resulting NotePad app, and select Run as administrator. (The host file will not let you edit its contents unless you open your editor in admin mode.)

If the User Account Control panel pops up and asks, “Do you want this app to make changes to your device?”, click yes.
When notepad (or your editor of choice) pops up, open the host file by selecting File -> Open or typing Ctrl-O.

In the open dialogue, type the following path in the address bar:
C:\Windows\System32\drivers\etc,
select “All files (*.*)” for the file type,
select the hosts file in the navigation pane,
and click the Open button.

Add the following entry at the end of the hosts file and save it.
127.0.0.1 www.fredscarsmvc.com

Install the .Net Hosting Bundle
When we created the web application in IIS, a web.config file was created in the directory of the published files at C:\Production\FredsCarsMvc. The contents of the file look like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\FredsCars.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
<!--ProjectGuid: 5D24B2CE-7E07-4CD7-A719-38E72A400470-->
This is one of the last refuges of the notorious web.config left over from the .Net Full Framework (original .Net, pre-.Net Core).
The important setting in this config file is that the AspNetCoreModuleV2 module is being added to the IIS application.
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
AspNetCoreModuleV2 is the module that enables IIS to run an ASP.Net Core application.
We can install this module using the .NET Hosting bundle which is an installer for the .NET Runtime and the ASP.NET Core Module. The bundle allows ASP.NET Core apps to run with IIS.
Download the Installer
Open up a browser and navigate to:
https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-aspnetcore-9.0.11-windows-hosting-bundle-installer in order to download the Host Bundle installer for .Net 9.x.
If you need another version you can find other versions at:
https://dotnet.microsoft.com/en-us/download/dotnet.
A file named dotnet-hosting-9.0.11-win.exe is downloaded to your Downloads folder.

Run the installer in admin mode. Make sure to run it in admin mode because ASP.NET Core Hosting Bundle needs elevated rights or it may only partially succeed. So right click the installer and select Run as administrator.

The installer will then pop up in a dialogue entitled:
“Microsoft .Net 9.0.11 – Windows Server Hosting Setup“.
Check the checkbox to agree to the terms and click the Install button.

Once the install is complete the dialogue will say,
“Installation Successfully Completed”.
Click the Close button.

View the AspNetCoreModuleV2 module
Once the Host Bundle is installed we can view the new AspNetCoreModuleV2 module in IIS.
In IIS Manager, click the FredsCarsMvc web application node in the left pane. Then in the Features View, double click Modules.

Here we can see that the AspNetCoreModuleV2 module has indeed been installed for the FredsCarsMvc web application in IIS. (This happens because both the web.config adds and points to the module and we just installed the module in the hosting bundle.)

Restart IIS and clean browser/DNS state
At this point, all web-hosting infrastructure is in place:
- the ASP.NET Core Hosting Bundle is installed
- the IIS site and HTTPS bindings are configured
- the SAN certificate has been applied
- the hosts file has been updated
Before continuing, we need to restart IIS and clear the local DNS cache to ensure:
- IIS loads the newly installed ASP.NET Core runtime
- HTTPS bindings and certificates are fully applied
- Windows re-reads the hosts file for name resolution
- stale DNS or browser-cached resolution is cleared
Run the following commands in your open Powershell window (in admin mode).
iisreset
ipconfig /flushdns
Create an SQL Server User Login
Earlier when we created the production databases, we used trusted connections.
FredsCars\appsettings.production.json
... existing code ...
"ConnectionStrings": {
"FredsCarsMvcConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FredsCarsMvc;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True",
"FCMvcIdentityConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FCMvcIdentity;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}
... existing code ...
A trusted connection (in the context of SQL Server) means that SQL Server trusts Windows to authenticate you, instead of you providing a SQL username and password.
Now that the production database is created and established we can create an SQL Server User Login to tighten up security for our application.
In order to create a User Login in SQL Server for our application we will do two things within SSMS or SSOX.
- Create a SQL Server-Level login
- Create two SQL Server Users mapped to that login; one for each db.
Create the server-level login
Run the the following SQL commands in SSMS or SSOX.
-- Create Server-level login
USE [master];
GO
CREATE LOGIN [FredsCarsMvcAppLogin]
WITH PASSWORD = 'Fr#dsC@rsMvcAppLog!n547',
CHECK_POLICY = OFF,
CHECK_EXPIRATION = OFF;
GO
I prefer to use SSMS (SQL Server Manager Studio). In SSMS click the New Query button to open up a query window.

A new tab will pop up as the query window entitled something like SQLQuery1.sql. Run the SQL commands in that window by clicking the Execute button with the Green right arrow button in the top tool bar or hitting the F5 key.

At this point the Server Level login, FredsCarsMvcAppLogin, will exist. To see this in SSMS right click the Logins node and select Refresh.

And now you will see the new login.

Create the SQL Users
Next we are going to create two SQL Database users mapped to the login we just created.
Run the following SQL Commands.
-- App DB User
USE [FredsCarsMvc];
GO
CREATE USER [FredsCarsMvcAppUser]
FOR LOGIN [FredsCarsMvcAppLogin];
GO
ALTER ROLE db_owner ADD MEMBER [FredsCarsMvcAppUser];
GO
-- Identity DB User
USE [FCMvcIdentity];
GO
CREATE USER [FcMvcIdentityUser]
FOR LOGIN [FredsCarsMvcAppLogin];
GO
ALTER ROLE db_owner ADD MEMBER [FcMvcIdentityUser];
GO
Now the two new Users mapped to the login will show up in SSMS Object Explorer.

Update the connection strings in appsettings.production.json for the application code
Make the following changes to the appsettings.production.json file.
FredsCars\appsettings.production.json
"ConnectionStrings": {
"FredsCarsMvcConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FredsCarsMvc;User ID=FredsCarsMvcAppLogin;Password=Fr#dsC@rsMvcAppLog!n547;Encrypt=True;MultipleActiveResultSets=true;TrustServerCertificate=True",
"FCMvcIdentityConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FCMvcIdentity;User ID=FredsCarsMvcAppLogin;Password=Fr#dsC@rsMvcAppLog!n547;Encrypt=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
},
Update the connection strings in appsettings.production.json in the published site folder
NOTE: In a real world scenario we would have CI (Continuous Integration) setup and simply make the change in our app code and republish the web app files. But, to keep the focus on learning ASP.Net Core MVC we will forgo CI and manually make the change to the production file.
Make the same changes we just made above to the appsettings.production.json file in the production folder.
C:\Production\FredsCarsMvc\appsettings.production.json
"ConnectionStrings": {
"FredsCarsMvcConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FredsCarsMvc;User ID=FredsCarsMvcAppLogin;Password=Fr#dsC@rsMvcAppLog!n547;Encrypt=True;MultipleActiveResultSets=true;TrustServerCertificate=True",
"FCMvcIdentityConnection": "Server=iImagineWS_DELL\\SQLEXPRESS2022;Database=FCMvcIdentity;User ID=FredsCarsMvcAppLogin;Password=Fr#dsC@rsMvcAppLog!n547;Encrypt=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
},
Restart IIS to reload updated configuration
Because we updated the production connection strings after IIS was restarted earlier, we need to restart the application again so the changes are loaded. Open a command prompt in Administration mode.

Type the following command.
iisreset
In production environments, recycling the application pool (the app pool the web app is set to use in IIS) is usually preferred, but iisreset is sufficient for a local deployment.
At this point if you navigate to https://www.fredscarsmvc.com, the application should start up and display in the browser (albeit with no data).

In the next section we’ll work on getting some data into the production environment.
Populate the Data
Now that the production database has been created and our production application can connect to it, the next step is to populate it with data.
There are several common approaches In real-world scenarios to populating data into the production database.
Common Options to Populate a Production Database
Application-Driven Seeding (EF Core)
In many applications data is populated using Entity Framework Core seeding.
This approach involves:
- Defining seed data in code (Program.cs)
- Applying it automatically when migrations are run or on first application startup.
- Ensuring repeatable, predictable datasets across environments
This is commonly used for:
- Lookup tables (categories, statuses, roles)
- Initial admin users
In fact, we did use this approach to seed the AspNetUsers table in the FCMvcIdentity production db with our back door admin user, the AspNetRoles table with Administrator and Staff roles in the AspNetRoles table, and associate the admin user with the Administrator role. This is the Identity data we seeded earlier in the Chapter in module 30, Security & Administration.
You can verify this data already exists in your production database with a couple of queries.

We also seeded application data while in development but we considered the seed data to be test data so in Program.cs we only seeded the app data if the environment was indeed Development; not test or production.
/*** Seed the business database ***/
if (builder.Environment.IsDevelopment())
{
// Alternative C# 8 using declaration.
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider
.GetRequiredService<FredsCarsDbContext>();
SeedData.Initialize(context);
}
SQL Scripts
Another common approach is populating data using raw SQL scripts.
This may include:
INSERTstatements- Bulk inserts
- Stored procedures
This method is often used when:
- Data comes from an external system
- DBAs control production deployments
- Precise control over the data is required
Data Migration & ETL Pipelines
In enterprise environments, data is often populated using ETL (Extract, Transform, Load) tools.
Common examples include:
- SQL Server Integration Services (SSIS)
- Azure Data Factory
- Custom migration services
These are typically used when:
- Migrating large datasets
- Transforming data between schemas
- Integrating multiple data sources
Export / Import Between Databases
A very practical and widely used approach—especially for smaller projects and early production setups—is exporting data from one database and importing it into another. And this is the approach we will be using.
This approach is commonly used when:
- Moving from LocalDB to SQL Server Express or Standard
- Promoting data from development to production
- Performing one-time or infrequent data transfers
Tools such as SQL Server Management Studio (SSMS) provide built-in Export and Import wizards that make this process straightforward and reliable.
So, let’s get started.
Using the SSMS “Export Data” / “Import Data” Wizard
Connect to the Databases
In SSMS, connect to (localdb)\MSSQLLocalDB (development) and to .\SQLEXPRESS2022 (production) or whatever your Express instance is.
If you haven’t already connected to your databases in SSMS, then in SSMS Object Explorer click the Connect button and select Database Engine.

The Connect to Server dialogue pops up. Select “(localdb)\MSSQLLocalDB” for Server name and “Windows Authentication” for Authentication from the drop downs and click the Connect button.

You will then see all of your databases in localDB under the Databases node in Object Explorer.

Follow the same procedure to connect to the production db, .\SQLEXPRESS2022, but this time in the Connect to Server dialogue, select
[YourServerName]\SQLEXPRESS
for Server Name (replace YourServerName with your instance name. Mine is SQLEXPRESS2022)
and leave Authentication to Windows Authentication.
Click the Connect button.

You should now be able to see all of your databases in SQLExpress (production) as well as localDB (development) in Object Explorer).

Export the data
Right click the FredsCarsMvc database under localdb and Select -> Tasks -> Export Data.

The Welcome Screen to the SQL Server Import and Export Wizard opens. Check, “Do not show this starting page again”, and then click the Next button.

From the “Choose a Data Source” screen, select “Microsoft OLE DB Driver for SqlServer” from the Data source drop down, and click the Properties button.

In the Data Link Properties dialogue, type in
(localdb)\MSSQLLocalDB
for: 1. Select or enter a server name.
Select Windows Authentication for:
2. Enter information to log on to the server.
Then click the Test Connection Button.

Here you should get a “Test connection succeeded” popup. Click the OK button to close the popup window.

Now back in the, “Data Link Properties” dialogue select the FredsCarsMvc database from the drop down next to label number 3. “Select the database”.

Click the OK button to close the “Data Link Properties” dialogue.
And click the Next button back in the “Choose a Data Source dialogue.
In the Choose a Destination dialogue, choose:Flat File Destination
for destination.
Type: C:\Data\FredsCarsMvc\data-FredsCarsMvc.csv
for File name.
Leave local at:English (United States)
Make sure Unicode is checked.
Set Format to Delimited.
Leave Text qualifier at the default <none>.
Make sure “Column names in the first data row” is checked.
Click the Next button.

In the “Specify Table Copy or Query” dialogue choose the option:
Copy data from one or more tables or views.
Click the Next button.

On the “Configure Flat File Destination” dialogue choose:[dbo].[Vehicle]
for Source table or view.
Leave Row delimiter and Column delimiter at their defaults:
Row delimiter: {CR}{LF}
Column delimiter: Comma {,}.
Click the Next button.

On the “Save and Run Package” dialogue select the:
Run immediately option, unselect the:
Save SSIS option,
and click the Finish button.

On the “Complete the Wizard” dialogue, you can review all of the settings we have just entered for the export and click the Finish button.

Now the csv file named, data-FredsCarsMvc.csv, should be in your C:\Data\FredsCarsMvc folder. And this is what the contents should look like; a comma delimited list.
Id,Status,Year,Make,Model,Color,Price,VIN,VehicleTypeId,ImagePath
1,0,2021,Dodge,Challenger,Frostbite,64164,2C3CDZFJ8MH631199,1,/images/cars/car1.webp
2,1,2020,Ford,Escape,Oxford White,22999,1FMCU0F63LUC25826,1,/images/cars/car2.webp
3,0,2021,Dodge,Durange,Black,50557,1C4RDJDG5MC837730,1,/images/cars/car3.webp
4,0,2021,Nissan,Niro,Blue,24960,2XYZT67JTF24AZG856,1,/images/cars/Car4.png
5,0,2021,Kia,Stinger,Gray,36090,6FG146B89624AZ7952,1,/images/cars/Car5.png
6,0,2021,Kia,Stinger,Gray,36090,6FG146B89624AZ7953,1,/images/cars/Car6.png
7,0,2022,Ram,Crew Cab,Black,68400,3C6UR5DL8NG157035,2,/images/trucks/truck1.webp
8,1,2017,Ram,Crew Cab,Red,33000,1C6RR7PT0HS814596,2,/images/trucks/truck2.webp
9,0,2022,Jeep,Compass,White,34980,3C4NJDFB5NT114024,3,/images/jeeps/jeep1.webp
10,0,2022,Jeep,Compass,Red,39275,3C4NJDCB1NT118172,3,/images/jeeps/jeep2.webp
11,0,2022,Jeep,Grand Cherokee,Pearlcoat,53575,1C4RJKBG5M8201121,3,/images/jeeps/jeep3.webp
12,0,2021,Jeep,Wrangler Sport S,Green,40940,1C4GJXAN0MW856433,3,/images/jeeps/jeep4.webp
13,0,2025,Jeep,Gladiator Rubicon 4 X 4,Joose,64125,1C6RJTBG0SL532163,3,/images/jeeps/jeep5.jpg
We now have our Vehicle data ready to import into production. But we are also going to need to move over the VehicleType data.
Follow the same steps we just went through to export Vehicle data but, for file name on the “Choose a Destination” screen, type in
C:\Data\FredsCarsMvc\FredsCarsMvc-VehicleType.csv.

And for the “Configure Flat File Destination” screen, choose VehicleType for, “Source table or view”.

You should end up with a second file named, FredsCarsMvc-VehicleType.csv, in the C:\Data\FredsCarsMvc folder with contents that looks like the following.
Id,Name
1,Cars
2,Trucks
3,Jeeps
Import the data
Now that we have our two csv files, one for application data, and one for VehicleType lookup data, we can import them both into our prod database.
We need to import the VehicleType data first because the Vehicle data has foreign keys which point to the VehicleType table.
Right click the FredsCarsMvc database under your production server and choose
Tasks -> Import Data.

On the Choose a Data Source screen, select “Flat File Source” for Data Source and set the File name to:
C:\Data\FredsCarsMvc\FredsCarsMvc-VehicleType.csv.
Leave the rest of the settings at their default.
Although double check that Unicode is checked.
Here we are defining the settings for the header row.

Click Columns in the left side pane to preview the data. Here we are defining the settings for the data rows. Leave the defaults and click Next.

In the, “Choose a Destination” dialogue, pick “Microsoft OLE DB Driver for SQL Server” and click the Properties button.

In the “Data Link Properties”, set the Server Name to whatever your SQL Server name is followed by a backslash followed by your SQL Server instance name. Yours will probably be SQLEXPRESS. Mine is SQLEXPRESS2022.YourServerName\SQLEXPRESS.
(You can copy your server information from your appsettings.production.js file.)
Choose “Windows Authentication” from the second dropdown as the authentication method.
Select the FredCarsMvc Database from the dropdown next to the label numbered 3, “Select the database”.
You can test the connection again by clicking the “Test Connection” button. And then click the OK button to close the “Test Connection Succeeded” popup window. Then click the OK button on the “Data Link Properties” dialogue to close it.

Back in the “Choose a Destination” dialogue, click the Next button.

The “Select Source Tables and Views” dialogue will have two columns. The first column shows we have selected the FredsCarsMvc-VehicleType.csv flat file we created in the last section in the C:\Data\FredsCarsMvc folder as the source containing the data to import.
In the second column, the wizard has tried to guess the table name to import into based on the flat file name, FredsCarsMvc-VehicleType.

However, the actual table name we want to import the VehicleType lookup data into is VehicleType.
Click into the Destination column value and from the dropdown select the VehicleType option. Then click the Edit Mappings button.

In the Column Mappings popup dialogue, check Enable identity insert and click the OK button to close the popup window.

Now back in the Select Source Table and Views, click the Next button to continue.

On the “Review Data Type Mapping” dialogue, click the Next button.

On the “Save and Run Package” screen, check the Run immediately box and uncheck the Save SSIS Package box. If you want to verify the choices you made click Next to go to the Complete the Wizard dialogue and click the Finish button. Otherwise just click the Finish button here to complete the import process.

Finally, and hopefully, click the Close button on the “The execution was successful” screen.

Open a browser and navigate to:
https://www.fredscarsmvc.com.
At this point there is still no Vehicle data because we have not yet imported the application data. But because we have now imported the VehicleType lookup data, the custom Categories component we built back in module 21 can now dynamically lay out the Category buttons.

Now go ahead and follow the same import procedure we just did for the VehicleType table (lookup data) and apply it to the Vehicle table (application data) with a few differences noted below.
On the first “Choose a Data Source” screen, type in
C:\Data\FredsCarsMvc\data-FredsCarsMvc.csv
for file name rather thenC:\Data\FredsCarsMvc\FredsCarsMvc-VehicleType.csv.

On the “Select Source Tables and Views” screen, select Vehicle from the Destination dropdown this time rather than Vehicle.

Now, once again, open a browser and navigate back to our landing page:
https://www.fredscarsmvc.com.
And now the application shows the Vehicle data in addition to the Category buttons.

Wrapping Up
We’re almost there now. We just need to test out the application’s functionality so we are sure there are no deployment surprises or gotchas.
Spend some time testing the category buttons, sorting, and paging.
In the screenshot below, I’ve clicked the CARS category button, and the “Page 2” button.

Now, Click the “Sign In” button in the upper right hand corner and log in as:
UserName: admin,
Password: Adm!n863.

The browser is redirected back to the home page and now the edit and delete icons show up next to the vehicle data rows and the Admin button shows up in the upper right hand corner along with the user profile icon which we can click to see our user name and roles.

Click the Admin button in the upper right hand corner to be taken to the admin tools page.

Click the Users button to be taken to the Users admin page and click the Create New link to create a new admin user.

On the Create User page, create a new user with the Administrator role.


A few more features you can test out on your own are:
- View Admin Roles page. Create and Delete a role.
- View Admin Categories page. Create and Delete a category.
- On the Login page, test out the “Forgot password” feature.
Conclusion
In this module we deployed our ASP.Net Core MVC web application to IIS (Internet Information Services).
First we created the production database using the existing migrations we created during development. Then we published the files to a folder named C:\Production\FredsCarsMvc.
Next we learned how to create a local HTTPS certificate by creating a SAN (Subject Alternative Name) in PowerShell which we needed in the next step where we created the IIS application. We needed this certificate to bind to the HTTPS protocol in the IIS app configuration. And finally created a Website in IIS to host the ASP.Net Core MVC web application.
We also configured the Host file to add the domain name we want to use in the website’s URL, installed the .Net Hosting Bundle, and created a SQL Server user in the production database to use in the prod connection strings in appsettings.production.json.
Finally we populated the production db by using SSMS’s (SQL Server Management Studio) export and import tools and tested out all of the application features.
In this chapter we began our journey of learning ASP.Net Core. We built a web application using MVC Core, the first of the three ASP.Net frameworks we will learn in this book. Along the way we learned a lot about C#, Microsoft’s programming language invented specifically for the .Net Framework and Entity Framework Core, Microsoft’s ORM (Object Relational Mapping) tool.
We also learned more about Bootstrap and jQuery which we first looked at in Chapter 1.
We learned some software development patterns; DI (Dependency Injection) and the Repository pattern and all about unit testing with xUnit.
This chapter also introduced core application concerns such as logging, user secrets, and error handling.
Finally, we covered Security & Administration using ASP.Net Core Identity and deployment to IIS.
We covered a lot of ground in this chapter to say the least and your learning curve should be going up quite a bit by now. In the next chapter we are going to rebuild the Fred’s Cars application in an alternative framework provided by ASP.Net Core called Razor Pages.
