In the realm of enterprise software, two programming languages have dominated: C# (c sharp) and Java. Java has a runtime environment – which must be installed on computers that want to run Java code – called the JVM, and likewise C# has a runtime environment called .NET. I have a lot of experience with Java/JVM, but not as much with C#/.NET. Of the two languages, Java is probably more popular. This is likely (at least in part) due to the fact that Java and the JVM were open source while C#/.NET’s source code had been Microsoft’s property and closed to the public.
Since June 2016, a version of .NET has been available as open source software. That version is .NET Core (.NET Framework is the closed source relative). After learning about the open source release, I wanted to work with the C#/.NET system. Here, I’m going to document my notes on building a simple REST API connected to an SQL database – in this case, PostgreSQL (which is case sensitive, and that matters).
Installation
Installation of .NET Core is pretty easy. Here’s a link to Microsoft’s website with details for different operation systems:
https://docs.microsoft.com/en-us/dotnet/core/install
Starting the Project
To start a new .NET Core REST API, we can issue a single command. After the command runs, we’ll have a C#/.NET project scaffold in our project directory.
dotnet new webapi -o [project-name] ls [project-name]
appsettings.Development.json
bin
[project-name].csproj
Program.cs
Startup.cs
appsettings.json
Controllers
obj
Properties
WeatherForecast.cs
To start the application, we can issue this command:
dotnet run
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to https://localhost:5001 on the IPv6 loopback interface: 'Cannot assign requested address'.
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to http://localhost:5000 on the IPv6 loopback interface: 'Cannot assign requested address'.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
HTTPS/HTTP and CORS
By default the application is configured for HTTPS. I’ll be hosting my project from a container service which will provide the HTTPS. So to make things easier, I’ll turn off HTTPS. To do this first specify the listen addresses (with port number), then tell the program not to redirect to HTTPS in Startup.cs.
Specify Listen Addresses
appsettings.json
In the appsettings.json, we can add a section for configuring the Kestrel server.
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:3000"
},
{
"Https": {
"Url": "https://0.0.0.0:3001"
},
}
}
},
}
Program.cs
In Program.cs, call the UseUrls
method on the WebBuilder
object to specify the server’s listen addresses.
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseUrls("http://+:5000"); webBuilder.UseStartup<Startup>(); }); }
In Startup.cs, simply comment out or remove the line in the Configure method with the call to app.UseHttpsRedirection
.
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "lists_api v1")); } // app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
Add CORS Policy
Add the following policy to the ConfigureService
method in the Startup.cs
class.
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "VtagApi", Version = "v1" }); }); services.AddCors(o => o.AddPolicy("AllowAll", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); })); }
Apply CORS Policy
Call app.UseCors
method to use AllowAll
policy.
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "VtagApi v1")); } // app.UseHttpsRedirection(); app.UseCors("AllowAll"); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
- https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-5.0&tabs=visual-studio
- https://stackoverflow.com/a/31942128/10205096
HTTP Requests and Responses
Entity Framework Core
Entity Framework Core is an Object Relational Mapping (ORM) tool that maps database entities to C# objects.
Migrations
The following dotnet cli commands can be used to perform migrations.
First, make desired changes to the Models or database context. Then, create the migration.
dotnet ef migrations add <MIGRATION_NAME>
This will add a migration script to the Migrations directory. To execute the migration:
dotnet ef database update
Revert Migrations
First, list the database migrations.
dotnet ef migration list
From that list grab the name of the migration you want to revert to.
dotnet ef database update <MIGRATION_NAME>
Then, remove add the reverted migrations.
dotnet ef migrations remove
Case Sensitive Table Column Names
- https://docs.microsoft.com/en-us/ef/core/miscellaneous/collations-and-case-sensitivity
- https://www.postgresql.org/docs/12/collation.html
Install MongoDB Driver
dotnet add [ProjectName].csproj package MongoDB.Driver
Column/Property Value Converters
Often times, our C# and database data types don’t quite match up. Using a string
for a VARCHAR
, works “out of the box” with the EF, but matching enumerated types from the database is a bit trickier. We’ll also look at converting a TEXT
type from the database into a C# List<string>
. With this conversion in place we can store and work with CSV strings.
- https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations
- https://stackoverflow.com/questions/35367621/entity-framework-query-a-comma-separated-field
Namespaces
This section contains information about important Namespaces using in the creation of the REST API application.
Annotations
System.ComponentModel.DataAnnotations.Schema
- Table – directs a Model class to use specified database table.
namespace MyApi
{
[Table("entities")]
public class Entity
{
// Model class defined here.
}
}
- Column – specify which column the Model property is mapped to.
namespace MyApi
{
[Table("entities")]
public class Entity
{
[Column("size")]
public int Size { get; set; }
// Model class defined here.
}
}
- NotMapped – tell Entity Framework not to map a property/column.
namespace MyApi
{
[Table("entities")]
public class Entity
{
[NotMapped]
public int Name { get; set; }
// Model class defined here.
}
}
Docker
Using Docker with .NET Core apps is pretty simple.