欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

EFCore实现读写分离

程序员文章站 2022-03-15 13:08:48
读写分离一、数据访问层接口1.接口定义2.接口实现3.读写操作枚举类设计4.连接字符串读取设计5.DBContext拓展6.获取DBContext接口设计7.获取DBContext接口实现8.DBContext动态替换连接二、上端数据库连接字符串配置三、源码下载一、数据访问层接口1.接口定义代码如下(示例):using EFCoreDemo.CodeFirst.Migrations.Extend;using System;namespace EFCoreDemo.CodeFirst.IServ...

一、数据访问层接口

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