Learning Objectives
In this tutorial we’ll create an ASP .NET Core 3.0 web application using MVC, Entity Framework, and a restful Web API.
By the end of the article, we’ll have an operational web application that lets us create and modify test suites and add manage test cases in each test suite.
We’ll focus on getting up and running quickly and will largely go with the default look and feel for new web applications.
Along the way we’ll discuss:
- ASP .NET Core 3.0
- Model View Controller (MVC)
- Entity Framework
- Web API
Understanding ASP .NET Core 3.0 MVC Web Apps
ASP .NET Core 3.0 is a web server running on .NET Core 3.0. This is an enterprise-ready web server capable of serving up static or dynamic web pages as well as API endpoints.
ASP .NET offers a variety of web page technologies but in this article we’ll focus on their Model View Controller (MVC) offering with its Razor syntax.
This is not an article about understanding the details of MVC or Razor, but we will be looking at some code related to these things.
Model View Controller consists of the following components:
- The Model, or class, containing the data
- The View, or web page, presenting the data
- The Controller, a class that handles requests from the view, modifies the Model, and directs to an updated View.
Razor Views use a templating engine built on top of HTML that allows for various directives. In this article we’ll be focusing exclusively on HTML helpers and letting the scaffolding features of Visual Studio generate the rest of the view for us.
The other technology we’ll be working with is Entity Framework, which is a .NET wrapper around databases that can handle dynamic querying and database migrations.
Let’s get started.
Creating the Project
In Visual Studio 2019, create a new project and select an ASP.NET Core web application project.
There are a lot of different flavors of ASP .NET, but for this tutorial we’ll be going with an ASP .NET Core 3.0 Web Application (Model-View-Controller) application.
Before creating the application, click Change
under Authentication and select Individual User Accounts and then Store user accounts in-app:
While we won’t be doing much with authentication or identity, this will set us up well to talk about Entity Framework in this article.
ASP .NET Core helpfully defaults to a local database on your development machine when you’re just getting set up. This lets you get started on code quickly and work in a portable environment until you’re ready to switch to a database server else.
Go ahead and create the project.
Launching the Application
Hit Control + F5
or open the Debug
menu and select Start without Debugging
. The application will begin compiling and launch shortly.
You may see a dialog similar to the following:
I recommend clicking Yes and then trusting the certificate presented to you. This will allow you to work with https locally.
Once the app compiles, your browser should open and you should see something similar to the following:
Congratulations, you have a working ASP .NET Core web application.
Next, let’s click login to create / authenticate your user. You should see a database error page informing you that migrations are needed.
In order to resolve the error we’ll need to go to the Package Manager Console and type Update-Database
and hit enter. This will cause the local database to be updated with the tables specified by the identity provider
Once that completes, you’ll be able to successfully create and authenticate your user and login.
Adding your First Entity
First, let’s create a class in the Models
folder. This can be anything you’d like, but the example here we’ll call it a TestSuite
. The TestSuite
is a container that will hold TestCase
entities later on.
This is what my TestCase
class looks like:
Notice the heavy use of attributes. Let’s go over them:
-
Key
tells Entity Framework that this is the primary key / identity column in the database table. -
Required
indicates that the field must have a value. This is interpreted at both client-side as well as in Entity Framework’s generated column definitions. -
HiddenInput
is read by the MVC templating engine to not render inputs for the given fields. We’ll see what this means in the next step. -
Display
orDisplayName
can be used to customize the way the field is presented to the user in the template.
Next we’ll add a MVC Controller to handle our entity entity and present the user with a view.
We’ll do this by right clicking on the solution explorer and selecting Add > Controller…
This brings up the Add New Scaffolded Item dialog pictured below:
The process of scaffolding has Visual Studio generate code based on your options and referenced code. In our example, it will generate fields based on our model specified and choices in the Add MVC Controller dialog.
We’ll specify our TestSuite
entity in the Model class field. For Data Context, you can specify a new one if you’d like, but there’s not a huge reason not to use the existing ApplicationDbContext
class. The remainder of the checkboxes are good to keep checked.
Once you click Add, it will generate everything for you. This could take some time.
A Peek into Entity Framework Migrations
Once the scaffolding operation is complete, take a look at ApplicationDbContext
. The scaffolding operation added a line to the context for us:
public DbSet<TestSuite> TestSuite {get; set;}
Because we now have a new entity defined in an EF context, we will need to add an Entity Framework migration for the entity defined.
Do this by running Add-Migration AddedTestCases
in package manager console. Note that the name of the migration can be whatever you want, as long as it’s easy for you to understand what operation it represents.
Once this completes, take a look at the class generated by the Add-Migration
operation. It should look something like this:
This helps demystify the database migration process. Database migrations are just code that gets executed when the change is applied or reverted.
Once you are ready to apply this database migration, go to Package Manager Console and run Update-Database
Exposing the New View
At around line 25 of _layout.cshtml
, add the following code:
Note that asp-controller
here refers to the name of the controller, which was named during scaffolding earlier in this tutorial.
This link will allow us to navigate to the new series of views. You should be able to create, read, update, and delete TestSuite
entities.
While the generated code largely “just works” I did need to make some adjustments. Specifically, on the create page, I removed the created and modified fields.
Additionally, in the create and edit post methods on the TestSuitesController
I also set the initial created time and updated the modified time on edit.
Ideally the edit post handler should not take in all properties of the object, but instead only copy over the values exposed in the user interface. This will prevent certain types of attacks and also work better for concurrency if the entities have been changed since the page loaded.
Adding a Second Entity
Now that we have one entity working well, let’s add a TestCase
entity and TestCases
collection to each TestSuite
. This represents a specific test that can be performed.
First, let’s create a simple enum to represent the status of the test case:
Here I’m using the Display
attribute to specify how the NotRun
value should display if it is ever rendered in a user interface.
Next, let’s define the TestCase
:
Here the fields are largely the same as before, but note TestSuite
and TestSuiteId
. The TestSuiteId
is a foreign key relationship field used to store the key of the the TestSuite
entity referenced by the TestSuite
property.
We’ll also add a collection to the TestSuite
class so we can get to the TestCase
entities associated with it:
From here, we can go ahead and scaffold the TestCase
entity in the same way we did earlier for TestSuite
.
Once the scaffolding operation is complete, we can create the database migration by using Add-Migration AddedTestCases
in Package Manager Console.
Take a look at the generated migration and how it handles foreign keys.
Automatically Migrating Databases
Instead of running Update-Database
this time, however, let’s look at a way you can have your application automatically migrate on startup.
Go to Startup.cs
and paste in the following method:
Next, at the bottom of the Configure
method, add the following statement:
ApplyDatabaseMigrations(app);
When the server starts up, it will now look at the database and perform any migrations that have not yet been run.
Note that it’s recommended to add a lot more logging and error handling if you’re going to try this in a production application.
View Work for Test Cases
Like before, add a link to the TestCasesController
from Layout.cshtml
. This will let us look at the default view for Test Cases.
The first thing that stood out to me was that the test case status drop down lists aren’t working. For whatever reason, the default scaffolding doesn’t seem to work well with enums. Thankfully, there’s an easy way around this.
Using the select
element, we can customize its asp-items
property. This lets us select the source of the items for the drop down. The built-in GetEnumSelectList
HTML helper gives us exactly what we want:
This will also respect our display name attribute and display “Not Run” instead of “NotRun”.
Next, let’s link to the Test Suite from the Test Case list by replacing the @Html.DisplayFor
in the grid with an ActionFor
like so:
This tells ASP .NET to generate a link to the Details action on the TestSuites controller and pass in the ID of the suite to view.
Finally, let’s render the list of test cases from a test suite’s details view by adding the following code:
That’s pretty simple code, however it has a problem – it gets a null reference exception when the page is loaded.
What’s going on?
Entity Framework by default will not lazily load collections unless we tell it to. Because of this, Model.TestCases
is null and can’t be iterated over.
We can fix this by going into the TestSuitesController
‘s Details
method. Once there, change the LINQ syntax to be:
Line 2 is particularly important. Include
tells Entity Framework to also retrieve the TestCases
entity when loading the TestSuite
.
With this change, the code should now work properly.
Adding a Web API
Okay, now that we have our entities and user interface, let’s add a Web API controller. We’ll do this via the add controller dialog, but choose an API Controller with Actions, using Entity Framework.
Select the TestCase
entity and name your controller TestsController
. This name will make sure the controller name doesn’t overlap the MVC controller for the TestSuite entity. Additionally, the default path of this API controller will be api/tests
given its name, and this sounds about right to me.
While the generated API methods will let you do a number of things out of the box, let’s add some custom endpoints to look at how you can query using Entity Framework.
Add the following code to your TestsController
:
This code defines four new GET endpoints to handle requests. Each one of these will then call to GetTestCasesByStatusAsync
and request to filter down to a specific status.
The GetTestCasesByStatus
method will then use LINQ’s Where
method to filter TestCase
entities in a query based on the status
parameter passed in.
This is an example of using Entity Framework to manage database objects without needing any SQL. The result is transparent, maintainable, and supports all code completion and compiler safety features in Visual Studio.
We can test this by making an HTTP GET request using Postman or similar to one of our new endpoints when the web application is running.
Note: If you’d like to learn more about Web API or how to create an API-only project, take a look at my dedicated article on .NET Core APIs.
Conclusion
We managed to get an ASP .NET Core 3.0 web application up and running with user management, database support, and a Web API.
As you can see, scaffolding and HTML helpers powered by attributes are a very powerful way of getting a user interface operational.
Additionally, Entity Framework gives you speed and flexibility in building a database application. It lets you work inside Visual Studio with the assistance of the compiler and IntelliSense. This minimizes the risk of you making a mistake.
Entity Framework migrations and automatic migrations make the database migration process ridiculously simple by baking it into the application itself. This can be an option for teams if supported with proper testing and logging as well as standard database backup and disaster recovery plans.
Ultimately, ASP .NET Core significantly lowers the bar to creating a new application and gives you productivity tools to get up and running quickly. It also has the depth and customization to take an initial concept and serve thousands of customers daily.
The post Create an ASP .NET Core Site with Entity Framework appeared first on Kill All Defects.
Top comments (4)
Nice Article About ASP.NET. Easy to understand for beginners.
The Entity framework does the mapping on application memory based on its designed logic while the query results returns from info.
Great article, very easy to follow.
I particularly liked the explanations about automatic migrations. I used them with EF6 and I couldn't figure out how to do with EF Core.
PS: there is a little typo in "we wll need to add an..."
Thanks! Fixed. Dang tpyos.
Thanks