导读:本期聚焦于小伙伴创作的《C#如何实现数据库读写分离_C# EF Core读写分离配置教程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何实现数据库读写分离_C# EF Core读写分离配置教程》有用,将其分享出去将是对创作者最好的鼓励。

在C#项目里,当业务量增长导致数据库读写压力增大时,读写分离是常用的优化方案,核心思路是将写操作指向主库,读操作指向多个从库,从而分散数据库负载。EF Core作为常用的ORM框架,支持通过多数据库上下文配置实现读写分离。

C#如何实现数据库读写分离_C# EF Core读写分离配置教程

读写分离的核心原理

读写分离依赖主从数据库架构,主库负责处理增删改等写操作,从库通过主从复制同步主库数据,负责处理查询等读操作。在EF Core中实现读写分离,需要解决两个核心问题:一是区分当前操作是读还是写,二是根据操作类型选择对应的数据库连接。

操作类型判断规则

通常我们可以通过以下规则判断操作类型:

  • 写操作:调用SaveChangesSaveChangesAsync方法,或者执行INSERTUPDATEDELETE语句
  • 读操作:调用WhereFirstOrDefaultToList等查询方法,或者执行SELECT语句

EF Core读写分离基础配置

首先我们需要定义主从库的配置信息,然后在EF Core中注册多个数据库上下文,分别对应主库和从库。

配置主从库连接字符串

appsettings.json中添加主从库的连接配置:

{
  "ConnectionStrings": {
    "MasterDb": "Server=127.0.0.1;Database=TestDb;User Id=sa;Password=123456;",
    "SlaveDb1": "Server=192.168.0.1;Database=TestDb;User Id=sa;Password=123456;",
    "SlaveDb2": "Server=192.168.0.2;Database=TestDb;User Id=sa;Password=123456;"
  }
}

定义数据库上下文基类

先定义一个基础的数据库上下文,后续主库和从库上下文都继承该类:

using Microsoft.EntityFrameworkCore;

namespace ReadWriteSplitDemo.Data
{
    public class BaseDbContext : DbContext
    {
        public BaseDbContext(DbContextOptions options) : base(options)
        {
        }

        // 定义测试用的实体集合
        public DbSet<User> Users { get; set; }
    }

    // 用户实体
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

实现读写分离上下文工厂

我们需要一个工厂类来根据操作类型返回对应的数据库上下文,这里通过判断当前是否处于写操作状态来选择主库还是从库。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System;

namespace ReadWriteSplitDemo.Data
{
    // 操作类型枚举
    public enum DbOperationType
    {
        Read,
        Write
    }

    // 上下文工厂类
    public class DbContextFactory
    {
        private readonly IServiceProvider _serviceProvider;
        // 存储当前请求的操作类型,默认是读操作
        private static AsyncLocal<DbOperationType> _currentOperationType = new AsyncLocal<DbOperationType>();

        public DbContextFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        // 获取当前操作类型
        public static DbOperationType CurrentOperationType
        {
            get => _currentOperationType.Value;
            set => _currentOperationType.Value = value;
        }

        // 获取对应的数据库上下文
        public BaseDbContext GetDbContext()
        {
            if (CurrentOperationType == DbOperationType.Write)
            {
                // 写操作返回主库上下文
                var options = _serviceProvider.GetRequiredService<DbContextOptions<MasterDbContext>>();
                return new MasterDbContext(options);
            }
            else
            {
                // 读操作随机选择一个从库上下文,实现负载均衡
                var slaveDbs = new[] { "SlaveDb1", "SlaveDb2" };
                var random = new Random();
                var selectedSlave = slaveDbs[random.Next(slaveDbs.Length)];
                var options = _serviceProvider.GetRequiredService<DbContextOptions<SlaveDbContext>>();
                return new SlaveDbContext(options, selectedSlave);
            }
        }
    }

    // 主库上下文
    public class MasterDbContext : BaseDbContext
    {
        public MasterDbContext(DbContextOptions options) : base(options)
        {
        }
    }

    // 从库上下文
    public class SlaveDbContext : BaseDbContext
    {
        private readonly string _connectionName;
        public SlaveDbContext(DbContextOptions options, string connectionName) : base(options)
        {
            _connectionName = connectionName;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                // 这里实际项目中可以通过配置获取对应从库的连接字符串
                // 示例简化,实际需结合IConfiguration获取
            }
            base.OnConfiguring(optionsBuilder);
        }
    }
}

注册服务与中间件配置

Program.cs中注册相关服务,并添加中间件来自动识别写操作,切换操作类型。

using Microsoft.EntityFrameworkCore;
using ReadWriteSplitDemo.Data;

var builder = WebApplication.CreateBuilder(args);

// 注册主库上下文
builder.Services.AddDbContext<MasterDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("MasterDb"));
});

// 注册从库上下文
builder.Services.AddDbContext<SlaveDbContext>(options =>
{
    // 从库连接字符串后续在上下文内部动态选择
    options.UseSqlServer(builder.Configuration.GetConnectionString("SlaveDb1"));
});

// 注册上下文工厂
builder.Services.AddScoped<DbContextFactory>();

var app = builder.Build();

// 添加中间件,识别写操作
app.Use(async (context, next) =>
{
    // 判断请求是否为写操作,这里简单通过请求方法和路径判断,实际可结合业务调整
    if (context.Request.Method == "POST" || context.Request.Method == "PUT" || context.Request.Method == "DELETE")
    {
        DbContextFactory.CurrentOperationType = DbOperationType.Write;
    }
    else
    {
        DbContextFactory.CurrentOperationType = DbOperationType.Read;
    }
    await next();
});

app.MapGet("/", () => "Hello World!");

// 测试读接口
app.MapGet("/users", async (DbContextFactory factory) =>
{
    using var dbContext = factory.GetDbContext();
    return await dbContext.Users.ToListAsync();
});

// 测试写接口
app.MapPost("/users", async (DbContextFactory factory, User user) =>
{
    using var dbContext = factory.GetDbContext();
    dbContext.Users.Add(user);
    await dbContext.SaveChangesAsync();
    return user;
});

app.Run();

注意事项与优化建议

实际生产环境中使用EF Core实现读写分离还需要注意以下几点:

  • 主从复制存在延迟,写操作后立刻读的场景可能需要强制走主库,避免读取到旧数据
  • 从库可以配置多个,通过加权轮询、最少连接数等算法实现更合理的负载均衡
  • 事务操作必须走主库,避免事务跨库导致一致性问题
  • 可以结合AOP切面编程,在方法层面标记操作类型,更灵活地控制读写路由
读写分离只是数据库优化的一种方式,当单表数据量过大时,还需要结合分库分表等方案进一步提升性能。

C#EF_Core读写分离数据库配置修改时间:2026-07-05 13:03:29

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。