When building .NET applications, choosing the right database solution is an important step to good performance and scalability. Neon's serverless Postgres is a great choice for .NET developers, thanks to features like automatic scaling, branching, and connection pooling that integrate well with .NET's ecosystem.
In this guide, we'll walk through setting up a Neon database with a .NET application and explore best practices for connecting and interacting with Neon Postgres and structuring your application using Entity Framework Core.
Prerequisites
- .NET 8.0 or later installed
- A Neon account
- Basic familiarity with Entity Framework Core
Setting Up Your Neon Database
- Create a new Neon project from the Neon Console
- Note your connection string from the connection details page
Your connection string will look similar to this:
Creating a .NET Project with Neon Integration
With your Neon database set up, let's create a sample inventory management system to demonstrate Neon integration.
-
Create a new .NET Web API project:
This command creates a new Web API project with a basic structure including:
Program.cs
: The entry point of your applicationappsettings.json
: Configuration filesProperties/launchSettings.json
: Debug and launch configuration
Then navigate to the project directory:
-
Install the required NuGet packages:
These packages provide us with the following:
Npgsql.EntityFrameworkCore.PostgreSQL
: The Postgres database provider for Entity Framework CoreMicrosoft.EntityFrameworkCore.Tools
: Command-line tools for migrationsMicrosoft.EntityFrameworkCore.Design
: Design-time tools for EF Core
-
Create a Models directory and add your entity models:
Create a new file
Models/Product.cs
:Our
Product
model includes the following:- Data annotations for validation
- A unique identifier (
Id
) - Basic product information fields
- Optimistic concurrency control (
Version
) - Comments indicating where to add relationships
-
Create a Data directory for your database context, this is where you will define your
DbContext
which represents your database schema:Create a new file
Data/InventoryContext.cs
with the following content:The
DbContext
includes:- Entity configuration using Fluent API
- Precision settings for decimal values
- Default value configurations
- Index definitions
- Support for separate configuration classes
We've set up the basic structure for our application. Next, we'll configure the database connection and implement the repository pattern for database operations.
Configuring Database Connection
With the database context in place, we need to configure the connection to our Neon database. Let's set this up securely.
Basic Configuration
Update Program.cs
to include the database context:
Managing Connection Strings Securely
There are two main approaches to storing your connection string securely:
- Development: Use
appsettings.Development.json
for local development:
- Production: Use environment variables:
That way, you can set the NEON_CONNECTION_STRING
environment variable in your production environment to securely store your connection string.
As an alternative, you can use the Azure Key Vault
to store your connection string securely. To learn more about this approach, check out the Azure Key Vault documentation.
Testing the Configuration
To verify everything is working correctly, start your application:
If everything is set up correctly, you should see a message indicating that the application is running and listening on a specific port.
Implementing Repository Pattern
The repository pattern acts as an abstraction layer between your application logic and data access code. This pattern helps maintain clean separation of concerns and makes your code more testable and maintainable.
In our inventory system, we'll implement this pattern to handle all database operations related to products.
First, let's define the interface that specifies what operations our repository can perform. Create a new file Repositories/IProductRepository.cs
with the following content:
This interface defines the contract for our repository. That way all implementations will provide these basic CRUD (Create, Read, Update, Delete) operations. Using an interface allows us to easily swap implementations or create mock versions for testing.
Next, let's implement the repository, starting with the ProductRepository
class in Repositories/ProductRepository.cs
:
Each method in the repository serves a specific purpose:
GetByIdAsync
: Retrieves a single product by its ID using asynchronous operationsGetAllAsync
: Returns all products from the database as an enumerable collectionCreateAsync
: Adds a new product to the database and returns the created productUpdateAsync
: Modifies an existing product's information in the databaseDeleteAsync
: Removes a product from the database by its ID
To use this repository in your application, register it with the dependency injection container in Program.cs
:
This registration makes the repository available throughout your application.
Then you can inject it into your controllers or services, create a new controller Controllers/ProductsController.cs
:
The controller uses the repository to interact with the database. Each action corresponds to a CRUD operation and returns appropriate HTTP status codes based on the operation's success.
The repository pattern is particularly useful when working with Postgres as it provides a single place to implement database-specific optimizations and connection handling strategies.
After implementing the repository pattern, make sure to register the controllers in Program.cs
:
With the repository pattern in place, we are nearly ready to start using our Neon database. Before that, let's add some sample data to the database using migrations.
Migrations
Similar to other web frameworks, Entity Framework Core uses migrations to keep track of changes to your database schema. With migrations, you can incrementally apply schema updates as your data models evolve over time.
dotnet-ef
Installing Before creating migrations, make sure you have the dotnet-ef
tool is installed. This tool provides command-line capabilities for managing Entity Framework migrations and database updates.
Install dotnet-ef
globally by running:
Note: If you encounter any issues after installation, make sure your environment's
PATH
includes the directory where .NET global tools are installed. It is usually~/.dotnet/tools
on macOS and Linux. You can add this to yourPATH
temporarily with the following command, or add it to your shell configuration file (like.bashrc
or.zshrc
) to make it permanent:
Creating and Applying Migrations
Once dotnet-ef
is installed, you can create a migration to define your database schema based on your DbContext
and entity classes.
-
The following command generates the initial migration files, representing the schema of your database:
This command creates a new folder called
Migrations
(if it doesn't already exist) and generates files that contain code to create your database tables. It examines yourDbContext
and entity classes to determine the schema, so that you don't have to write SQL manually. -
Now, apply the migration to your actual database:
This command executes the generated migration code against the database, creating the necessary tables and constraints based on your model definitions.
After these steps, your database will be fully synchronized with your data model, and you're ready to start using it in your application.
Now if you run your application and navigate to http://localhost:5221/api/products
, you should see an empty array []
as we haven't added any products yet.
Testing CRUD Operations
With our API and database set up, we’re ready to test the CRUD operations. We’ll use simple HTTP requests to add, retrieve, update, and delete products from our Neon database.
1. Adding a New Product
To add a new product, send a POST
request to http://localhost:5221/api/products
. Here’s an example using curl
:
Alternatively, you can use Postman by setting up a
POST
request to the same URL, setting the headerContent-Type
toapplication/json
, and adding the JSON body.
If the request is successful, the API will return the newly created product with an auto-generated Id
.
2. Retrieving All Products
To retrieve all products, send a GET
request to http://localhost:5221/api/products
:
This should return a list of all products in the database. If you just added one product, you’ll see an array with that single product.
3. Retrieving a Product by ID
To retrieve a specific product, send a GET
request to http://localhost:5221/api/products/{id}
, replacing {id}
with the actual product ID. For example:
This request will return the product with the specified Id
. If no product is found, it may return a 404 Not Found
.
4. Updating a Product
To update an existing product, send a PUT
request to http://localhost:5221/api/products/{id}
. Include the updated information in the request body. Here’s an example:
This request updates the product with Id = 1
. The API will return the updated product information if the operation succeeds.
5. Deleting a Product
To delete a product, send a DELETE
request to http://localhost:5221/api/products/{id}
, replacing {id}
with the product’s actual ID:
If successful, the API will delete the product from the database and return a status code indicating success.
Conclusion
Connecting .NET applications to Neon gives you a strong and scalable database setup. By following these steps and using features like connection pooling and automatic scaling, you can create applications that perform well even as they grow.
As next steps, consider adding more features to your application, such as authentication and authorization. You can also explore advanced Neon features like branching and data replication to enhance your application's performance and reliability.
For more details, check out:
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. Users on paid plans can open a support ticket from the console. For more details, see Getting Support.