黄山秋叶 發表於 2020-8-17 14:03:00

企业项目实战 .Net Core + Vue/Angular 分库分表日志系统二 | 简单的分库分表设计

<h3 id="教程">教程</h3>
<p>01 |模块化方案一</p>
<p>02 |模块化方案二</p>
<h1 id="其他教程预览">其他教程预览</h1>
<h3 id="分库分表项目实战教程">分库分表项目实战教程</h3>
<h3 id="git地址-httpsgithubcommrchujiueasylogger">Git地址: https://github.com/MrChuJiu/EasyLogger</h3>
<p>01 |前言</p>
<p>02 |简单的分库分表设计</p>
<p>03 |控制反转搭配简单业务</p>
<p>04 |强化设计方案</p>
<p>05 |完善业务自动创建数据库</p>
<p>06 |最终篇-通过AOP自动连接数据库-完成日志业务</p>
<h1 id="前言">前言</h1>
<p>项目涉及到了一些设计模式,如果你看的不是很明白,没有关系坚持下来,写完之后去思考去品,你就会有一种突拨开云雾的感觉,所以请不要在半途感觉自己看不懂选择放弃,如果我哪里写的详细,或者需要修正请联系我,谢谢。</p>
<h1 id="创建项目">创建项目</h1>
<h2 id="1sdk安装">1.SDK安装</h2>
<p>我们开发用的vs版本是2019 .Net Core的版本是3.1<br>
下载 SDK 地址 :https://dotnet.microsoft.com/download</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/CoreSDKDown.png" alt="" loading="lazy"></p>
<h2 id="2新建项目">2.新建项目</h2>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/Create1.png" alt="" loading="lazy"></p>
<p><img src="https://images.cnblogs.com/cnblogs_com/HDONG/1827694/o_200813143420CreateApi.png" alt="" loading="lazy"></p>
<p>这里选择Core 版本是3.1 项目类型是API<br>
Docker支持我们不勾选,我会在后续给大家单独再开一个系列 我们专讲,慢慢来。</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/xiangmujiegou.png" alt="" loading="lazy"></p>
<p>这里可以看到一个非常干净的项目就创建出来了,项目结构就是这样,里面的详情可以去看老张的第二个章节,讲的很明白,我就不在重复了<br>
https://www.cnblogs.com/laozhang-is-phi/p/9495620.html<br>
进阶可以去看开源源码 https://github.com/aspnet/MetaPackages/tree/master/src/Microsoft.AspNetCore<br>
然后我们直接F5 启动项目看看 是不是新建的项目是不是没有问题,项目一切正常,我们开始进入正轨。</p>
<h1 id="项目思考">项目思考</h1>
<h2 id="1分表">1.分表</h2>
<p>分表这个功能,就是把相同结构不同名称的多张表数据读取出来,这个部分其实很简单,我们常见的ORM都支持切换表名进行查询。</p>
<h2 id="2分库">2.分库</h2>
<p>分库如何实现呢。<br>
先来看一下我们常用的ORM框架是如何连接数据库的。</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/SqlSugarCLien.png" alt="" loading="lazy"> SqlSugar连接数据库</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/FreeSqlClient.png" alt="" loading="lazy"> FreeSql连接数据库</p>
<p>它们有一个共同点,NEW一个对象传递数据库连接字符串,设置好数据库类型,各自的xxx配置,就会得到一个连接的Client,然后就可以进行CRUD了。</p>
<p>那New多个对象,每个对象都是不同的连接字符串岂不是就可以操作多个数据库了。</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/errsql.PNG" alt="" loading="lazy"></p>
<p>大家写代码可别直接在业务/数据访问层这么写,整不好就让你下班领盒饭了。</p>
<p>那么这种情境下因该如何设计能让代码更加规范、易扩展呢!</p>
<h2 id="3思考">3.思考</h2>
<p>思路就是这样,那么如果让你来设计,你会怎么做呢,大家可以自己尝试着先去根据自己想法设计看看,然后我们下一节,我来给讲解我的设计方案。</p>
<h1 id="设计要求">设计要求</h1>
<p>为了提高难度设计难度我们来同时兼容FreeSql、SqlSugar2款现在最热门的ORM。<br>
我们设计要做到:</p>
<h4 id="易扩展就算再来一个我也能轻松支持">易扩展(就算再来一个我也能轻松支持)</h4>
<h4 id="切换快不改动业务代码前提下2个orm框架我想用谁就用谁随便切换">切换快(不改动业务代码前提下,2个ORM框架我想用谁就用谁,随便切换)</h4>
<h4 id="可共存可以取2款orm各自优点在一起开发使用">可共存(可以取2款ORM各自优点在一起开发使用)</h4>
<h1 id="实战">实战</h1>
<h2 id="1制定规范">1.制定规范</h2>
<h4 id="我们先新建一个类库-easyloggerdbstorageps该类库用于制定规范提供接口">①、我们先新建一个类库 EasyLogger.DbStorage(ps:该类库用于制定规范,提供接口)</h4>
<h4 id="新建-interface-文件夹">②、新建 Interface 文件夹</h4>
<h4 id="新建泛型接口-ianystorage-存储器">③、新建泛型接口 IAnyStorage (存储器)</h4>
<p>该接口规范字典操作标准方法。<br>
这里使用的ConcurrentDictionary 是一个并发字典。</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/IAngStorage.PNG" alt="" loading="lazy"></p>
<pre><code> public interface IAnyStorage&lt;T&gt;
      where T : class
    {
      ConcurrentDictionary&lt;string, T&gt; DataMap { get; }

      T GetByName(string name, string defaultName);


      void AddOrUpdate(string name, T val);


      void Remove(string name);


      void Clear();
    }
</code></pre>
<h2 id="2遵循规范">2.遵循规范</h2>
<p>我们先做SqlSugar的版本</p>
<h3 id="新建-easyloggersqlsugardbstorage类库">①、新建 EasyLogger.SqlSugarDbStorage类库</h3>
<h3 id="新建-interface-文件夹-1">②、新建 Interface 文件夹</h3>
<h3 id="新建-isqlsugarproviderstorage--sqlsugar连接提供程序存储器-接口继承ianystorage">③、新建 ISqlSugarProviderStorage(SqlSugar连接提供程序存储器) 接口继承IAnyStorage</h3>
<blockquote>
<p>继承IAnyStorage因为他是泛型继承它,我们需要传递一个参数,他是什么呢?当然是我们的SqlSugar连接提供程序了。</p>
</blockquote>
<p>我们安装NuGet包安装 sqlSugarCore</p>
<h3 id="新建isqlsugarprovider-sqlsugar连接提供程序-接口作为泛型参数传入">⑤、新建ISqlSugarProvider (SqlSugar连接提供程序) 接口作为泛型参数传入</h3>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/13151911.png" alt="" loading="lazy"></p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/3151931.png" alt="" loading="lazy"></p>
<h3 id="新建impl-文件夹">⑥、新建Impl 文件夹</h3>
<h3 id="新建-defaultsqlsugarproviderstorage类继承-isqlsugarproviderstorage-进行sqlsugar连接提供程序存储器的具体实现">⑦、新建 DefaultSqlSugarProviderStorage类继承 ISqlSugarProviderStorage 进行SqlSugar连接提供程序存储器的具体实现</h3>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/813152539.png" alt="" loading="lazy"></p>
<pre><code>public class DefaultSqlSugarProviderStorage : ISqlSugarProviderStorage
    {
      public ConcurrentDictionary&lt;string, ISqlSugarProvider&gt; DataMap { get; private set; }

      public DefaultSqlSugarProviderStorage(IServiceProvider serviceProvider)
      {
            DataMap = new ConcurrentDictionary&lt;string, ISqlSugarProvider&gt;();

            var tmpDataMap = serviceProvider.GetServices&lt;ISqlSugarProvider&gt;()
                .ToDictionary(item =&gt; item.ProviderName);

            foreach (var item in tmpDataMap)
            {
                this.AddOrUpdate(item.Key, item.Value);
            }
      }
      public void AddOrUpdate(string name, ISqlSugarProvider val)
      {
            DataMap = val;
      }

      public void Clear()
      {
            DataMap.Clear();
      }

      public ISqlSugarProvider GetByName(string name, string defaultName)
      {
            ISqlSugarProvider result = null;

            if (name == null)
            {
                if (!DataMap.TryGetValue(defaultName, out result))
                {
                  throw new Exception("没有找到 DefaultName Provider");
                }
                return result;
            }
            else if (DataMap.TryGetValue(name, out result))
            {
                return result;
            }

            throw new ArgumentException($"没有找到{name}Provider");
      }

      public void Remove(string name)
      {
            if (string.IsNullOrWhiteSpace(name))
            {
                return;
            }

            this.DataMap.TryRemove(name, out ISqlSugarProvider result);
      }
    }
</code></pre>
<p>字典的操作我想都能看明白,基础差的朋友可能会觉得不懂的就是下面这句,这是做什么呢,其实就是我们把注入ISqlSugarProvider的实现都查询<br>
出来,放到我们的DateMap集合中(好处后面我会实践给大家讲到),到此我们SqlSugar连接提供程序存储库就完成了。</p>
<pre><code>   var tmpDataMap = serviceProvider.GetServices&lt;ISqlSugarProvider&gt;()
                .ToDictionary(item =&gt; item.ProviderName);

            foreach (var item in tmpDataMap)
            {
                this.AddOrUpdate(item.Key, item.Value);
            }
</code></pre>
<p>差点SqlSugar连接提供程序的实现给漏了。<br>
我们在 Impl 文件夹 新建 SqlSugarProvider类,继承ISqlSugarProvider接口。</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/13161107.png" alt="" loading="lazy"></p>
<pre><code>public class SqlSugarProvider : ISqlSugarProvider
    {
      public string ProviderName { get; set; }
      public SqlSugarClient Sugar { get; set; }

      public SqlSugarProvider()
      {
            this.Sugar = this.CreateSqlSugar();
            this.ProviderName = "DefaultSqlSugar";
      }

      private SqlSugarClient CreateSqlSugar()
      {
            // todo 临时
            var db = new SqlSugarClient(
             new ConnectionConfig()
             {
               ConnectionString = "server=.;uid=sa;pwd=@jhl85661501;database=SqlSugar4XTest",
               DbType = DbType.SqlServer,//设置数据库类型
                IsAutoCloseConnection = true,//自动释放数据务,如果存在事务,在事务结束后释放
                InitKeyType = InitKeyType.Attribute //从实体特性中读取主键自增列信息
            });
            return db;
      }

      public void Dispose()
      {
            this.Sugar.Dispose();
      }
    }
</code></pre>
<p>CreateSqlSugar 方法是我从SqlSugar官方复制过来的,大家注意把ConnectionString改成自己的数据库连接。</p>
<p>我们现在来配置一下 Startup 先看看效果。</p>
<h1 id="感谢-damn-帮我发现漏掉说明的代码">感谢 @Damn 帮我发现漏掉说明的代码!</h1>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/11111.png" alt="" loading="lazy"></p>
<pre><code> public void ConfigureServices(IServiceCollection services)
      {
            // 注入
            services.AddSingleton&lt;ISqlSugarProvider, SqlSugarProvider&gt;();
            services.AddSingleton&lt;ISqlSugarProviderStorage, DefaultSqlSugarProviderStorage&gt;();
            services.AddControllers();
      }
</code></pre>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/3162050.png" alt="" loading="lazy"></p>
<pre><code>   app.Use(async (context, next) =&gt;
            {
                var sqlStorage = app.ApplicationServices.GetService&lt;ISqlSugarProviderStorage&gt;();
                var sugarClient = sqlStorage.GetByName(null, "DefaultSqlSugar").Sugar;
                Console.WriteLine("查看sugarClient");
            });
</code></pre>
<p>我们成功的从SqlSugar连接提供程序存储中,拿到SqlSugarClient连接。</p>
<h1 id="补充">补充</h1>
<p>下面代码是我在技术群里看到使用FreeSql的方式,这么写没有问题简单单例的实现,我推荐大家使用上文中依赖注入的方式来使用!</p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/o_20081405020500814125827.png" alt="" loading="lazy"></p>
<p><img src="https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/EasyLoggerImages/o_20081405021100814125834.png" alt="" loading="lazy"></p>
<h1 id="结尾">结尾</h1>
<p>回顾一下本节的内容。</p>
<h5 id="1我们新建统一的泛型-ianystorage接口连接提供程序存储库来规范调用">1.我们新建统一的泛型 IAnyStorage接口(连接提供程序存储库)来规范调用。</h5>
<h5 id="2我们创建了-isqlsugarproviderstorage-sqlsugar连接提供程序存储库-通过继承ianystorage来规范接口实现">2.我们创建了 ISqlSugarProviderStorage (SqlSugar连接提供程序存储库) 通过继承IAnyStorage来规范接口实现。</h5>
<h5 id="3新建了-isqlsugarprovider-sqlsugar连接提供程序">3.新建了 ISqlSugarProvider (SqlSugar连接提供程序).</h5>
<h5 id="4我们完善了接口的实现">4.我们完善了接口的实现。</h5>
<h5 id="5我们成功在-startup-中新建一个简单的中间件-成功从sqlsugar连接提供程序存储中拿到sqlsugarclient连接">5.我们成功在 Startup 中新建一个简单的中间件 成功从SqlSugar连接提供程序存储中,拿到SqlSugarClient连接。</h5>
<h1 id="思考问题">思考问题</h1>
<p>我们把数据库连接字符串写在(SqlSugar连接提供程序)合理吗,是不是依赖太深,这个时候我们就用到控制反转来降低依赖性,那么具体怎么做呢!</p><br><br>
来源:https://www.cnblogs.com/MrChuJiu/p/13517207.html
頁: [1]
查看完整版本: 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统二 | 简单的分库分表设计