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


    教程

    01 | 模块化方案一

    02 | 模块化方案二

    其他教程预览

    分库分表项目实战教程

    Git地址: https://github.com/MrChuJiu/EasyLogger

    01 | 前言

    02 | 简单的分库分表设计

    03 | 控制反转搭配简单业务

    04 | 强化设计方案

    05 | 完善业务自动创建数据库

    06 | 最终篇-通过AOP自动连接数据库-完成日志业务

    前言

    项目涉及到了一些设计模式,如果你看的不是很明白,没有关系坚持下来,写完之后去思考去品,你就会有一种突拨开云雾的感觉,所以请不要在半途感觉自己看不懂选择放弃,如果我哪里写的详细,或者需要修正请联系我,谢谢。

    创建项目

    1.SDK安装

    我们开发用的vs版本是2019 .Net Core的版本是3.1
    下载 SDK 地址 :https://dotnet.microsoft.com/download

    2.新建项目

    这里选择Core 版本是3.1 项目类型是API
    Docker支持我们不勾选,我会在后续给大家单独再开一个系列 我们专讲,慢慢来。

    这里可以看到一个非常干净的项目就创建出来了,项目结构就是这样,里面的详情可以去看老张的第二个章节,讲的很明白,我就不在重复了
    https://www.cnblogs.com/laozhang-is-phi/p/9495620.html
    进阶可以去看开源源码 https://github.com/aspnet/MetaPackages/tree/master/src/Microsoft.AspNetCore
    然后我们直接F5 启动项目看看 是不是新建的项目是不是没有问题,项目一切正常,我们开始进入正轨。

    项目思考

    1.分表

    分表这个功能,就是把相同结构不同名称的多张表数据读取出来,这个部分其实很简单,我们常见的ORM都支持切换表名进行查询。

    2.分库

    分库如何实现呢。
    先来看一下我们常用的ORM框架是如何连接数据库的。

    SqlSugar连接数据库

    FreeSql连接数据库

    它们有一个共同点,NEW一个对象传递数据库连接字符串,设置好数据库类型,各自的xxx配置,就会得到一个连接的Client,然后就可以进行CRUD了。

    那New多个对象,每个对象都是不同的连接字符串岂不是就可以操作多个数据库了。

    大家写代码可别直接在业务/数据访问层这么写,整不好就让你下班领盒饭了。

    那么这种情境下因该如何设计能让代码更加规范、易扩展呢!

    3.思考

    思路就是这样,那么如果让你来设计,你会怎么做呢,大家可以自己尝试着先去根据自己想法设计看看,然后我们下一节,我来给讲解我的设计方案。

    设计要求

    为了提高难度设计难度我们来同时兼容FreeSql、SqlSugar2款现在最热门的ORM。
    我们设计要做到:

    易扩展(就算再来一个我也能轻松支持)

    切换快(不改动业务代码前提下,2个ORM框架我想用谁就用谁,随便切换)

    可共存(可以取2款ORM各自优点在一起开发使用)

    实战

    1.制定规范

    ①、我们先新建一个类库 EasyLogger.DbStorage(ps:该类库用于制定规范,提供接口)

    ②、新建 Interface 文件夹

    ③、新建泛型接口 IAnyStorage (存储器)

    该接口规范字典操作标准方法。
    这里使用的ConcurrentDictionary 是一个并发字典。

     public interface IAnyStorage<T>
            where T : class
        {
            ConcurrentDictionary<string, T> DataMap { get; }
    
            T GetByName(string name, string defaultName);
    
    
            void AddOrUpdate(string name, T val);
    
    
            void Remove(string name);
    
    
            void Clear();
        }
    

    2.遵循规范

    我们先做SqlSugar的版本

    ①、新建 EasyLogger.SqlSugarDbStorage类库

    ②、新建 Interface 文件夹

    ③、新建 ISqlSugarProviderStorage (SqlSugar连接提供程序存储器) 接口继承IAnyStorage

    继承IAnyStorage因为他是泛型继承它,我们需要传递一个参数,他是什么呢?当然是我们的SqlSugar连接提供程序了。

    我们安装NuGet包安装 sqlSugarCore

    ⑤、新建ISqlSugarProvider (SqlSugar连接提供程序) 接口作为泛型参数传入

    ⑥、新建Impl 文件夹

    ⑦、新建 DefaultSqlSugarProviderStorage类继承 ISqlSugarProviderStorage 进行SqlSugar连接提供程序存储器的具体实现

    public class DefaultSqlSugarProviderStorage : ISqlSugarProviderStorage
        {
            public ConcurrentDictionary<string, ISqlSugarProvider> DataMap { get; private set; }
    
            public DefaultSqlSugarProviderStorage(IServiceProvider serviceProvider)
            {
                DataMap = new ConcurrentDictionary<string, ISqlSugarProvider>();
    
                var tmpDataMap = serviceProvider.GetServices<ISqlSugarProvider>()
                    .ToDictionary(item => item.ProviderName);
    
                foreach (var item in tmpDataMap)
                {
                    this.AddOrUpdate(item.Key, item.Value);
                }
            }
            public void AddOrUpdate(string name, ISqlSugarProvider val)
            {
                DataMap[name] = 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);
            }
        }
    

    字典的操作我想都能看明白,基础差的朋友可能会觉得不懂的就是下面这句,这是做什么呢,其实就是我们把注入ISqlSugarProvider的实现都查询
    出来,放到我们的DateMap集合中(好处后面我会实践给大家讲到),到此我们SqlSugar连接提供程序存储库就完成了。

         var tmpDataMap = serviceProvider.GetServices<ISqlSugarProvider>()
                    .ToDictionary(item => item.ProviderName);
    
                foreach (var item in tmpDataMap)
                {
                    this.AddOrUpdate(item.Key, item.Value);
                }
    

    差点SqlSugar连接提供程序的实现给漏了。
    我们在 Impl 文件夹 新建 SqlSugarProvider类,继承ISqlSugarProvider接口。

    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();
            }
        }
    

    CreateSqlSugar 方法是我从SqlSugar官方复制过来的,大家注意把ConnectionString改成自己的数据库连接。

    我们现在来配置一下 Startup 先看看效果。

    感谢 @Damn 帮我发现漏掉说明的代码!

     public void ConfigureServices(IServiceCollection services)
            {
                // 注入
                services.AddSingleton<ISqlSugarProvider, SqlSugarProvider>();
                services.AddSingleton<ISqlSugarProviderStorage, DefaultSqlSugarProviderStorage>();
                services.AddControllers();
            }
    

       app.Use(async (context, next) =>
                {
                    var sqlStorage = app.ApplicationServices.GetService<ISqlSugarProviderStorage>();
                    var sugarClient = sqlStorage.GetByName(null, "DefaultSqlSugar").Sugar;
                    Console.WriteLine("查看sugarClient");
                });
    

    我们成功的从SqlSugar连接提供程序存储中,拿到SqlSugarClient连接。

    补充

    下面代码是我在技术群里看到使用FreeSql的方式,这么写没有问题简单单例的实现,我推荐大家使用上文中依赖注入的方式来使用!

    结尾

    回顾一下本节的内容。

    1.我们新建统一的泛型 IAnyStorage接口(连接提供程序存储库)来规范调用。
    2.我们创建了 ISqlSugarProviderStorage (SqlSugar连接提供程序存储库) 通过继承IAnyStorage来规范接口实现。
    3.新建了 ISqlSugarProvider (SqlSugar连接提供程序).
    4.我们完善了接口的实现。
    5.我们成功在 Startup 中新建一个简单的中间件 成功从SqlSugar连接提供程序存储中,拿到SqlSugarClient连接。

    思考问题

    我们把数据库连接字符串写在(SqlSugar连接提供程序)合理吗,是不是依赖太深,这个时候我们就用到控制反转来降低依赖性,那么具体怎么做呢!

  • 相关阅读:
    WPF在XAML的资源中定义空字符串String.Empty
    WPF中定义TabItem的可选区域(特别是当使用Label来呈现Header时)
    反转ListBox的ListBoxItem(控件级别,不是数据的反转)
    WPF获取相对位置、坐标的方法
    WPF为ItemsControl设置ItemsPanelTemplate
    WPF中List的Add()与Insert()方法的区别
    返回表达式列表中最小值least(exp1,exp2,exp3,……,expn)
    oracle_基本SQL语言
    JS 实现的浏览器系统通知 iNotify.js
    RHCE认证考前辅导
  • 原文地址:https://www.cnblogs.com/MrChuJiu/p/13517207.html
Copyright © 2020-2023  润新知