Base solution for your next web application
Open Closed

Read-replica support with Entity Framework Core and ABP Framework ? #12405


User avatar
0
kansoftware created

We want to use a read replica of the database in our ABP project to optimize read-heavy operations and improve performance.

How can we configure ABP to route read queries to the replica while ensuring write operations still go to the primary database?
Please guide us on best practices, configuration settings, and potential pitfalls when implementing read-replica support with Entity Framework Core and ABP Framework.


3 Answer(s)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi kansoftware

    To provide a specific suggestion, would you like to direct all your GET queries to the Replica database, or are you looking to use a repository with a read structure to direct certain read queries to the Replica database? Let me know this distinction, and I can offer a more specific suggestion.

  • User Avatar
    0
    kansoftware created

    use a repository with a read structure

  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @kansoftware

    Abp uses DbContext internally, so you need to configure your primary database and read replica (read-only) database.

    "ConnectionStrings": {
      "Default": "Server=localhost; Database=MainDb; Trusted_Connection=True; TrustServerCertificate=True;",
      "ReadReplica": "Server=localhost; Database=ReplicaDb; Trusted_Connection=True; TrustServerCertificate=True;"
    }
    

    Update your DB configuration to support a read replica connection.

    public static class YourProjectNameDbContextConfigurer
    {
        public static void Configure(DbContextOptionsBuilder< YourProjectNameDbContext > builder, string primaryConnectionString, string readReplicaConnectionString, bool useReadReplica = false)
        {
            if (useReadReplica)
            {
                builder.UseSqlServer(readReplicaConnectionString); // Use Read Replica for Queries
            }
            else
            {
                builder.UseSqlServer(primaryConnectionString); // Use Primary DB
            }
        }
    }
    

    Modify YourProjectEntityFrameworkModule.cs:
    Example Code

    public override void PreInitialize()
    {
        if (!SkipDbContextRegistration)
        {
            var configuration = AppConfigurations.Get(
                WebContentDirectoryFinder.CalculateContentRootFolder(),
                addUserSecrets: true
            );
            
            var primaryConnectionString = configuration.GetConnectionString("Default");
            var readReplicaConnectionString = configuration.GetConnectionString("ReadReplica");
                
            Configuration.Modules.AbpEfCore().AddDbContext< YourProjectNameDbContext >(options =>
            {
                // Register Primary DB Context (for writes)
                YourProjectNameDbContextConfigurer.Configure(options.DbContextOptions, primaryConnectionString, readReplicaConnectionString, false);
            });
        
            Configuration.Modules.AbpEfCore().AddDbContext< ReadOnlyDbContext >(options =>
            {
                // Register Read-Only DB Context (for reads)
                YourProjectNameDbContextConfigurer.Configure(options.DbContextOptions, primaryConnectionString, readReplicaConnectionString, true);
            });
        }
    }
    

    Since Abp repositories use DbContext internally, we need a separate read-only DbContext.

    public class ReadOnlyDbContext : YourProjectNameDbContext
    {
        public ReadOnlyDbContext(DbContextOptions< ReadOnlyDbContext > options) : base(options) { }
    }
    

    We now route read queries to ReadOnlyDbContext.

    public class ReadOnlyRepository : EfCoreRepositoryBase, IReadOnlyRepository
        where TEntity : class, IEntity
    {
        public ReadOnlyRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { }
    
        public override IQueryable GetAll()
        {
            return base.GetAll().AsNoTracking(); // Ensures read-only queries
        }
    }
    

    You can create a ReadOnlyRepository to perform read operations using the designated replica database and use it wherever you need to execute read queries.