Introduction
In recent years, a number of competing Single Page Application (SPA) frameworks like Angular, Vue, and React have surfaced. One of those, React - a product of Facebook - has runaway from the pack and has remained strides ahead of it. In this tutorial, I will show you how to write a data driven ASP.NET Core 2.0 SPA that includes CRUD operations (create, read, update and delete) using React. Our data will be stored in a SQL Server database, but beyond that, we will not be using any other third party technologies, such as Redux or even JQuery. Stick around and you'll see what makes React so great, and how it makes JQuery virtually obsolete.
Background
React provides many benefits, the most important of which (in my opinion) is components. React components are simple. They simplify your application architecture, they are encapsulated and they can be reused, and styling components is greatly simplified (say goodbye to your global style sheet). In this tutorial, we'll be writing JSX using TypeScript so we will also be utilizing ES6 JavaScript enhancements, which offer more benefits. I decided to write this tutorial because I could not find a comprehensive tutorial that dealt with using React in a data driven ASP.NET Core 2.0 application. What follows is part one of a three part series.
Using the Code
You can use the code in this tutorial in your own applications by simply changing the model names, i.e., changing the data type of the model data passed to each component. That may not make sense to you now, but it will make perfect sense by the time you get to the end of this tutorial. Also by that time, you will understand how React fits within the ASP.NET Core framework. Let's begin, shall we!
Building Our React Application
In this tutorial, we'll be building a movie database application. Visual Studio 2017 includes a basic React project template, so we'll be starting with that. Let's create our project...
- Open Visual Studio and select New Project.
- From the New Project dialog box, select .NET Core and then ASP.NET Core Web Application (figure 1):
- From the ASP.NET Core Web Application dialog box, select React.js (figure 2)
- Name the application '
TestReactMovieApplication
'.
Building Our Models
Our models consists of three classes: Actor
, Movie
, and MovieActor
, which simply defines the relationship between movies and actors. Let's start by creating a new Class
. In Visual Studio, right-click on the project, select Add, then New Class. Call the new class models.cs (figure 2.1).
Next open the models.cs file and paste the Actor
class as shown below (fig 2.2). Then do the same for the Movie
and MovieActor
classes shown below that (figures 2.3 and 2.4). Note our models.cs file will contain all three classes in order to reduce file-sprawl.
[Table("Actor")]
public class Actor
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order = 0)]
[Required]
[DisplayName("Id")]
public int Id { get; set; }
[Required]
[MaxLength(50)]
[DisplayName("Name")]
public string Name { get; set; }
[MaxLength(10)]
[DisplayName("Gender")]
public string Gender { get; set; }
[DisplayName("Age")]
public int? Age { get; set; }
[MaxLength(255)]
[DisplayName("Picture")]
public string Picture { get; set; }
}
Figure 2.2
[Table("Movie")]
public class Movie
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order = 0)]
[Required]
[DisplayName("Id")]
public int Id { get; set; }
[Required]
[MaxLength(100)]
[DisplayName("Title")]
public string Title { get; set; }
[Required]
[MaxLength(100)]
[DisplayName("Director")]
public string Director { get; set; }
[Required]
[DisplayName("DateReleased")]
public DateTime DateReleased { get; set; }
[MaxLength(50)]
[DisplayName("ReleasedBy")]
public string ReleasedBy { get; set; }
[MaxLength(10)]
[DisplayName("Rating")]
public string Rating { get; set; }
[MaxLength(50)]
[DisplayName("Genre")]
public string Genre { get; set; }
[DisplayName("GrossRevenue")]
public decimal? GrossRevenue { get; set; }
}
Figure 2.3
[Table("MovieActor")]
public class MovieActor
{
[Key, Column(Order = 0)]
[Required]
[DisplayName("MovieId")]
public int MovieId { get; set; }
[Key, Column(Order = 1)]
[Required]
[DisplayName("ActorId")]
public int ActorId { get; set; }
}
Figure 2.4
Next, we'll need to create a database context for EntityFramework. Right click on your project, select Add, then Class. Give the file the name AppDbContext.cs. Then open the new file and paste the following code (figure 2.5). Note that we have pluralized the names of the DBSets. This helps us easily differentiate the models from the datasets. Also since our MovieActor
table has a compound primary key, we need to specify that in the OnModelCreating()
method.
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<appdbcontext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<movieactor>()
.HasKey(m => new {m.MovieId,m.ActorId});
}
public DbSet<actor> Actors {get; set;}
public DbSet<movie> Movies {get; set;}
public DbSet<movieactor> MovieActors {get; set;}
}
Figure 2.5
We'll need to add the DbContext
to the project services. We're also going to specify a JSON option that will maintain the case of our field names rather than changing them to camelCase, which is the default. (This will break the Microsoft Fetch Data example - don't be alarmed.) Open startup.cs in your project root and make sure the ConfigureServices()
method looks like the following (figure 2.5.1).
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<appdbcontext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("AppDbContext"));
options.ConfigureWarnings(x => x.Ignore(RelationalEventId.AmbientTransactionWarning));
});
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
}
Figure 2.5.1
Configuring the Application
tsconfig.json
Since we'll be writing a bit of TypeScript, we're going to turn TypeScript's 'strict
' mode off to make our life easier. In tsconfig.json, which lives in the root of your application, let's set 'strict
' to false
as shown below:
"compilerOptions": { "strict": false
Connection String
We'll set our connection string in appsettings.json, also in the root of our application (figure 2.6). Your connection string should look similar to the one shown, however your server name may be different depending on what you specified when you installed SQL Server. Enter it just before the "Logging" settings.
{
"ConnectionStrings": {
"AppDbContext": "server=localhost\\SQL2012;Database=Movies;Integrated Security=True;
MultipleActiveResultSets=true"
},
"Logging": {
Anatomy of a React TS Application
At this point, I'd like to digress and talk about the anatomy of our React.js application. This tutorial will follow an MVC (model, view, controller) methodology except that instead of server-side views, we will have client side React components acting as our views. I also want to talk about the structure of our React application in a bit more detail.
The Project
When you create an ASP.NET Core React application, you'll find the following project items (figure 7). The most important things to know are: that we no longer user server-side views (instead, we have React components under the ClientApp folder); that the client script must be "compiled" (performed by webpack); that the client script is "transpiled" (converted from TypeScript to ES5 or ES6 per your tsconfig.json file); that all server to client communication happens using AJAX and JSON.
Figure 7
Models
As well as the server side models that we created above (models.cs in figure 7), React requires client-side models. We'll be creating a client-side models file with essentially the same structure except for the datatypes (TypeScript datatypes are simpler and fewer) and without any of the ASP.NET data attributes.
Components (Views)
In our React application, we'll be building client-side components (in ClientApp\components) to act as the "views" of our application (fig 10). As in traditional ASP.NET MVC applications, we will create a folder for each component (model). However, rather than creating five views, we will perform all of our CRUD operations with just three, which will simplify maintenance of our views.
Figure 10
Also, each component will have its own .css file. This is a big improvement over a global styles.css file in which everything affects everything else. The only thing to know about the component css file is that it will only ever affect itself and any children that it contains.
Controllers
As in traditional ASP.NET MVC applications, our React application will have one controller for each model, i.e., we will have three separate controllers (figure 11).
Figure 11
There will be a few differences in our controllers compared to a traditional MVC controller. All of our controllers will be WebAPI controllers and all methods will return JSON data. This has the advantage of allowing us to leverage our application to also serve mobile applications. Secondly, in following many WebAPI practices, we will use the HTTP methods PUT
and POST
for edit and create respectively. So instead of a Create
method, we will have a POST
method, and instead of an Edit
method, we will have a PUT
method.
That concludes Part 1 of this tutorial. Stick around. In Part 2, we'll be creating our components.
History
- 11/7/2017: Original article