EFCore实现读写分离

2022-07-25,,

读写分离

  • 一、数据访问层接口
    • 1.接口定义
    • 2.接口实现
    • 3.读写操作枚举类设计
    • 4.连接字符串读取设计
    • 5.DBContext拓展
    • 6.获取DBContext接口设计
    • 7.获取DBContext接口实现
    • 8.DBContext动态替换连接
  • 二、上端数据库连接字符串配置
  • 三、源码下载

一、数据访问层接口

1.接口定义

代码如下(示例):

using EFCoreDemo.CodeFirst.Migrations.Extend;
using System;

namespace EFCoreDemo.CodeFirst.IService
{
    public interface IBaseService : IDisposable
    {
        #region Query
        /// <summary>
        /// 根据id查询实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public T Find<T>(int id, ReadWriteEnum readWriteEnum = ReadWriteEnum.Read) where T : class;

        #endregion

        #region Add
        /// <summary>
        /// 新增数据,即时Commit
        /// </summary>
        /// <param name="t"></param>
        /// <returns>返回带主键的实体</returns>
        public T Insert<T>(T t) where T : class;
        #endregion

        #region Delete
        /// <summary>
        /// 根据主键删除数据,即时Commit
        /// </summary>
        /// <param name="t"></param>
        public void Delete<T>(int Id) where T : class;

        public void Delete<T>(T t) where T : class;

        #endregion

        #region Other
        /// <summary>
        /// 立即保存全部修改
        /// 把增/删的savechange给放到这里,是为了保证事务的
        /// </summary>
        void Commit();

        #endregion
    }
}

2.接口实现

代码如下(示例):

using EFCoreDemo.CodeFirst.IService;
using EFCoreDemo.CodeFirst.Migrations.Extend;
using Microsoft.EntityFrameworkCore;
using System;

namespace EFCoreDemo.CodeFirst.Service
{
    public class BaseService : IBaseService, IDisposable
    {
        protected IDbContextFactory _ContextFactory = null;
        protected DbContext _Context { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public BaseService(IDbContextFactory contextFactory)
        {
            _ContextFactory = contextFactory;
        }
        /// <summary>
        /// 主库  即可以读取也可以增删改;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <param name="readWriteEnum"></param>
        /// <returns></returns>
        public T Find<T>(int id, ReadWriteEnum readWriteEnum = ReadWriteEnum.Read) where T : class
        {
            //ReadWriteEnum.Read;
            //确定链接---从库
            _Context = _ContextFactory.GetMainOrSlave(readWriteEnum);
            return this._Context.Set<T>().Find(id);
        }

        public T Insert<T>(T t) where T : class
        {
            //ReadWriteEnum.Read;
            //确定链接---主库
            _Context = _ContextFactory.GetMainOrSlave(ReadWriteEnum.Write);
            this._Context.Set<T>().Add(t);
            this.Commit();//写在这里  就不需要单独commit  不写就需要 
            return t;
        }
        public void Delete<T>(int Id) where T : class
        {
            _Context = _ContextFactory.GetMainOrSlave(ReadWriteEnum.Write);
            T t = this.Find<T>(Id);//也可以附加
            if (t == null) throw new Exception("t is null");
            this._Context.Set<T>().Remove(t);
            this.Commit();
        }

        public void Delete<T>(T t) where T : class
        {
            _Context = _ContextFactory.GetMainOrSlave(ReadWriteEnum.Write);
            if (t == null) throw new Exception("t is null");
            this._Context.Set<T>().Attach(t);
            this._Context.Set<T>().Remove(t);
            this.Commit();
        }

        public void Commit()
        {
            this._Context.SaveChanges();
        }

        public void Dispose()
        {
            if (this._Context != null)
            {
                this._Context.Dispose();
            }
        }
    }
}

3.读写操作枚举类设计

代码如下(示例):

 public enum ReadWriteEnum
    {
        Read,  //从库操作
        Write  //主库操作
    }

4.连接字符串读取设计

代码如下(示例):

 public class DBConnectionOption
    {
        public string MainConnectionString { get; set; }
        public List<string> SlaveConnectionStringList { get; set; }
    }

5.DBContext拓展

代码如下(示例):

public static class DbContextExtend
    {
        public static DbContext SetCurrentConnString(this DbContext dbContext, string conn)
        {
            if (dbContext is EFCodeFirstDemoContext)
            {

                var context = (EFCodeFirstDemoContext)dbContext; // context 是 EFCoreContext 实例;

                return context.SetCurrentConnString(conn);
            }
            else
                throw new Exception();
        }
    }

6.获取DBContext接口设计

代码如下(示例):

 public interface IDbContextFactory
    {
        public DbContext GetMainOrSlave(ReadWriteEnum readWriteEnum);
    }

7.获取DBContext接口实现

代码如下(示例):

public class DbContextFactory : IDbContextFactory
    {

        private DbContext _Context = null;

        private DBConnectionOption _readAndWrite = null;

        //private static int _iSeed = 0;//应该long

        /// <summary>
        ///能把链接信息也注入进来
        ///需要IOptionsMonitor
        /// </summary>
        /// <param name="context"></param>
        public DbContextFactory(DbContext context, IOptionsMonitor<DBConnectionOption> options)
        {
            _readAndWrite = options.CurrentValue;
            this._Context = context;
        }
        public DbContext GetMainOrSlave(ReadWriteEnum readWriteEnum)
        {
            //判断枚举,不同的枚举可以创建不同的Context 或者更换Context链接;
            switch (readWriteEnum)
            {
                case ReadWriteEnum.Write:
                    SetMainConnnectionString();
                    break;  //选择链接//更换_Context链接   //选择链接
                case ReadWriteEnum.Read:
                    SetSlaveConnectionString();
                    break;  //选择链接//更换_Context链接
                default:
                    break;
            }
            return _Context;
        }

        /// <summary>
        /// 更换成主库连接
        /// </summary>
        /// <returns></returns>
        private void SetMainConnnectionString()
        {
            string conn = _readAndWrite.MainConnectionString;
            _Context.SetCurrentConnString(conn);
        }


        private static int _iSeed = 0;

        /// <summary>
        /// 更换成主库连接
        /// 
        /// ///策略---数据库查询的负载均衡
        /// </summary>
        /// <returns></returns>
        private void SetSlaveConnectionString()
        {
            string conn = string.Empty;
            {
                // //随机
                //int Count=  _readAndWrite.ReadConnectionList.Count;
                //int index=  new Random().Next(0, Count);
                //conn = _readAndWrite.ReadConnectionList[index];
            }
            {
                //来一个轮询
                conn = this._readAndWrite.SlaveConnectionStringList[_iSeed++ % this._readAndWrite.SlaveConnectionStringList.Count];//轮询;  
            }
            {
                ///是不是可以直接配置到配置文件里面
            }
            _Context.SetCurrentConnString(conn);
        }

    }

8.DBContext动态替换连接

代码如下(示例):

  private string CurrentConnString = null;

        public DbContext SetCurrentConnString(string conn)
        {
            CurrentConnString = conn;
            return this;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseLazyLoadingProxies()
                       .UseSqlServer(CurrentConnString);
                 //.UseSqlServer("Server=.;Database=EFCoreCodeFirstDemo;uid=sa;pwd=Lykj20190325");
            }
            optionsBuilder.UseLoggerFactory(MyLoggerFactory);

        }

二、上端数据库连接字符串配置

在appsettings.json文件中加入ConnectionStrings节点

代码如下(示例):

"ConnectionStrings": {
    "MainConnectionString": "Server=.;Database=EFCoreCodeFirstDemo;uid=sa;pwd=Lykj20190325",
    "SlaveConnectionStringList": [
      "Server=.;Database=SlaveEFCoreCodeFirstDemo01;uid=sa;pwd=Lykj20190325",
      "Server=.;Database=SlaveEFCoreCodeFirstDemo02;uid=sa;pwd=Lykj20190325",
      "Server=.;Database=SlaveEFCoreCodeFirstDemo03;uid=sa;pwd=Lykj20190325"
    ]
  }

三、源码下载

下载

本文地址:https://blog.csdn.net/fisea/article/details/111982136

《EFCore实现读写分离.doc》

下载本文的Word格式文档,以方便收藏与打印。