Part 4: Persisting Invoices in a Database with Entity Framework Core
In the previous part, we implemented the billing service that consumes order events from RabbitMQ and generates invoices. Now, we will add persistence for the invoices in a database using Entity Framework Core.
Database Configuration
First, let’s set up Entity Framework Core and the database.
Adding Dependencies
Add the following dependencies to your project:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
Configuring the Database Context
Create a folder named Data
and add the following BillingDbContext
class:
using BillingService.Models;
using Microsoft.EntityFrameworkCore;
namespace BillingService.Data
{
public class BillingDbContext(DbContextOptions<BillingDbContext> options) : DbContext(options)
{
public DbSet<Invoice> Invoices { get; set; }
}
}
Configuring the Database Connection
In the appsettings.json
file, add the connection string for the SQLite database:
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=Data/app.db"
}
}
Registering the Context in Startup
In the Program.cs
file, register the BillingDbContext
and configure the connection string:
using BillingService.Data;
using BillingService.Services;
using Microsoft.EntityFrameworkCore;
using RabbitMQ.Client;
var builder = WebApplication.CreateBuilder(args);
// Configuring the DbContext
builder.Services.AddDbContext<BillingDbContext>(options =>
{
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"));
});
// Configuring RabbitMQ
builder.Services.AddSingleton<IConnection>(sp =>
{
var factory = new ConnectionFactory() { HostName = "localhost" };
return factory.CreateConnection();
});
builder.Services.AddSingleton<IEventSubscriber, RabbitMqEventSubscriber>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// Start the event subscription service
var subscriber = app.Services.GetRequiredService<IEventSubscriber>();
subscriber.Subscribe();
app.Run();
Updating the Billing Service
Let’s update the billing service to save invoices to the database.
Updating the Event Consumer (Subscriber)
Update the RabbitMqEventSubscriber
class to use the BillingDbContext
:
using BillingService.Data;
using BillingService.Models;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
using System.Text.Json;
namespace BillingService.Services;
public class RabbitMqEventSubscriber(IConnection connection, IServiceScopeFactory scopeFactory) : IEventSubscriber
{
public void Subscribe()
{
using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "orders", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += async (model, deliverEventArgs) =>
{
var body = deliverEventArgs.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var order = JsonSerializer.Deserialize<Order>(message);
if (order == null) return;
// Create a scope to resolve scoped services
using var scope = scopeFactory.CreateScope();
// Create a context instance to interact with the database
var context = scope.ServiceProvider.GetRequiredService<BillingDbContext>();
var invoice = new Invoice
{
Id = Guid.NewGuid(),
OrderId = order.Id,
ProductName = order.ProductName,
Quantity = order.Quantity,
Price = order.Price,
CreatedAt = DateTime.UtcNow
};
context.Invoices.Add(invoice);
await context.SaveChangesAsync();
Console.WriteLine($"Invoice generated: {JsonSerializer.Serialize(invoice)}");
};
channel.BasicConsume(queue: "orders", autoAck: true, consumer: consumer);
Console.WriteLine("Press [enter] to exit.");
Console.ReadLine();
}
}
Updating the Database
Create a migration to update the database with the new invoices table:
- Create the Migration:
dotnet ef migrations add InitialCreate
2. Update the Database:
dotnet ef database update
Testing Invoice Persistence
- Run the Project: Press F5 to start the project.
- Verify Persistence: Use Postman or another tool of your choice to send a POST request to the order service and verify that the corresponding invoice is saved in the database.