PostgreSQL


To use PostgreSQL database integration, the NuGet package CrystalSharp.PostgreSql must be installed.

IMPORTANT

Kindly keep in mind that this particular configuration is intended for the purpose of storing data in the database and reading the data from the database. However, the configuration procedures for the Event Store, Read Model Store, and Sagas will be approached differently.

In order to integrate a database effectively, it is necessary to adhere to the following mandatory steps:

  • Inject the IPostgreSqlEntityFrameworkCoreContext interface into the DbContext constructor.
  • Override the method public virtual Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) of the DbContext class.
  • Call the SaveChanges method of the IPostgreSqlEntityFrameworkCoreContext interface.
  • Register the implementation of DbContext.

Following is the code that illustrates how to perform the above steps:

public class AppDbContext : DbContext
{
    private readonly IPostgreSqlEntityFrameworkCoreContext _pgEfContext;
    public DbSet<Order> Order { get; set; }

    public AppDbContext()
    {
        //
    }

    public AppDbContext(DbContextOptions<AppDbContext> options, IPostgreSqlEntityFrameworkCoreContext pgEfContext)
        : base(options)
    {
        _pgEfContext = pgEfContext;
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        return await _pgEfContext.SaveChanges(this, cancellationToken).ConfigureAwait(false);
    }
}

In the above code snippet, the AppDbContext class overrides the SaveChangesAsync method of the DbContext class. Overriding the SaveChangesAsync method is a mandatory requirement. The constructor injects the IPostgreSqlEntityFrameworkCoreContext interface, which is necessary for saving changes in the database. Within the overridden SaveChangesAsync method, pay attention to the utilization of the await _pgEfContext.SaveChanges(this, cancellationToken) method. This method executes all the essential operations prior to saving and ultimately persists the data in the database.

NOTE

If there are any domain event handlers, then those domain event handlers will be triggered only after the data has been successfully stored in the database.

Registration for the DbContext implementation is required. Following is the code that illustrates how to register AppDbContext in the Program.cs file:

PostgreSqlSettings postgreSqlSettings = new("CONNECTION-STRING");

CrystalSharpAdapter.New(builder.Services)
    .AddCqrs(typeof(PlaceOrderCommandHandler))
    .AddPostgreSql<AppDbContext>(postgreSqlSettings)
    .CreateResolver();

In the above code snippet, when initializing the Crystal Sharp framework, the PostgreSqlSettings class is instantiated with a connection string and then a call to an extension method AddPostgreSql<AppDbContext>(postgreSqlSettings) for the DbContext registration.

In order to save the data, call the overridden method SaveChangesAsync. Following is the code that illustrates the saving of data:

public class PlaceOrderCommandHandler : CommandHandler<PlaceOrderCommand, PlaceOrderResponse>
{
    private readonly AppDbContext _dbContext;

    public PlaceOrderCommandHandler(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public override async Task<CommandExecutionResult<PlaceOrderResponse>> Handle(PlaceOrderCommand request, CancellationToken cancellationToken = default)
    {
        Order order = Order.PlaceOrder(request.OrderCode, request.TotalPrice);

        await _dbContext.Order.AddAsync(order, cancellationToken).ConfigureAwait(false);
        await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

        PlaceOrderResponse response = new() { OrderId = order.GlobalUId };

        return await Ok(response);
    }
}

In the above code snippet, AppDbContext is injected into the command handler constructor, and then in the Handle method of the command handler, a new order is created and saved. Here, notice the await _dbContext.SaveChangesAsync(cancellationToken) method; it is the same SaveChangesAsync of the DbContext class, which is overridden in the AppDbContext class.