From 5c0d3e648396a5ac4f66d29846e4bf357b8ce05e Mon Sep 17 00:00:00 2001 From: xiaochanghai Date: Thu, 18 Apr 2024 09:59:25 +0800 Subject: [PATCH] =?UTF-8?q?Dapper=E4=BB=A3=E7=A0=81=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E5=88=B0=E9=A1=B9=E7=9B=AE=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/SimpleDapper.dll | Bin 101376 -> 0 bytes Model/Tiobon.Web.pdm | 6 +- Tiobon.Core.Api/Program.cs | 2 +- Tiobon.Core.Api/Tiobon.Core.Api.csproj | 7 - Tiobon.Core.Api/Tiobon.Core.xml | 77 ++ .../BaseProvider/ServerMapPath/IDependency.cs | 6 + .../ServerMapPath/PathProvider.cs | 50 ++ .../DB/Dapper/Configuration/AppSetting.cs | 164 +++++ .../DB/Dapper/Const/DataBaseType.cs | 6 + .../DB/Dapper/Const/SqlDbTypeName.cs | 23 + .../DB/Dapper/DBManager/BaseDBConfig.cs | 151 ++++ .../Dapper/DBManager/DBConnectionAttribute.cs | 8 + .../DB/Dapper/DBManager/DBServerProvider.cs | 138 ++++ .../DB/Dapper/Entity/EntityAttribute.cs | 38 + .../DB/Dapper/Enums/DbCurrentType.cs | 13 + .../AutofacManager/AutofacContainerModule.cs | 9 + .../DB/Dapper/Extensions/EntityProperties.cs | 620 ++++++++++++++++ .../DB/Dapper/Extensions/GenericExtension.cs | 61 ++ .../DB/Dapper/Extensions/LambdaExtensions.cs | 67 ++ .../Dapper/Extensions/MethodInfoExtensions.cs | 21 + .../DB/Dapper/Extensions/ServerExtension.cs | 31 + .../ServiceProviderManagerExtension.cs | 18 + .../DB/Dapper/Extensions/SimpleDapperSetup.cs | 14 + .../DB/Dapper/Extensions/StringExtension.cs | 117 +++ Tiobon.Core.Common/DB/Dapper/ISqlDapper.cs | 121 ++++ Tiobon.Core.Common/DB/Dapper/ITransDapper.cs | 105 +++ .../DB/Dapper/SimpleDapper.csproj | 29 + Tiobon.Core.Common/DB/Dapper/SqlDapper.cs | 665 ++++++++++++++++++ Tiobon.Core.Common/DB/Dapper/TransDapper.cs | 495 +++++++++++++ .../DB/Dapper/Utilities/DbAccess.cs | 153 ++++ .../DB/Dapper/Utilities/FileHelper.cs | 83 +++ .../DB/Dapper/Utilities/HttpContext.cs | 15 + .../DB/Dapper/Utilities/StringHelper.cs | 15 + .../DB/Dapper/Utilities/UtilConvert.cs | 33 + Tiobon.Core.Common/DB/DbSql/DbInsert.cs | 9 +- Tiobon.Core.Common/Seed/FrameSeed.cs | 2 +- Tiobon.Core.Common/Tiobon.Core.Common.csproj | 11 +- .../Common_Test/DbAccess_Should.cs | 2 +- Tiobon.Core.Tests/Tiobon.Core.Tests.csproj | 6 - 39 files changed, 3365 insertions(+), 26 deletions(-) delete mode 100644 Lib/SimpleDapper.dll create mode 100644 Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/IDependency.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/PathProvider.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Configuration/AppSetting.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Const/DataBaseType.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Const/SqlDbTypeName.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/DBManager/BaseDBConfig.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/DBManager/DBConnectionAttribute.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/DBManager/DBServerProvider.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Entity/EntityAttribute.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Enums/DbCurrentType.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/AutofacManager/AutofacContainerModule.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/EntityProperties.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/GenericExtension.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/LambdaExtensions.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/MethodInfoExtensions.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/ServerExtension.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/ServiceProviderManagerExtension.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/SimpleDapperSetup.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Extensions/StringExtension.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/ISqlDapper.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/ITransDapper.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/SimpleDapper.csproj create mode 100644 Tiobon.Core.Common/DB/Dapper/SqlDapper.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/TransDapper.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Utilities/DbAccess.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Utilities/FileHelper.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Utilities/HttpContext.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Utilities/StringHelper.cs create mode 100644 Tiobon.Core.Common/DB/Dapper/Utilities/UtilConvert.cs diff --git a/Lib/SimpleDapper.dll b/Lib/SimpleDapper.dll deleted file mode 100644 index 70f4aa69155bae232ff073b3f255e8e93cb7ed22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101376 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 - + @@ -105116,8 +105116,8 @@ Shadow=0 1630077158 -1713342688 -((-18861,2845), (-12775,6445)) +1713343323 +((-6725,2913), (-639,6513)) 0 16711680 16744448 diff --git a/Tiobon.Core.Api/Program.cs b/Tiobon.Core.Api/Program.cs index aa09e857..516b90a4 100644 --- a/Tiobon.Core.Api/Program.cs +++ b/Tiobon.Core.Api/Program.cs @@ -9,7 +9,7 @@ using Microsoft.IdentityModel.Logging; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using Serilog; -using SimpleDapper.Extensions; +using Tiobon.Core.Common.DB.Dapper.Extensions; using Tiobon.Core; using Tiobon.Core.Common.Core; using Tiobon.Core.Extensions; diff --git a/Tiobon.Core.Api/Tiobon.Core.Api.csproj b/Tiobon.Core.Api/Tiobon.Core.Api.csproj index 76503fb3..3a83b4f5 100644 --- a/Tiobon.Core.Api/Tiobon.Core.Api.csproj +++ b/Tiobon.Core.Api/Tiobon.Core.Api.csproj @@ -53,7 +53,6 @@ - @@ -140,12 +139,6 @@ - - - ..\Lib\SimpleDapper.dll - - - diff --git a/Tiobon.Core.Api/Tiobon.Core.xml b/Tiobon.Core.Api/Tiobon.Core.xml index c5a39a08..1af72fab 100644 --- a/Tiobon.Core.Api/Tiobon.Core.xml +++ b/Tiobon.Core.Api/Tiobon.Core.xml @@ -72,6 +72,83 @@ + + + 博客管理 + + + + + 构造函数 + + + + + + + 获取博客列表【无权限】 + + + + + + + + + + 获取博客详情 + + + + + + + 获取详情【无权限】 + + + + + + + 获取博客测试信息 v2版本 + + + + + + 添加博客【无权限】 + + + + + + + + + + + + + + 更新博客信息 + + + + + + + 删除博客 + + + + + + + apache jemeter 压力测试 + 更新接口 + + + 构造函数 diff --git a/Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/IDependency.cs b/Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/IDependency.cs new file mode 100644 index 00000000..fb05bf3d --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/IDependency.cs @@ -0,0 +1,6 @@ +namespace Tiobon.Core.Common.DB.Dapper.BaseProvider.ServerMapPath +{ + public interface IDependency + { + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/PathProvider.cs b/Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/PathProvider.cs new file mode 100644 index 00000000..a81ce8e1 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/BaseProvider/ServerMapPath/PathProvider.cs @@ -0,0 +1,50 @@ + +using Microsoft.AspNetCore.Hosting; +using System.IO; +using Tiobon.Core.Common.DB.Dapper.Extensions; + +namespace Tiobon.Core.Common.DB.Dapper.BaseProvider.ServerMapPath; + +public interface IPathProvider : IDependency +{ + string MapPath(string path); + string MapPath(string path, bool rootPath); + IWebHostEnvironment GetHostingEnvironment(); +} + +public class PathProvider : IPathProvider +{ + private IWebHostEnvironment _hostingEnvironment; + + public PathProvider(IWebHostEnvironment environment) + { + _hostingEnvironment = environment; + } + public IWebHostEnvironment GetHostingEnvironment() + { + return _hostingEnvironment; + } + + public string MapPath(string path) + { + return MapPath(path, false); + } + /// + /// + /// + /// + /// 获取wwwroot路径 + /// + public string MapPath(string path, bool rootPath) + { + if (rootPath) + { + if (_hostingEnvironment.WebRootPath == null) + { + _hostingEnvironment.WebRootPath = _hostingEnvironment.ContentRootPath + "/wwwroot".ReplacePath(); + } + return Path.Combine(_hostingEnvironment.WebRootPath, path).ReplacePath(); + } + return Path.Combine(_hostingEnvironment.ContentRootPath, path).ReplacePath(); + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Configuration/AppSetting.cs b/Tiobon.Core.Common/DB/Dapper/Configuration/AppSetting.cs new file mode 100644 index 00000000..72c66df5 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Configuration/AppSetting.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.DependencyInjection; +using Tiobon.Core.Common.DB.Dapper.DBManager; +using Tiobon.Core.Common.DB.Dapper.Enums; + +namespace Tiobon.Core.Common.DB.Dapper; + +public static class AppSetting +{ + public static IConfiguration Configuration { get; private set; } + + public static string DbConnectionString { get; private set; } + public static string DBType { get; private set; } + + + private static ConnectionStrings _connection; + + + public static void Init() + { + Configuration = new ConfigurationBuilder() + .Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true }) + .Build(); ; + + //var baseDirectory = AppContext.BaseDirectory; + //var provider = services.BuildServiceProvider(); + + + ////设置修改或删除时需要设置为默认用户信息的字段 + //CreateMember = provider.GetRequiredService>().Value ?? new CreateMember(); + //ModifyMember = provider.GetRequiredService>().Value ?? new ModifyMember(); + + #region 初始化数据库 + MainDb.CurrentDbConnId = app("MainDB"); + + var mainConnetctDb = BaseDBConfig.MutiConnectionString.allDbs.Find(x => x.ConnId == MainDb.CurrentDbConnId); + + Const.DBType.Name = DataBaseType.SqlServer.ToString(); + DBType = DataBaseType.SqlServer.ToString(); + + try + { + DbConnectionString = mainConnetctDb.Connection; + } + catch { } + if (string.IsNullOrEmpty(DbConnectionString)) + throw new Exception("未配置好数据库默认连接"); + + #region 检查服务是否可用 + + //bool mysql = Const.DBType.Name == DbCurrentType.MySql.ToString(); + //if (mysql) + //{ + + //} + bool mssql = Const.DBType.Name == DbCurrentType.MsSql.ToString(); + + if (mssql) + { + using var conn = new SqlConnection(_connection.DbConnectionString); + while (true) + { + + //if (Utility.IsPortOpen(conn.DataSource, 1433)) + //{ + // Logger.WriteLog("[数据库] 服务状态正常"); + // break; + //} + //else + //{ + // Logger.WriteLog("[数据库] 服务状态异常, 稍后重试"); + // System.Threading.Thread.Sleep(5000); + //} + + //conn.Open(); + //if (conn.State == ConnectionState.Open) + //{ + // conn.Close(); + // Logger.WriteLog("[数据库] 服务状态正常"); + // break; + //} + //else + //{ + // Logger.WriteLog("[数据库] 服务状态异常, 等待 5 秒后重试"); + // System.Threading.Thread.Sleep(5000); + //} + } + } + #endregion + + #endregion + + + } + // 多个节点name格式 :["key:key1"] + public static string GetSettingString(string key) + { + return Configuration[key]; + } + // 多个节点,通过.GetSection("key")["key1"]获取 + public static IConfigurationSection GetSection(string key) + { + return Configuration.GetSection(key); + } + + /// + /// 封装要操作的字符 + /// + /// 节点配置 + /// + public static string app(params string[] sections) + { + try + { + + if (sections.Any()) + { + return Configuration[string.Join(":", sections)]; + } + } + catch (Exception) { } + + return ""; + } + + /// + /// 递归获取配置信息数组 + /// + /// + /// + /// + public static List app(params string[] sections) + { + List list = new List(); + // 引用 Microsoft.Extensions.Configuration.Binder 包 + Configuration.Bind(string.Join(":", sections), list); + return list; + } +} + + +#region 数据库链接 +/// +/// 数据库链接 +/// +public class ConnectionStrings +{ + /// + /// 数据库类型 + /// + public string DBType { get; set; } + + /// + /// 数据库链接字符串 + /// + public string DbConnectionString { get; set; } +} +#endregion + diff --git a/Tiobon.Core.Common/DB/Dapper/Const/DataBaseType.cs b/Tiobon.Core.Common/DB/Dapper/Const/DataBaseType.cs new file mode 100644 index 00000000..ff974cde --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Const/DataBaseType.cs @@ -0,0 +1,6 @@ +namespace Tiobon.Core.Common.DB.Dapper.Const; + +public static class DBType +{ + public static string Name { get; set; } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Const/SqlDbTypeName.cs b/Tiobon.Core.Common/DB/Dapper/Const/SqlDbTypeName.cs new file mode 100644 index 00000000..29917734 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Const/SqlDbTypeName.cs @@ -0,0 +1,23 @@ +namespace Tiobon.Core.Common.DB.Dapper; + +public struct SqlDbTypeName +{ + public const string NVarChar = "nvarchar"; + public const string VarChar = "varchar"; + public const string NChar = "nchar"; + public const string Char = "char"; + public const string Text = "text"; + public const string Int = "int"; + public const string BigInt = "bigint"; + public const string DateTime = "datetime"; + public const string Date = "date"; + public const string SmallDateTime = "smalldatetime"; + public const string SmallDate = "smalldate"; + public const string Float = "float"; + public const string Decimal = "decimal"; + public const string Double = "double"; + public const string Bit = "bit"; + public const string Bool = "bool"; + public const string UniqueIdentifier = "uniqueidentifier"; + +} diff --git a/Tiobon.Core.Common/DB/Dapper/DBManager/BaseDBConfig.cs b/Tiobon.Core.Common/DB/Dapper/DBManager/BaseDBConfig.cs new file mode 100644 index 00000000..b5c17303 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/DBManager/BaseDBConfig.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SqlSugar; + +namespace Tiobon.Core.Common.DB.Dapper.DBManager; +public class BaseDBConfig +{ + /* 之前的单库操作已经删除,如果想要之前的代码,可以查看我的GitHub的历史记录 + * 目前是多库操作,默认加载的是AppSetting.json设置为true的第一个db连接。 + */ + public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn(); + + private static string DifDBConnOfSecurity(params string[] conn) + { + foreach (var item in conn) + { + try + { + if (File.Exists(item)) + { + return File.ReadAllText(item).Trim(); + } + } + catch (System.Exception) + { + } + } + + return conn[conn.Length - 1]; + } + + + public static (List, List) MutiInitConn() + { + List listdatabase = AppSetting.app("DBS") + .Where(i => i.Enabled).ToList(); + foreach (var i in listdatabase) + { + SpecialDbString(i); + } + + List listdatabaseSimpleDB = new List(); //单库 + List listdatabaseSlaveDB = new List(); //从库 + + // 单库,且不开启读写分离,只保留一个 + if (!AppSetting.app(new string[] { "CQRSEnabled" }).ObjToBool() && !AppSetting.app(new string[] { "MutiDBEnabled" }).ObjToBool()) + { + if (listdatabase.Count == 1) + { + return (listdatabase, listdatabaseSlaveDB); + } + else + { + var dbFirst = listdatabase.FirstOrDefault(d => d.ConnId == AppSetting.app(new string[] { "MainDB" }).ObjToString()); + if (dbFirst == null) + { + dbFirst = listdatabase.FirstOrDefault(); + } + + listdatabaseSimpleDB.Add(dbFirst); + return (listdatabaseSimpleDB, listdatabaseSlaveDB); + } + } + + + // 读写分离,且必须是单库模式,获取从库 + if (AppSetting.app(new string[] { "CQRSEnabled" }).ObjToBool() && !AppSetting.app(new string[] { "MutiDBEnabled" }).ObjToBool()) + { + if (listdatabase.Count > 1) + { + listdatabaseSlaveDB = listdatabase.Where(d => d.ConnId != AppSetting.app(new string[] { "MainDB" }).ObjToString()).ToList(); + } + } + + + return (listdatabase, listdatabaseSlaveDB); + //} + } + + /// + /// 定制Db字符串 + /// 目的是保证安全:优先从本地txt文件获取,若没有文件则从AppSetting.json中获取 + /// + /// + /// + private static MutiDBOperate SpecialDbString(MutiDBOperate mutiDBOperate) + { + if (mutiDBOperate.DbType == DataBaseType.Sqlite) + { + mutiDBOperate.Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, mutiDBOperate.Connection); + } + else if (mutiDBOperate.DbType == DataBaseType.SqlServer) + { + mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_SqlserverConn.txt", mutiDBOperate.Connection); + } + else if (mutiDBOperate.DbType == DataBaseType.MySql) + { + mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_MySqlConn.txt", mutiDBOperate.Connection); + } + else if (mutiDBOperate.DbType == DataBaseType.Oracle) + { + mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_OracleConn.txt", mutiDBOperate.Connection); + } + + return mutiDBOperate; + } + + +} + + +public enum DataBaseType +{ + MySql = 0, + SqlServer = 1, + Sqlite = 2, + Oracle = 3, + PostgreSQL = 4, + Dm = 5, + Kdbndp = 6, +} + +public class MutiDBOperate +{ + /// + /// 连接启用开关 + /// + public bool Enabled { get; set; } + + /// + /// 连接ID + /// + public string ConnId { get; set; } + + /// + /// 从库执行级别,越大越先执行 + /// + public int HitRate { get; set; } + + /// + /// 连接字符串 + /// + public string Connection { get; set; } + + /// + /// 数据库类型 + /// + public DataBaseType DbType { get; set; } +} \ No newline at end of file diff --git a/Tiobon.Core.Common/DB/Dapper/DBManager/DBConnectionAttribute.cs b/Tiobon.Core.Common/DB/Dapper/DBManager/DBConnectionAttribute.cs new file mode 100644 index 00000000..d011c0ef --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/DBManager/DBConnectionAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Tiobon.Core.Common.DB.Dapper.DBManager; + +public class DBConnectionAttribute : Attribute +{ + public string DBName { get; set; } +} diff --git a/Tiobon.Core.Common/DB/Dapper/DBManager/DBServerProvider.cs b/Tiobon.Core.Common/DB/Dapper/DBManager/DBServerProvider.cs new file mode 100644 index 00000000..87afbaf3 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/DBManager/DBServerProvider.cs @@ -0,0 +1,138 @@ +//using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using Tiobon.Core.Common.DB.Dapper.Const; +using Tiobon.Core.Common.DB.Dapper; +using Tiobon.Core.Common.DB.Dapper.Enums; +using Tiobon.Core.Common.DB.Dapper.Extensions; + +namespace Tiobon.Core.Common.DB.Dapper.DBManager; +public class DBServerProvider +{ + private static Dictionary ConnectionPool = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public static string DefaultConnName = "defalut"; + + static DBServerProvider() + { + SetConnection(DefaultConnName, AppSetting.DbConnectionString); + } + public static void SetConnection(string key, string val) + { + if (ConnectionPool.ContainsKey(key)) + { + ConnectionPool[key] = val; + return; + } + ConnectionPool.Add(key, val); + } + /// + /// 设置默认数据库连接 + /// + /// + public static void SetDefaultConnection(string val) + { + SetConnection(DefaultConnName, val); + } + + public static string GetConnectionString(string key) + { + key = key ?? DefaultConnName; + if (ConnectionPool.ContainsKey(key)) + { + return ConnectionPool[key]; + } + return key; + } + /// + /// 获取默认数据库连接 + /// + /// + public static string GetConnectionString() + { + return GetConnectionString(DefaultConnName); + } + //private static bool _isMysql = DBType.Name == DbCurrentType.MySql.ToString(); + private static bool _isMysql = false; + public static IDbConnection GetDbConnection(string connString = null) + { + if (_isMysql) + { + return new MySql.Data.MySqlClient.MySqlConnection(connString ?? ConnectionPool[DefaultConnName]); + } + return new SqlConnection(connString ?? ConnectionPool[DefaultConnName]); + } + public static IDbConnection GetMyDbConnection(string connString = null) + { + //new MySql.Data.MySqlClient.MySqlConnection(connString); + string mySql = "Data Source=132.232.2.109;Database=mysql;User ID=xx;Password=xxx;pooling=true;CharSet=utf8;port=3306;sslmode=none"; + // MySqlConnector + return new MySql.Data.MySqlClient.MySqlConnection(mySql); + + } + //public static VOLContext DbContext + //{ + // get { return GetEFDbContext(); } + //} + //public static VOLContext GetEFDbContext() + //{ + // return GetEFDbContext(null); + //} + //public static VOLContext GetEFDbContext(string dbName) + //{ + // VOLContext beefContext = Utilities.HttpContext.Current.RequestServices.GetService(typeof(VOLContext)) as VOLContext; + // if (dbName != null) + // { + // if (!ConnectionPool.ContainsKey(dbName)) + // { + // throw new Exception("数据库连接名称错误"); + // } + // beefContext.Database.GetDbConnection().ConnectionString = ConnectionPool[dbName]; + // } + // return beefContext; + //} + + //public static void SetDbContextConnection(VOLContext beefContext, string dbName) + //{ + // if (!ConnectionPool.ContainsKey(dbName)) + // { + // throw new Exception("数据库连接名称错误"); + // } + // beefContext.Database.GetDbConnection().ConnectionString = ConnectionPool[dbName]; + //} + /// + /// 获取实体的数据库连接 + /// + /// + /// + /// + //public static void GetDbContextConnection(VOLContext defaultDbContext) + //{ + // //string connstr= defaultDbContext.Database.GetDbConnection().ConnectionString; + // // if (connstr != ConnectionPool[DefaultConnName]) + // // { + // // defaultDbContext.Database.GetDbConnection().ConnectionString = ConnectionPool[DefaultConnName]; + // // }; + //} + + public static ISqlDapper SqlDapper + { + get + { + return new SqlDapper(DefaultConnName); + } + } + public static ISqlDapper GetSqlDapper(string dbName = null) + { + return new SqlDapper(dbName ?? DefaultConnName); + } + public static ISqlDapper GetSqlDapper() + { + //获取实体真实的数据库连接池对象名,如果不存在则用默认数据连接池名 + string dbName = typeof(TEntity).GetTypeCustomValue(x => x.DBName) ?? DefaultConnName; + return GetSqlDapper(dbName); + } + +} diff --git a/Tiobon.Core.Common/DB/Dapper/Entity/EntityAttribute.cs b/Tiobon.Core.Common/DB/Dapper/Entity/EntityAttribute.cs new file mode 100644 index 00000000..af753017 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Entity/EntityAttribute.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tiobon.Core.Common.DB.Dapper.Entity +{ + public class EntityAttribute : Attribute + { + /// + /// 真实表名(数据库表名,若没有填写默认实体为表名) + /// + public string TableName { get; set; } + /// + /// 表中文名 + /// + public string TableCnName { get; set; } + /// + /// 子表 + /// + public Type[] DetailTable { get; set; } + /// + /// 子表中文名 + /// + public string DetailTableCnName { get; set; } + /// + /// 数据库 + /// + public string DBServer { get; set; } + + //是否开启用户数据权限,true=用户只能操作自己(及下级角色)创建的数据,如:查询、删除、修改等操作 + public bool CurrentUserPermission { get; set; } + + public Type ApiInput { get; set; } + public Type ApiOutput { get; set; } + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Enums/DbCurrentType.cs b/Tiobon.Core.Common/DB/Dapper/Enums/DbCurrentType.cs new file mode 100644 index 00000000..2e85259d --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Enums/DbCurrentType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tiobon.Core.Common.DB.Dapper.Enums +{ + public enum DbCurrentType + { + MySql = 1, + MsSql = 2, + PgSql = 3 + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/AutofacManager/AutofacContainerModule.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/AutofacManager/AutofacContainerModule.cs new file mode 100644 index 00000000..067fdbf2 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/AutofacManager/AutofacContainerModule.cs @@ -0,0 +1,9 @@ +namespace Tiobon.Core.Common.DB.Dapper.Extensions.AutofacManager; + +public class AutofacContainerModule +{ + public static TService GetService() where TService:class + { + return typeof(TService).GetService() as TService; + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/EntityProperties.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/EntityProperties.cs new file mode 100644 index 00000000..a2ed785e --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/EntityProperties.cs @@ -0,0 +1,620 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using Tiobon.Core.Common.DB.Dapper.Entity; + + +namespace Tiobon.Core.Common.DB.Dapper.Extensions; + +public static class EntityProperties +{ + + /// + /// 获取对象里指定成员名称 + /// + /// + /// 格式 Expression> exp = x => new { x.字段1, x.字段2 };或x=>x.Name + /// + public static string[] GetExpressionProperty(this Expression> properties) + { + if (properties == null) + return new string[] { }; + if (properties.Body is NewExpression) + return ((NewExpression)properties.Body).Members.Select(x => x.Name).ToArray(); + if (properties.Body is MemberExpression) + return new string[] { ((MemberExpression)properties.Body).Member.Name }; + if (properties.Body is UnaryExpression) + return new string[] { ((properties.Body as UnaryExpression).Operand as MemberExpression).Member.Name }; + throw new Exception("未实现的表达式"); + } + + public static Dictionary GetColumType(this PropertyInfo[] properties, bool containsKey) + { + Dictionary dictionary = new Dictionary(); + foreach (PropertyInfo property in properties) + { + //if (!containsKey && property.IsKey()) + //{ + // continue; + //} + var keyVal = GetColumnType(property, true); + dictionary.Add(keyVal.Key, keyVal.Value); + } + return dictionary; + } + + private static readonly Dictionary entityMapDbColumnType = new Dictionary() { + {typeof(int),SqlDbTypeName.Int }, + {typeof(int?),SqlDbTypeName.Int }, + {typeof(long),SqlDbTypeName.BigInt }, + {typeof(long?),SqlDbTypeName.BigInt }, + {typeof(decimal),"decimal(18, 5)" }, + {typeof(decimal?),"decimal(18, 5)" }, + {typeof(double),"decimal(18, 5)" }, + {typeof(double?),"decimal(18, 5)" }, + {typeof(float),"decimal(18, 5)" }, + {typeof(float?),"decimal(18, 5)" }, + {typeof(Guid),"UniqueIdentifier" }, + {typeof(Guid?),"UniqueIdentifier" }, + {typeof(byte),"tinyint" }, + {typeof(byte?),"tinyint" }, + {typeof(string),"nvarchar" } + }; + /// + /// 返回属性的字段及数据库类型 + /// + /// + /// 是否包括后字段具体长度:nvarchar(100) + /// + public static KeyValuePair GetColumnType(this PropertyInfo property, bool lenght = false) + { + string colType = ""; + object objAtrr = property.GetTypeCustomAttributes(typeof(ColumnAttribute), out bool asType); + if (asType) + { + colType = ((ColumnAttribute)objAtrr).TypeName.ToLower(); + if (!string.IsNullOrEmpty(colType)) + { + //不需要具体长度直接返回 + if (!lenght) + { + return new KeyValuePair(property.Name, colType); + } + if (colType == "decimal" || colType == "double" || colType == "float") + { + objAtrr = property.GetTypeCustomAttributes(typeof(DisplayFormatAttribute), out asType); + colType += "(" + (asType ? ((DisplayFormatAttribute)objAtrr).DataFormatString : "18,5") + ")"; + + } + ///如果是string,根据 varchar或nvarchar判断最大长度 + if (property.PropertyType.ToString() == "System.String") + { + colType = colType.Split("(")[0]; + objAtrr = property.GetTypeCustomAttributes(typeof(MaxLengthAttribute), out asType); + if (asType) + { + int length = ((MaxLengthAttribute)objAtrr).Length; + colType += "(" + (length < 1 || length > (colType.StartsWith("n") ? 8000 : 4000) ? "max" : length.ToString()) + ")"; + } + else + { + colType += "(max)"; + } + } + return new KeyValuePair(property.Name, colType); + } + } + if (entityMapDbColumnType.TryGetValue(property.PropertyType, out string value)) + { + colType = value; + } + else + { + colType = SqlDbTypeName.NVarChar; + } + if (lenght && colType == SqlDbTypeName.NVarChar) + { + colType = "nvarchar(max)"; + } + return new KeyValuePair(property.Name, colType); + } + + /// + ///要执行的sql语句如:通过EntityToSqlTempName.Temp_Insert0.ToString()字符串占位,生成的的sql语句会把EntityToSqlTempName.Temp_Insert0.ToString()替换成生成的sql临时表数据 + /// string sql = " ;DELETE FROM " + typeEntity.Name + " where " + typeEntity.GetKeyName() + + /// " in (select * from " + EntityToSqlTempName.Temp_Insert0.ToString() + ")"; + /// + /// + /// + /// 指定生成的数组值的类型 + /// + /// + public static string GetArraySql(this object[] array, FieldType fieldType, string sql) + { + if (array == null || array.Count() == 0) + { + return string.Empty; + } + string columnType = string.Empty; + List arrrayEntityList = array.Select(x => new ArrayEntity { column1 = x.ToString() }).ToList(); + return arrrayEntityList.GetEntitySql(false, sql, null, null, fieldType); + } + /// + /// 根据实体获取key的类型,用于update或del操作 + /// + /// + /// + public static FieldType GetFieldType(this Type typeEntity) + { + FieldType fieldType; + string columnType = typeEntity.GetProperties().Where(x => x.Name == typeEntity.GetKeyName()).ToList()[0].GetColumnType(false).Value; + switch (columnType) + { + case SqlDbTypeName.Int: fieldType = FieldType.Int; break; + case SqlDbTypeName.BigInt: fieldType = FieldType.BigInt; break; + case SqlDbTypeName.VarChar: fieldType = FieldType.VarChar; break; + case SqlDbTypeName.UniqueIdentifier: fieldType = FieldType.UniqueIdentifier; break; + default: fieldType = FieldType.NvarChar; break; + } + return fieldType; + } + public static string GetEntitySql(this IEnumerable entityList, + bool containsKey = false, + string sql = null, + Expression> ignoreFileds = null, + Expression> fixedColumns = null, + FieldType? fieldType = null + ) + { + + if (entityList == null || entityList.Count() == 0) return ""; + PropertyInfo[] propertyInfo = typeof(T).GetProperties().ToArray(); + if (propertyInfo.Count() == 0) + { + propertyInfo = entityList.ToArray()[0].GetType().GetGenericProperties().ToArray(); + } + propertyInfo = propertyInfo.GetGenericProperties().ToArray(); + + string[] arr = null; + if (fixedColumns != null) + { + arr = fixedColumns.GetExpressionToArray(); + PropertyInfo keyProperty = typeof(T).GetKeyProperty(); + propertyInfo = propertyInfo.Where(x => (containsKey && x.Name == keyProperty.Name) || arr.Contains(x.Name)).ToArray(); + } + if (ignoreFileds != null) + { + arr = ignoreFileds.GetExpressionToArray(); + propertyInfo = propertyInfo.Where(x => !arr.Contains(x.Name)).ToArray(); + } + + Dictionary dictProperties = propertyInfo.GetColumType(containsKey); + if (fieldType != null) + { + string realType = fieldType.ToString(); + if ((int)fieldType == 0 || (int)fieldType == 1) + { + realType += "(max)"; + } + dictProperties = new Dictionary { { dictProperties.Select(x => x.Key).ToList()[0], realType } }; + } + if (dictProperties.Keys.Count * entityList.Count() > 50 * 3000) + { + throw new Exception("写入数据太多,请分开写入。"); + } + + string cols = string.Join(",", dictProperties.Select(c => "[" + c.Key + "]" + " " + c.Value)); + StringBuilder declareTable = new StringBuilder(); + + string tempTablbe = "#" + EntityToSqlTempName.TempInsert.ToString(); + + declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")"); + declareTable.Append("\r\n"); + + //参数总数量 + int parCount = (dictProperties.Count) * (entityList.Count()); + int takeCount = 0; + int maxParsCount = 2050; + if (parCount > maxParsCount) + { + //如果参数总数量超过2100,设置每次分批循环写入表的大小 + takeCount = maxParsCount / dictProperties.Count; + } + + int count = 0; + StringBuilder stringLeft = new StringBuilder(); + StringBuilder stringCenter = new StringBuilder(); + StringBuilder stringRight = new StringBuilder(); + + int index = 0; + foreach (T entity in entityList) + { + //每1000行需要分批写入(数据库限制每批至多写入1000行数据) + if (index == 0 || index >= 1000 || takeCount - index == 0) + { + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); stringCenter.Clear(); stringRight.Clear(); + } + + stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;"); + stringCenter.Append("N'"); + + index = 0; count = 0; + } + stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " "); + index++; + foreach (PropertyInfo property in propertyInfo) + { + //if (!containsKey && property.IsKey()) { continue; } + string par = "@v" + count; + stringLeft.Append(par + ","); + stringCenter.Append(par + " " + dictProperties[property.Name] + ","); + object val = property.GetValue(entity); + if (val == null) + { + stringRight.Append(par + "=NUll,"); + } + else + { + stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',"); + } + count++; + } + stringLeft.Remove(stringLeft.Length - 1, 1); + stringLeft.Append("),("); + } + + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); stringCenter.Clear(); stringRight.Clear(); + } + if (!string.IsNullOrEmpty(sql)) + { + sql = sql.Replace(EntityToSqlTempName.TempInsert.ToString(), tempTablbe); + declareTable.AppendLine(sql); + } + else + { + declareTable.AppendLine(" SELECT " + (string.Join(",", fixedColumns?.GetExpressionToArray() ?? new string[] { "*" })) + " FROM " + tempTablbe); + } + + + if (tempTablbe.Substring(0, 1) == "#") + { + declareTable.AppendLine("; drop table " + tempTablbe); + } + return declareTable.ToString(); + } + public static string GetKeyName(this Type typeinfo) + { + return typeinfo.GetProperties().GetKeyName(); + } + public static string GetKeyName(this PropertyInfo[] properties) + { + return properties.GetKeyName(false); + } + /// + /// 获取key列名 + /// + /// + /// true获取key对应类型,false返回对象Key的名称 + /// + public static string GetKeyName(this PropertyInfo[] properties, bool keyType) + { + string keyName = string.Empty; + foreach (PropertyInfo propertyInfo in properties) + { + if (!propertyInfo.IsKey()) + continue; + if (!keyType) + return propertyInfo.Name; + var attributes = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), false); + //如果没有ColumnAttribute的需要单独再验证,下面只验证有属性的 + if (attributes.Length > 0) + return ((ColumnAttribute)attributes[0]).TypeName.ToLower(); + else + return GetColumType(new PropertyInfo[] { propertyInfo }, true)[propertyInfo.Name]; + } + return keyName; + } + + /// + /// 获取主键字段 + /// + /// + /// + public static PropertyInfo GetKeyProperty(this Type entity) + { + return entity.GetProperties().GetKeyProperty(); + } + public static PropertyInfo GetKeyProperty(this PropertyInfo[] properties) + { + return properties.Where(c => c.IsKey()).FirstOrDefault(); + } + public static bool IsKey(this PropertyInfo propertyInfo) + { + object[] keyAttributes = propertyInfo.GetCustomAttributes(typeof(KeyAttribute), false); + if (keyAttributes.Length > 0) + return true; + return false; + } + + /// + /// 判断是否包含某个属性: + /// 如 [Editable(true)] + // public string MO { get; set; }包含Editable + /// + /// + /// + /// + public static bool ContainsCustomAttributes(this PropertyInfo propertyInfo, Type type) + { + propertyInfo.GetTypeCustomAttributes(type, out bool contains); + return contains; + } + + /// + /// 获取PropertyInfo指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this PropertyInfo propertyInfo, Type type, out bool asType) + { + object[] attributes = propertyInfo.GetCustomAttributes(type, false); + if (attributes.Length == 0) + { + asType = false; + return new string[0]; + } + asType = true; + return attributes[0]; + } + + /// + /// 验证数据库字段类型与值是否正确, + /// + /// propertyInfo为当字段,当前字段必须有ColumnAttribute属性, + /// 如字段:标识为数据库int类型[Column(TypeName="int")] public int Id { get; set; } + /// 如果是小数float或Decimal必须对propertyInfo字段加DisplayFormatAttribute属性 + /// + /// + /// IEnumerable<(bool, string, object)> bool成否校验成功,string校验失败信息,object,当前校验的值 + public static IEnumerable<(bool, string, object)> ValidationValueForDbType(this PropertyInfo propertyInfo, params object[] values) + { + string dbTypeName = propertyInfo.GetTypeCustomValue(c => c.TypeName); + foreach (object value in values) + { + yield return dbTypeName.ValidationVal(value, propertyInfo); + } + } + + + private static readonly Dictionary ProperWithDbType = new Dictionary() { + { typeof(string),SqlDbTypeName.NVarChar }, + { typeof(DateTime),SqlDbTypeName.DateTime}, + {typeof(long),SqlDbTypeName.BigInt }, + {typeof(int),SqlDbTypeName.Int}, + { typeof(decimal),SqlDbTypeName.Decimal }, + { typeof(float),SqlDbTypeName.Float }, + { typeof(double),SqlDbTypeName.Double }, + { typeof(byte),SqlDbTypeName.Int },//类型待完 + { typeof(Guid),SqlDbTypeName.UniqueIdentifier} + }; + public static string GetProperWithDbType(this PropertyInfo propertyInfo) + { + bool result = ProperWithDbType.TryGetValue(propertyInfo.PropertyType, out string value); + if (result) + { + return value; + } + return SqlDbTypeName.NVarChar; + } + + /// + /// 验证数据库字段类型与值是否正确, + /// + /// 数据库字段类型(如varchar,nvarchar,decimal,不要带后面长度如:varchar(50)) + /// 值 + /// 要验证的类的属性,若不为null,则会判断字符串的长度是否正确 + /// (bool, string, object)bool成否校验成功,string校验失败信息,object,当前校验的值 + public static (bool, string, object) ValidationVal(this string dbType, object value, PropertyInfo propertyInfo = null) + { + if (string.IsNullOrEmpty(dbType)) + { + dbType = propertyInfo != null ? propertyInfo.GetProperWithDbType() : SqlDbTypeName.NVarChar; + } + dbType = dbType.ToLower(); + string val = value?.ToString(); + //验证长度 + string reslutMsg = string.Empty; + if (dbType == SqlDbTypeName.Int || dbType == SqlDbTypeName.BigInt) + { + if (!value.IsInt()) + reslutMsg = "只能为有效整数"; + } + else if (dbType == SqlDbTypeName.DateTime + || dbType == SqlDbTypeName.Date + || dbType == SqlDbTypeName.SmallDateTime + || dbType == SqlDbTypeName.SmallDate + ) + { + if (!value.IsDate()) + reslutMsg = "必须为日期格式"; + } + else if (dbType == SqlDbTypeName.Float || dbType == SqlDbTypeName.Decimal || dbType == SqlDbTypeName.Double) + { + string formatString = string.Empty; + if (propertyInfo != null) + formatString = propertyInfo.GetTypeCustomValue(x => x.DataFormatString); + //if (string.IsNullOrEmpty(formatString)) + // throw new Exception("请对字段" + propertyInfo?.Name + "添加DisplayFormat属性标识"); + + if (!val.IsNumber(formatString)) + { + string[] arr = (formatString ?? "10,0").Split(','); + reslutMsg = $"整数{arr[0]}最多位,小数最多{arr[1]}位"; + } + } + else if (dbType == SqlDbTypeName.UniqueIdentifier) + { + if (!val.IsGuid()) + { + reslutMsg = propertyInfo.Name + "Guid不正确"; + } + } + else if (propertyInfo != null + && (dbType == SqlDbTypeName.VarChar + || dbType == SqlDbTypeName.NVarChar + || dbType == SqlDbTypeName.NChar + || dbType == SqlDbTypeName.Char + || dbType == SqlDbTypeName.Text)) + { + + //默认nvarchar(max) 、text 长度不能超过20000 + if (val.Length > 20000) + { + reslutMsg = $"字符长度最多【20000】"; + } + else + { + int length = propertyInfo.GetTypeCustomValue(x => new { x.Length }).GetInt(); + if (length == 0) { return (true, null, null); } + //判断双字节与单字段 + else if (length < 8000 && + ((dbType.Substring(0, 1) != "n" + && Encoding.UTF8.GetBytes(val.ToCharArray()).Length > length) + || val.Length > length) + ) + { + reslutMsg = $"最多只能【{length}】个字符。"; + } + } + } + if (!string.IsNullOrEmpty(reslutMsg) && propertyInfo != null) + { + reslutMsg = propertyInfo.GetDisplayName() + reslutMsg; + } + return (reslutMsg == "" ? true : false, reslutMsg, value); + } + + public static string GetDisplayName(this PropertyInfo property) + { + string displayName = property.GetTypeCustomValue(x => new { x.Name }); + if (string.IsNullOrEmpty(displayName)) + { + return property.Name; + } + return displayName; + } + + /// + /// 获取属性的指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this MemberInfo member, Type type) + { + object[] obj = member.GetCustomAttributes(type, false); + if (obj.Length == 0) return null; + return obj[0]; + } + + /// + /// 获取类的多个指定属性的值 + /// + /// 当前类 + /// 指定的类 + /// 指定属性的值 格式 Expression> exp = x => new { x.字段1, x.字段2 }; + /// 返回的是字段+value + public static Dictionary GetTypeCustomValues(this MemberInfo member, Expression> expression) + { + var attr = member.GetTypeCustomAttributes(typeof(TEntity)); + if (attr == null) + { + return null; + } + + string[] propertyName = expression.GetExpressionProperty(); + Dictionary propertyKeyValues = new Dictionary(); + + foreach (PropertyInfo property in attr.GetType().GetProperties()) + { + if (propertyName.Contains(property.Name)) + { + propertyKeyValues[property.Name] = (property.GetValue(attr) ?? string.Empty).ToString(); + } + } + return propertyKeyValues; + } + + /// + /// 获取类的单个指定属性的值(只会返回第一个属性的值) + /// + /// 当前类 + /// 指定的类 + /// 指定属性的值 格式 Expression> exp = x => new { x.字段1, x.字段2 }; + /// + public static string GetTypeCustomValue(this MemberInfo member, Expression> expression) + { + var propertyKeyValues = member.GetTypeCustomValues(expression); + if (propertyKeyValues == null || propertyKeyValues.Count == 0) + { + return null; + } + return propertyKeyValues.First().Value ?? ""; + } + /// + /// 获取表带有EntityAttribute属性的真实表名 + /// + /// + /// + public static string GetEntityTableName(this Type type) + { + Attribute attribute = type.GetCustomAttribute(typeof(EntityAttribute)); + if (attribute != null && attribute is EntityAttribute) + { + return (attribute as EntityAttribute).TableName ?? type.Name; + } + return type.Name; + } + + +} + +public class ArrayEntity +{ + public string column1 { get; set; } +} + +public enum FieldType +{ + VarChar = 0, + NvarChar, + Int, + BigInt, + UniqueIdentifier +} + +public enum EntityToSqlTempName +{ + TempInsert = 0 +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/GenericExtension.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/GenericExtension.cs new file mode 100644 index 00000000..c07435f2 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/GenericExtension.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions; + +/// +/// 泛型扩展 +/// +public static class GenericExtension +{ + + public static DataTable ToDataTable(this IEnumerable source, Expression> columns = null, bool contianKey = true) + { + DataTable dtReturn = new DataTable(); + if (source == null) return dtReturn; + + PropertyInfo[] oProps = typeof(T).GetProperties() + .Where(x => x.PropertyType.Name != "List`1").ToArray(); + if (columns != null) + { + string[] columnArray = columns.GetExpressionToArray(); + oProps = oProps.Where(x => columnArray.Contains(x.Name)).ToArray(); + } + //移除自增主键 + PropertyInfo keyType = oProps.GetKeyProperty();// oProps.GetKeyProperty()?.PropertyType; + if (!contianKey && keyType != null && (keyType.PropertyType == typeof(int) || keyType.PropertyType == typeof(long))) + { + oProps = oProps.Where(x => x.Name != keyType.Name).ToArray(); + } + + foreach (var pi in oProps) + { + var colType = pi.PropertyType; + + if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + colType = colType.GetGenericArguments()[0]; + } + + dtReturn.Columns.Add(new DataColumn(pi.Name, colType)); + } + foreach (var rec in source) + { + var dr = dtReturn.NewRow(); + foreach (var pi in oProps) + { + dr[pi.Name] = pi.GetValue(rec, null) == null + ? DBNull.Value + : pi.GetValue + (rec, null); + } + dtReturn.Rows.Add(dr); + } + return dtReturn; + } + +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/LambdaExtensions.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/LambdaExtensions.cs new file mode 100644 index 00000000..e9d1a82b --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/LambdaExtensions.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Tiobon.Core.Common.DB.Dapper.Enums; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions +{ + public static class LambdaExtensions + { + /// + /// 获取对象表达式指定属性的值 + /// 如获取:Out_Scheduling对象的ID或基他字段 + /// + /// + /// 格式 Expression>sch=x=>new {x.v1,x.v2} or x=>x.v1 解析里面的值返回为数组 + /// + public static string[] GetExpressionToArray(this Expression> expression) + { + string[] propertyNames = null; + if (expression.Body is MemberExpression) + { + propertyNames = new string[] { ((MemberExpression)expression.Body).Member.Name }; + } + else + { + propertyNames = expression.GetExpressionProperty().Distinct().ToArray(); + } + return propertyNames; + } + + /// + /// 属性判断待完 + /// + /// + /// + public static IEnumerable GetGenericProperties(this Type type) + { + return type.GetProperties().GetGenericProperties(); + } + /// + /// 属性判断待完 + /// + /// + /// + public static IEnumerable GetGenericProperties(this IEnumerable properties) + { + return properties.Where(x => !x.PropertyType.IsGenericType && x.PropertyType.GetInterface("IList") == null || x.PropertyType.GetInterface("IEnumerable", false) == null); + } + } + + public class ParameterRebinder : ExpressionVisitor + { + + private readonly Dictionary map; + public ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + + public static Expression ReplaceParameters(Dictionary map, Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/MethodInfoExtensions.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/MethodInfoExtensions.cs new file mode 100644 index 00000000..b889e425 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/MethodInfoExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions; + +public static class MethodInfoExtensions +{ + public static string GetFullName(this MethodInfo method) + { + if (method.DeclaringType == null) + { + return $@"{method.Name}"; + } + + return $"{method.DeclaringType.FullName}.{method.Name}"; + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/ServerExtension.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/ServerExtension.cs new file mode 100644 index 00000000..88b32212 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/ServerExtension.cs @@ -0,0 +1,31 @@ +using Tiobon.Core.Common.DB.Dapper.BaseProvider.ServerMapPath; +using System; +using System.Collections.Generic; +using System.Text; +using Tiobon.Core.Common.DB.Dapper.Extensions.AutofacManager; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions +{ + public static class ServerExtension + { + /// + /// 返回的路径后面不带/,拼接时需要自己加上/ + /// + /// + /// + public static string MapPath(this string path) + { + return MapPath(path, false); + } + /// + /// + /// + /// + /// 获取wwwroot路径 + /// + public static string MapPath(this string path,bool rootPath) + { + return AutofacContainerModule.GetService().MapPath(path,rootPath); + } + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/ServiceProviderManagerExtension.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/ServiceProviderManagerExtension.cs new file mode 100644 index 00000000..f4bd9706 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/ServiceProviderManagerExtension.cs @@ -0,0 +1,18 @@ +using Tiobon.Core.Common.DB.Dapper.Extensions; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions +{ + public static class ServiceProviderManagerExtension + { + public static object GetService(this Type serviceType) + { + // HttpContext.Current.RequestServices.GetRequiredService(serviceType); + return Utilities.HttpContext.Current.RequestServices.GetService(serviceType); + } + + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/SimpleDapperSetup.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/SimpleDapperSetup.cs new file mode 100644 index 00000000..62389df2 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/SimpleDapperSetup.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions; + +/// +/// Tiobon.Core.Common.DB.Dapper 启动服务 +/// +public static class SimpleDapperSetup +{ + public static void AddSimpleDapperSetup(this IServiceCollection services) + { + AppSetting.Init(); + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Extensions/StringExtension.cs b/Tiobon.Core.Common/DB/Dapper/Extensions/StringExtension.cs new file mode 100644 index 00000000..ff87aae4 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Extensions/StringExtension.cs @@ -0,0 +1,117 @@ +using System; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using Tiobon.Core.Common.DB.Dapper.Const; +using Tiobon.Core.Common.DB.Dapper.Enums; + +namespace Tiobon.Core.Common.DB.Dapper.Extensions +{ + public static class StringExtension + { + public static bool _windows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static string ReplacePath(this string path) + { + if (string.IsNullOrEmpty(path)) + return ""; + if (_windows) + return path.Replace("/", "\\"); + return path.Replace("\\", "/"); + + } + + public static bool GetGuid(this string guid, out Guid outId) + { + Guid emptyId = Guid.Empty; + return Guid.TryParse(guid, out outId); + } + + public static bool IsGuid(this string guid) + { + Guid newId; + return guid.GetGuid(out newId); + } + + public static bool IsInt(this object obj) + { + if (obj == null) + return false; + bool reslut = Int32.TryParse(obj.ToString(), out int _number); + return reslut; + + } + public static bool IsDate(this object str) + { + return str.IsDate(out _); + } + public static bool IsDate(this object str, out DateTime dateTime) + { + dateTime = DateTime.Now; + if (str == null || str.ToString() == "") + { + return false; + } + return DateTime.TryParse(str.ToString(), out dateTime); + } + /// + /// 根据传入格式判断是否为小数 + /// + /// + /// 18,5 + /// + public static bool IsNumber(this string str, string formatString) + { + if (string.IsNullOrEmpty(str)) + return false; + int precision = 32; + int scale = 5; + try + { + if (string.IsNullOrEmpty(formatString)) + { + precision = 10; + scale = 2; + } + else + { + string[] numbers = formatString.Split(','); + precision = Convert.ToInt32(numbers[0]); + scale = numbers.Length == 0 ? 2 : Convert.ToInt32(numbers[1]); + } + } + catch { }; + return IsNumber(str, precision, scale); + } + /**/ + /// + /// 判断一个字符串是否为合法数字(指定整数位数和小数位数) + /// + /// 字符串 + /// 整数位数 + /// 小数位数 + /// + public static bool IsNumber(this string str, int precision, int scale) + { + if ((precision == 0) && (scale == 0)) + { + return false; + } + string pattern = @"(^\d{1," + precision + "}"; + if (scale > 0) + { + pattern += @"\.\d{0," + scale + "}$)|" + pattern; + } + pattern += "$)"; + return Regex.IsMatch(str, pattern); + } + + + public static int GetInt(this object obj) + { + if (obj == null) + return 0; + int.TryParse(obj.ToString(), out int _number); + return _number; + + } + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/ISqlDapper.cs b/Tiobon.Core.Common/DB/Dapper/ISqlDapper.cs new file mode 100644 index 00000000..e7ccd710 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/ISqlDapper.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Dapper; + +namespace Tiobon.Core.Common.DB.Dapper; + +public interface ISqlDapper +{ + /// + /// var p = new object(); + // p.Add("@a", 11); + //p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); + //p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); + // /// + IDbConnection Connection { get; } + /// + /// + /// + /// + /// + List QueryList(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) where T : class; + Task> QueryListAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class; + T QueryFirst(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) where T : class; + Task QueryFirstAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class; + object ExecuteScalar(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false); + Task ExecuteScalarAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false); + + int ExcuteNonQuery(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false); + IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false); + SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false); + (List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false); + (List, List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false); + + DataTable GetDataTable(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false); + Task GetDataTableAsync(string cmd, object param = null, IDbTransaction transaction = null, CommandType? commandType = null, int? commandTimeout = null); + + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + int Add(T entity, Expression> updateFileds = null, bool beginTransaction = false); + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + int AddRange(IEnumerable entities, Expression> updateFileds = null, bool beginTransaction = false); + + + /// + /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 + /// + /// + /// 实体必须带主键 + /// 指定更新的字段x=new {x.a,x.b} + /// 是否开启事务 + /// + int Update(T entity, Expression> updateFileds = null, bool beginTransaction = false); + + /// + /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 + /// + /// + /// 实体必须带主键 + /// 指定更新的字段x=new {x.a,x.b} + /// 是否开启事务 + /// + int UpdateRange(IEnumerable entities, Expression> updateFileds = null, bool beginTransaction = false); + + int DelWithKey(object[] keys); + int DelWithKey(object[] keys, bool beginTransaction = false); + /// + /// sqlserver批量写入 + /// 使用时DataTable table表字段顺序要和数据库字段顺序一致 + /// + /// mysql批量写入 + /// + /// + /// + /// 默认当前下载路径 + /// 默认$"{DateTime.Now.ToString("yyyyMMddHHmmss")}.csv" + /// + int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null); + /// + /// + /// + /// + /// + /// + /// 所包含的列 + /// + /// + /// + /// + int BulkInsert(List entities, string tableName = null, + Expression> columns = null, + SqlBulkCopyOptions? sqlBulkCopyOptions = null); + + T Execute(Func func, bool beginTransaction = false, bool disposeConn = true); + + int ExecuteDML(string cmd, object param = null, CommandType? commandType = null, IDbTransaction dbTransaction = null); + Task ExecuteDMLAsync(string cmd, object param = null, CommandType? commandType = null, IDbTransaction dbTransaction = null); + + IDbTransaction GetNewTransaction(); + + void CommitTransaction(IDbTransaction dbTransaction); + void RollbackTransaction(IDbTransaction dbTransaction); + DataTable GetTransDataTable(string cmd, object param = null, CommandType? commandType = null, IDbTransaction dbTransaction = null); + T QueryTransFirst(string cmd, object param = null, CommandType? commandType = null, IDbTransaction dbTransaction = null) where T : class; +} \ No newline at end of file diff --git a/Tiobon.Core.Common/DB/Dapper/ITransDapper.cs b/Tiobon.Core.Common/DB/Dapper/ITransDapper.cs new file mode 100644 index 00000000..949a2037 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/ITransDapper.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq.Expressions; +using Dapper; + +namespace Tiobon.Core.Common.DB.Dapper; + +public interface ITransDapper +{ + + void BeginTransaction(Func action, Action error, bool disposeConn = true); + + /// + /// var p = new object(); + // p.Add("@a", 11); + //p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); + //p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); + // /// + /// + /// + /// + /// + /// + List QueryList(string cmd, object param, CommandType? commandType = null) where T : class; + T QueryFirst(string cmd, object param, CommandType? commandType = null) where T : class; + object ExecuteScalar(string cmd, object param, CommandType? commandType = null); + + int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null); + IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null); + SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null); + (List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null); + (List, List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null); + + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + int Add(T entity, Expression> updateFileds = null); + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + int AddRange(IEnumerable entities, Expression> updateFileds = null); + + + /// + /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 + /// + /// + /// 实体必须带主键 + /// 指定更新的字段x=new {x.a,x.b} + /// 是否开启事务 + /// + int Update(T entity, Expression> updateFileds = null); + + /// + /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 + /// + /// + /// 实体必须带主键 + /// 指定更新的字段x=new {x.a,x.b} + /// 是否开启事务 + /// + int UpdateRange(IEnumerable entities, Expression> updateFileds = null); + + int DelWithKey(params object[] keys); + /// + /// sqlserver批量写入 + /// 使用时DataTable table表字段顺序要和数据库字段顺序一致 + /// + /// mysql批量写入 + /// + /// + /// + /// 默认当前下载路径 + /// 默认$"{DateTime.Now.ToString("yyyyMMddHHmmss")}.csv" + /// + int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null); + /// + /// + /// + /// + /// + /// + /// 所包含的列 + /// + /// + /// + /// + int BulkInsert(List entities, string tableName = null, + Expression> columns = null, + SqlBulkCopyOptions? sqlBulkCopyOptions = null); + + DataTable GetDataTable(string cmd, object param, CommandType? commandType = null); +} \ No newline at end of file diff --git a/Tiobon.Core.Common/DB/Dapper/SimpleDapper.csproj b/Tiobon.Core.Common/DB/Dapper/SimpleDapper.csproj new file mode 100644 index 00000000..9a7ca487 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/SimpleDapper.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + True + + + + + + + + false + + + + + + + + + + + + + + + + diff --git a/Tiobon.Core.Common/DB/Dapper/SqlDapper.cs b/Tiobon.Core.Common/DB/Dapper/SqlDapper.cs new file mode 100644 index 00000000..3859404f --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/SqlDapper.cs @@ -0,0 +1,665 @@ + +using Dapper; +using MySql.Data.MySqlClient; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using Tiobon.Core.Common.DB.Dapper.Const; +using Tiobon.Core.Common.DB.Dapper.DBManager; +using Tiobon.Core.Common.DB.Dapper.Enums; +using Tiobon.Core.Common.DB.Dapper.Extensions; +using Tiobon.Core.Common.DB.Dapper.Utilities; +using System.Threading.Tasks; + +namespace Tiobon.Core.Common.DB.Dapper; + +public class SqlDapper : ISqlDapper +{ + private string _connectionString; + private IDbConnection _connection { get; set; } + public IDbConnection Connection + { + get + { + if (_connection == null || _connection.State == ConnectionState.Closed) + { + _connection = DBServerProvider.GetDbConnection(_connectionString); + } + return _connection; + } + } + public SqlDapper() + { + _connectionString = DBServerProvider.GetConnectionString(); + } + /// + /// string mySql = "Data Source=132.232.2.109;Database=mysql;User + /// ID=root;Password=mysql;pooling=true;CharSet=utf8;port=3306;sslmode=none"; + /// this.conn = new MySql.Data.MySqlClient.MySqlConnection(mySql); + /// + /// + public SqlDapper(string connKeyName) + { + _connectionString = DBServerProvider.GetConnectionString(connKeyName); + } + + /// + /// var p = new object(); + // p.Add("@a", 11); + //p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); + //p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); + // /// + /// + /// + /// + /// + /// + public List QueryList(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class + { + return Execute((conn, dbTransaction) => + { + return conn.Query(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text).ToList(); + }, beginTransaction); + } + + public async Task> QueryListAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class + { + return await Execute(async (conn, dbTransaction) => + { + return (await conn.QueryAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text)).ToList(); + }, beginTransaction); + } + + + public T QueryFirst(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) where T : class + { + List list = QueryList(cmd, param, commandType: commandType ?? CommandType.Text, beginTransaction: beginTransaction).ToList(); + return list.Count == 0 ? null : list[0]; + } + public async Task QueryFirstAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class + { + var data = await QueryListAsync(cmd, param, commandType, beginTransaction, commandTimeout); + List list = data.ToList(); + return !list.Any() ? null : list[0]; + } + + public object ExecuteScalar(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + try + { + return Execute((conn, dbTransaction) => + { + return conn.ExecuteScalar(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, beginTransaction); + } + catch (Exception) + { + throw; + } + } + + public async Task ExecuteScalarAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + try + { + return await Execute(async (conn, dbTransaction) => + { + return await conn.ExecuteScalarAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, beginTransaction); + } + catch (Exception) + { + throw; + } + } + + public int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + return Execute((conn, dbTransaction) => + { + return conn.Execute(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, beginTransaction); + } + public IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + return Execute((conn, dbTransaction) => + { + return conn.ExecuteReader(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, beginTransaction, false); + } + public SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + return Execute((conn, dbTransaction) => + { + return conn.QueryMultiple(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, beginTransaction, false); + } + + /// + /// 获取output值 param.Get("@b"); + /// + /// + /// + /// + /// + /// + /// + public (List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + using (SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType, beginTransaction)) + { + return (reader.Read().ToList(), reader.Read().ToList()); + } + } + + public (List, List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + using (SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType, beginTransaction)) + { + return (reader.Read().ToList(), reader.Read().ToList(), reader.Read().ToList()); + } + } + + + public T Execute(Func func, bool beginTransaction = false, bool disposeConn = true) + { + IDbTransaction dbTransaction = null; + + if (beginTransaction) + { + Connection.Open(); + dbTransaction = Connection.BeginTransaction(); + } + try + { + T reslutT = func(Connection, dbTransaction); + dbTransaction?.Commit(); + return reslutT; + } + catch (Exception) + { + dbTransaction?.Rollback(); + Connection.Dispose(); + throw; + } + finally + { + if (disposeConn) + { + Connection.Dispose(); + } + } + } + + public async Task ExecuteAsync(Func> funcAsync, bool beginTransaction = false, bool disposeConn = true) + { + IDbTransaction dbTransaction = null; + + if (beginTransaction) + { + Connection.Open(); + dbTransaction = Connection.BeginTransaction(); + } + try + { + T reslutT = await funcAsync(Connection, dbTransaction); + dbTransaction?.Commit(); + return reslutT; + } + catch (Exception) + { + dbTransaction?.Rollback(); + Connection.Dispose(); + throw; + } + finally + { + if (disposeConn) + { + Connection.Dispose(); + } + } + } + + public int ExecuteDML(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) + { + return ExecuteDML((conn, dbTransaction) => + { + return conn.Execute(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, dbTransaction, false); + } + + public async Task ExecuteDMLAsync(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) + { + return await ExecuteDML(async (conn, dbTransaction) => + { + return await conn.ExecuteAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, dbTransaction, false); + } + + public IDataReader ExecuteDMLReader(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) + { + return ExecuteDML((conn, dbTransaction) => + { + return conn.ExecuteReader(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }, dbTransaction, false); + } + + + /// + /// 执行数据库 + /// + /// + /// + /// + /// + /// + public T ExecuteDML(Func func, IDbTransaction dbTransaction = null, bool disposeConn = false) + { + if (dbTransaction == null) + { + try + { + T reslutT = func(Connection, dbTransaction); + dbTransaction?.Commit(); + return reslutT; + } + catch (Exception) + { + dbTransaction?.Rollback(); + Connection.Dispose(); + throw; + } + finally + { + if (disposeConn) + Connection.Dispose(); + } + } + else + { + + //Connection.Open(); + + try + { + if (_connection == null) + _connection = dbTransaction.Connection; + T reslutT = func(Connection, dbTransaction); + return reslutT; + } + catch (Exception) + { + Connection.Dispose(); + throw; + } + finally + { + if (disposeConn) + Connection.Dispose(); + } + } + + } + + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + public int Add(T entity, Expression> updateFileds = null, bool beginTransaction = false) + { + return AddRange(new T[] { entity }, updateFileds, beginTransaction); + } + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + public int AddRange(IEnumerable entities, Expression> addFileds = null, bool beginTransaction = true) + { + Type entityType = typeof(T); + var key = entityType.GetKeyProperty(); + if (key == null) + { + throw new Exception("实体必须包括主键才能批量更新"); + } + string[] columns; + + //指定插入的字段 + if (addFileds != null) + { + columns = addFileds.GetExpressionToArray(); + } + else + { + var properties = entityType.GetGenericProperties(); + if (key.PropertyType != typeof(Guid)) + { + properties = properties.Where(x => x.Name != key.Name).ToArray(); + } + columns = properties.Select(x => x.Name).ToArray(); + } + string sql = null; + bool mysql = DBType.Name == DbCurrentType.MySql.ToString(); + if (mysql) + { + //mysql批量写入待优化 + sql = $"insert into {entityType.GetEntityTableName()}({string.Join(",", columns)})" + + $"values(@{string.Join(",@", columns)});"; + } + else + { + //sqlserver通过临时表批量写入 + sql = $"insert into {entityType.GetEntityTableName()}({string.Join(",", columns)})" + + $"select * from {EntityToSqlTempName.TempInsert};"; + sql = entities.GetEntitySql(entityType == typeof(Guid), sql, null, addFileds, null); + } + return Execute((conn, dbTransaction) => + { + return conn.Execute(sql, mysql ? entities.ToList() : null); + }, beginTransaction); + } + + + /// + /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 + /// + /// + /// 实体必须带主键 + /// 指定更新的字段x=new {x.a,x.b} + /// 是否开启事务 + /// + public int Update(T entity, Expression> updateFileds = null, bool beginTransaction = true) + { + return UpdateRange(new T[] { entity }, updateFileds, beginTransaction); + } + + /// + ///(根据主键批量更新实体) sqlserver使用的临时表参数化批量更新,mysql待优化 + /// + /// + /// 实体必须带主键 + /// 批定更新字段 + /// + /// + public int UpdateRange(IEnumerable entities, Expression> updateFileds = null, bool beginTransaction = true) + { + Type entityType = typeof(T); + var key = entityType.GetKeyProperty(); + if (key == null) + { + throw new Exception("实体必须包括主键才能批量更新"); + } + + var properties = entityType.GetGenericProperties() + .Where(x => x.Name != key.Name); + if (updateFileds != null) + { + properties = properties.Where(x => updateFileds.GetExpressionToArray().Contains(x.Name)); + } + + if (DBType.Name == DbCurrentType.MySql.ToString()) + { + List paramsList = new List(); + foreach (var item in properties) + { + paramsList.Add(item.Name + "=@" + item.Name); + } + string sqltext = $@"UPDATE {entityType.GetEntityTableName()} SET {string.Join(",", paramsList)} WHERE {entityType.GetKeyName()} = @{entityType.GetKeyName()} ;"; + + return ExcuteNonQuery(sqltext, entities, CommandType.Text, true); + // throw new Exception("mysql批量更新未实现"); + } + string fileds = string.Join(",", properties.Select(x => $" a.{x.Name}=b.{x.Name}").ToArray()); + string sql = $"update a set {fileds} from {entityType.GetEntityTableName()} as a inner join {EntityToSqlTempName.TempInsert.ToString()} as b on a.{key.Name}=b.{key.Name}"; + sql = entities.ToList().GetEntitySql(true, sql, null, updateFileds, null); + return ExcuteNonQuery(sql, null, CommandType.Text, true); + } + + public int DelWithKey(object[] keys, bool beginTransaction = false) + { + Type entityType = typeof(T); + var keyProperty = entityType.GetKeyProperty(); + if (keyProperty == null || keys == null || keys.Length == 0) return 0; + + IEnumerable<(bool, string, object)> validation = keyProperty.ValidationValueForDbType(keys); + if (validation.Any(x => !x.Item1)) + { + throw new Exception($"主键类型【{validation.Where(x => !x.Item1).Select(s => s.Item3).FirstOrDefault()}】不正确"); + } + string tKey = entityType.GetKeyProperty().Name; + FieldType fieldType = entityType.GetFieldType(); + string joinKeys = fieldType == FieldType.Int || fieldType == FieldType.BigInt + ? string.Join(",", keys) + : $"'{string.Join("','", keys)}'"; + + string sql = $"DELETE FROM {entityType.GetEntityTableName()} where {tKey} in ({joinKeys});"; + return (int)ExecuteScalar(sql, null); + } + /// + /// 使用key批量删除 + /// + /// + /// + /// + public int DelWithKey(object[] keys) + { + return DelWithKey(keys, false); + } + + #region 批量插入 + /// + /// 通过Bulk批量插入 + /// + /// + /// + /// + /// + /// + private int MSSqlBulkInsert(DataTable table, string tableName, SqlBulkCopyOptions sqlBulkCopyOptions = SqlBulkCopyOptions.UseInternalTransaction, string dbKeyName = null) + { + if (!string.IsNullOrEmpty(dbKeyName)) + { + Connection.ConnectionString = DBServerProvider.GetConnectionString(dbKeyName); + } + using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(Connection.ConnectionString, sqlBulkCopyOptions)) + { + sqlBulkCopy.DestinationTableName = tableName; + sqlBulkCopy.BatchSize = table.Rows.Count; + for (int i = 0; i < table.Columns.Count; i++) + { + sqlBulkCopy.ColumnMappings.Add(table.Columns[i].ColumnName, table.Columns[i].ColumnName); + } + sqlBulkCopy.WriteToServer(table); + return table.Rows.Count; + } + } + public int BulkInsert(List entities, string tableName = null, + Expression> columns = null, + SqlBulkCopyOptions? sqlBulkCopyOptions = null) + { + DataTable table = entities.ToDataTable(columns, false); + return BulkInsert(table, tableName ?? typeof(T).GetEntityTableName(), sqlBulkCopyOptions); + } + public int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null) + { + if (!string.IsNullOrEmpty(tmpPath)) + { + tmpPath = tmpPath.ReplacePath(); + } + if (Connection.GetType().Name == "MySqlConnection") + return MySqlBulkInsert(table, tableName, fileName, tmpPath); + return MSSqlBulkInsert(table, tableName, sqlBulkCopyOptions ?? SqlBulkCopyOptions.KeepIdentity); + } + #endregion + + #region 大批量数据插入,返回成功插入行数 + /// + ///大批量数据插入,返回成功插入行数 + /// + /// 数据库连接字符串 + /// 数据表 + /// 返回成功插入行数 + private int MySqlBulkInsert(DataTable table, string tableName, string fileName = null, string tmpPath = null) + { + if (table.Rows.Count == 0) + return 0; + tmpPath = tmpPath ?? FileHelper.GetCurrentDownLoadPath(); + fileName = fileName ?? $"{DateTime.Now.ToString("yyyyMMddHHmmss")}.csv"; + int insertCount = 0; + string csv = DataTableToCsv(table); + FileHelper.WriteFile(tmpPath, fileName, csv); + string path = tmpPath + fileName; + try + { + if (Connection.State == ConnectionState.Closed) + Connection.Open(); + using (IDbTransaction tran = Connection.BeginTransaction()) + { + MySqlBulkLoader bulk = new MySqlBulkLoader(Connection as MySqlConnection) + { + FieldTerminator = ",", + FieldQuotationCharacter = '"', + EscapeCharacter = '"', + LineTerminator = "\r\n", + FileName = path.ReplacePath(), + NumberOfLinesToSkip = 0, + TableName = tableName, + }; + bulk.Columns.AddRange(table.Columns.Cast().Select(colum => colum.ColumnName).ToList()); + insertCount = bulk.Load(); + tran.Commit(); + } + } + catch (Exception) { throw; } + finally + { + Connection?.Dispose(); + Connection?.Close(); + } + return insertCount; + // File.Delete(path); + } + #endregion + + #region 将DataTable转换为标准的CSV + /// + ///将DataTable转换为标准的CSV + /// + /// 数据表 + /// 返回标准的CSV + private string DataTableToCsv(DataTable table) + { + //以半角逗号(即,)作分隔符,列为空也要表达其存在。 + //列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。 + //列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。 + StringBuilder sb = new StringBuilder(); + DataColumn colum; + Type typeString = typeof(string); + Type typeDate = typeof(DateTime); + + foreach (DataRow row in table.Rows) + { + for (int i = 0; i < table.Columns.Count; i++) + { + colum = table.Columns[i]; + if (i != 0) sb.Append(","); + if (colum.DataType == typeString && row[colum].ToString().Contains(",")) + { + sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\""); + } + else if (colum.DataType == typeDate) + { + //centos系统里把datatable里的日期转换成了10/18/18 3:26:15 PM格式 + bool b = DateTime.TryParse(row[colum].ToString(), out DateTime dt); + sb.Append(b ? dt.ToString("yyyy-MM-dd HH:mm:ss") : ""); + } + else sb.Append(row[colum].ToString()); + } + sb.AppendLine(); + } + + return sb.ToString(); + } + #endregion + + #region 返回DataTable + /// + /// 返回DataTable + /// + /// + /// + /// + /// + /// + public DataTable GetDataTable(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) + { + DataTable table = new DataTable("MyTable"); + var reader = ExecuteReader(cmd, param, commandType, beginTransaction); + table.Load(reader); + return table; + } + + public DataTable GetTransDataTable(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) + { + DataTable table = new DataTable("MyTable"); + var reader = ExecuteDMLReader(cmd, param, commandType, dbTransaction); + table.Load(reader); + return table; + } + + public async Task GetDataTableAsync(string cmd, object param = null, IDbTransaction transaction = null, CommandType? commandType = null, int? commandTimeout = null) + { + DataTable table = new DataTable("MyTable"); + var reader = await Connection.ExecuteReaderAsync(cmd, param, transaction, commandTimeout, commandType: commandType ?? CommandType.Text); ; + table.Load(reader); + return table; + } + #endregion + + #region Transaction + public IDbTransaction GetNewTransaction() + { + Connection.Open(); + IDbTransaction trans = Connection.BeginTransaction(); + return trans; + } + + public void CommitTransaction(IDbTransaction dbTransaction) + { + + dbTransaction?.Commit(); + } + + public void RollbackTransaction(IDbTransaction dbTransaction) + { + dbTransaction?.Rollback(); + } + #endregion Transaction + + #region 查询按实体返回 + public List QueryTransList(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) where T : class + { + return ExecuteDML((conn, dbTransaction) => + { + return conn.Query(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text).ToList(); + }, dbTransaction, false); + } + public T QueryTransFirst(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) where T : class + { + List list = QueryTransList(cmd, param, commandType: commandType ?? CommandType.Text, dbTransaction: dbTransaction).ToList(); + return list.Count == 0 ? null : list[0]; + } + #endregion +} diff --git a/Tiobon.Core.Common/DB/Dapper/TransDapper.cs b/Tiobon.Core.Common/DB/Dapper/TransDapper.cs new file mode 100644 index 00000000..bb1f5ec1 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/TransDapper.cs @@ -0,0 +1,495 @@ + +using Dapper; +using Tiobon.Core.Common.DB.Dapper.Const; +using Tiobon.Core.Common.DB.Dapper.DBManager; +using Tiobon.Core.Common.DB.Dapper.Enums; +using Tiobon.Core.Common.DB.Dapper.Extensions; +using Tiobon.Core.Common.DB.Dapper.Utilities; +using MySql.Data.MySqlClient; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Data.SqlClient; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + + +namespace Tiobon.Core.Common.DB.Dapper; + +public class TransDapper : ITransDapper +{ + private string _connectionString; + private IDbConnection _connection { get; set; } + public IDbConnection Connection + { + get + { + if (_connection == null || _connection.State == ConnectionState.Closed) + _connection = DBServerProvider.GetDbConnection(_connectionString); + return _connection; + } + } + + + public TransDapper() + { + _connectionString = DBServerProvider.GetConnectionString(); + } + /// + /// string mySql = "Data Source=132.232.2.109;Database=mysql;User + /// ID=root;Password=mysql;pooling=true;CharSet=utf8;port=3306;sslmode=none"; + /// this.conn = new MySql.Data.MySqlClient.MySqlConnection(mySql); + /// + /// + public TransDapper(string connKeyName) + { + _connectionString = DBServerProvider.GetConnectionString(connKeyName); + } + + + private bool _transaction { get; set; } + + /// + /// 增加Dapper事务处理 + /// + /// + public void BeginTransaction(Func action, Action error, bool disposeConn = true) + { + _transaction = true; + try + { + Connection.Open(); + dbTransaction = Connection.BeginTransaction(); + bool result = action(this); + if (result) + { + dbTransaction?.Commit(); + } + else + { + dbTransaction?.Rollback(); + } + } + catch (Exception ex) + { + dbTransaction?.Rollback(); + error(ex); + } + finally + { + if (!_transaction) + { + if (disposeConn) + { + Connection.Dispose(); + } + dbTransaction?.Dispose(); + } + dbTransaction?.Dispose(); + _transaction = false; + } + } + + /// + /// var p = new object(); + // p.Add("@a", 11); + //p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); + //p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); + // /// + /// + /// + /// + /// + /// + public List QueryList(string cmd, object param, CommandType? commandType = null) where T : class + { + return Execute((conn) => + { + return conn.Query(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text).ToList(); + }); + } + public T QueryFirst(string cmd, object param, CommandType? commandType = null) where T : class + { + List list = QueryList(cmd, param, commandType: commandType ?? CommandType.Text).ToList(); + return list.Count == 0 ? null : list[0]; + } + public object ExecuteScalar(string cmd, object param, CommandType? commandType = null) + { + return Execute((conn) => + { + return conn.ExecuteScalar(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }); + } + + public int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null) + { + return Execute((conn) => + { + return conn.Execute(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }); + } + public IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null) + { + return Execute((conn) => + { + return conn.ExecuteReader(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }); + } + public SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null) + { + return Execute((conn) => + { + return conn.QueryMultiple(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); + }); + } + + /// + /// 获取output值 param.Get("@b"); + /// + /// + /// + /// + /// + /// + /// + public (List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null) + { + using (SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType)) + { + return (reader.Read().ToList(), reader.Read().ToList()); + } + } + + public (List, List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null) + { + using (SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType)) + { + return (reader.Read().ToList(), reader.Read().ToList(), reader.Read().ToList()); + } + } + IDbTransaction dbTransaction = null; + + private T Execute(Func func) + { + if (dbTransaction == null) throw new Exception("该方法用于事务,请使用DBHelper.Instance"); + try + { + T reslutT = func(Connection); + return reslutT; + } + catch (Exception ex) + { + throw; + } + } + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + public int Add(T entity, Expression> updateFileds = null) + { + return AddRange(new T[] { entity }, updateFileds); + } + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + public int AddRange(IEnumerable entities, Expression> addFileds = null) + { + Type entityType = typeof(T); + var key = entityType.GetKeyProperty(); + if (key == null) + throw new Exception("实体必须包括主键才能批量更新!"); + + string[] columns; + + //指定插入的字段 + if (addFileds != null) + columns = addFileds.GetExpressionToArray(); + + else + { + var properties = entityType.GetGenericProperties().Where(x => !x.ContainsCustomAttributes(typeof(NotMappedAttribute))); + //&& !string.IsNullOrEmpty(entityType.GetProperty(x.Name).GetValue(entities.FirstOrDefault())?.ToString())); + //var value = entityType.GetProperty(property.Name).GetValue(mainEntity)?.ToString(); + //if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) + //{ + // dbInsert.AddValue(key, value); + //} + //if (key.PropertyType != typeof(Guid)) + //{ + // properties = properties.Where(x => x.Name != key.Name).ToArray(); + //} + // 排除插入的为空的字段,避免影响数据库中的默认值 + //columns = properties.Select(x => x.Name).ToArray(); + columns = properties.Where(x => + { + foreach (var entitie in entities) + { + var data = typeof(T).GetProperty(x.Name).GetValue(entitie); + if (data != null) + return true; + } + return false; + }).Select(x => x.Name).ToArray(); + } + string sql = null; + if (DBType.Name == DbCurrentType.MySql.ToString()) + { + //mysql批量写入待优化 + sql = $"insert into {entityType.GetEntityTableName()}({string.Join(",", columns)})" + + $"values(@{string.Join(",@", columns)});"; + } + else if (DBType.Name == DbCurrentType.PgSql.ToString()) + { + //todo pgsql批量写入 待检查是否正确 + sql = $"insert into {entityType.GetEntityTableName()}({"\"" + string.Join("\",\"", columns) + "\""})" + + $"values(@{string.Join(",@", columns)});"; + } + else + { + //sqlserver通过临时表批量写入 + sql = $"insert into {entityType.GetEntityTableName()}({string.Join(",", columns)})" + + $"select {string.Join(",", columns)} from {EntityToSqlTempName.TempInsert};"; + //sql = entities.GetEntitySql(entityType == typeof(Guid), sql, null, addFileds, null); + sql = entities.GetEntitySql(true, sql, null, addFileds, null); + } + return Execute((conn) => + { + //todo pgsql待实现 + return conn.Execute(sql, DBType.Name == DbCurrentType.MySql.ToString() || DBType.Name == DbCurrentType.PgSql.ToString() ? entities.ToList() : null, dbTransaction); + }); + } + + + /// + /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 + /// + /// + /// 实体必须带主键 + /// 指定更新的字段x=new {x.a,x.b} + /// 是否开启事务 + /// + public int Update(T entity, Expression> updateFileds = null) + { + return UpdateRange(new T[] { entity }, updateFileds); + } + + /// + ///(根据主键批量更新实体) sqlserver使用的临时表参数化批量更新,mysql待优化 + /// + /// + /// 实体必须带主键 + /// 批定更新字段 + /// + /// + public int UpdateRange(IEnumerable entities, Expression> updateFileds = null) + { + Type entityType = typeof(T); + var key = entityType.GetKeyProperty(); + if (key == null) + throw new Exception("实体必须包括主键才能批量更新"); + + var properties = entityType.GetGenericProperties().Where(x => x.Name != key.Name && !x.ContainsCustomAttributes(typeof(NotMappedAttribute))); + if (updateFileds != null) + properties = properties.Where(x => updateFileds.GetExpressionToArray().Contains(x.Name)); + + if (DBType.Name == DbCurrentType.MySql.ToString()) + { + List paramsList = new List(); + foreach (var item in properties) + { + paramsList.Add(item.Name + "=@" + item.Name); + } + string sqltext = $@"UPDATE {entityType.GetEntityTableName()} SET {string.Join(",", paramsList)} WHERE {entityType.GetKeyName()} = @{entityType.GetKeyName()} ;"; + + return ExcuteNonQuery(sqltext, entities, CommandType.Text); + // throw new Exception("mysql批量更新未实现"); + } + string fileds = string.Join(",", properties.Select(x => $" a.{x.Name}=b.{x.Name}").ToArray()); + string sql = $"update a set {fileds} from {entityType.GetEntityTableName()} as a inner join {EntityToSqlTempName.TempInsert.ToString()} as b on a.{key.Name}=b.{key.Name}"; + sql = entities.ToList().GetEntitySql(true, sql, null, updateFileds, null); + return ExcuteNonQuery(sql, null, CommandType.Text); + } + + public int DelWithKey(params object[] keys) + { + Type entityType = typeof(T); + var keyProperty = entityType.GetKeyProperty(); + if (keyProperty == null || keys == null || keys.Length == 0) return 0; + + IEnumerable<(bool, string, object)> validation = keyProperty.ValidationValueForDbType(keys); + if (validation.Any(x => !x.Item1)) + throw new Exception($"主键类型【{validation.Where(x => !x.Item1).Select(s => s.Item3).FirstOrDefault()}】不正确"); + + string tKey = entityType.GetKeyProperty().Name; + FieldType fieldType = entityType.GetFieldType(); + string joinKeys = fieldType == FieldType.Int || fieldType == FieldType.BigInt + ? string.Join(",", keys) + : $"'{string.Join("','", keys)}'"; + + string sql = $"DELETE FROM {entityType.GetEntityTableName()} where {tKey} in ({joinKeys});"; + return ExcuteNonQuery(sql, null); + } + + /// + /// 通过Bulk批量插入 + /// + /// + /// + /// + /// + /// + private int MSSqlBulkInsert(DataTable table, string tableName, SqlBulkCopyOptions sqlBulkCopyOptions = SqlBulkCopyOptions.UseInternalTransaction, string dbKeyName = null) + { + if (!string.IsNullOrEmpty(dbKeyName)) + { + Connection.ConnectionString = DBServerProvider.GetConnectionString(dbKeyName); + } + using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(Connection.ConnectionString, sqlBulkCopyOptions)) + { + sqlBulkCopy.DestinationTableName = tableName; + sqlBulkCopy.BatchSize = table.Rows.Count; + for (int i = 0; i < table.Columns.Count; i++) + { + sqlBulkCopy.ColumnMappings.Add(table.Columns[i].ColumnName, table.Columns[i].ColumnName); + } + sqlBulkCopy.WriteToServer(table); + return table.Rows.Count; + } + } + public int BulkInsert(List entities, string tableName = null, + Expression> columns = null, + SqlBulkCopyOptions? sqlBulkCopyOptions = null) + { + DataTable table = entities.ToDataTable(columns, false); + return BulkInsert(table, tableName ?? typeof(T).GetEntityTableName(), sqlBulkCopyOptions); + } + public int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null) + { + if (!string.IsNullOrEmpty(tmpPath)) + { + tmpPath = tmpPath.ReplacePath(); + } + if (Connection.GetType().Name == "MySqlConnection") + return MySqlBulkInsert(table, tableName, fileName, tmpPath); + else if (Connection.GetType().Name == "NpgsqlConnection") + { + //todo pgsql待实现 + throw new Exception("Pgsql的批量插入没实现,可以先把日志start注释跑起来"); + } + return MSSqlBulkInsert(table, tableName, sqlBulkCopyOptions ?? SqlBulkCopyOptions.KeepIdentity); + } + + /// + ///大批量数据插入,返回成功插入行数 + /// + /// 数据库连接字符串 + /// 数据表 + /// 返回成功插入行数 + private int MySqlBulkInsert(DataTable table, string tableName, string fileName = null, string tmpPath = null) + { + if (table.Rows.Count == 0) + return 0; + tmpPath = tmpPath ?? FileHelper.GetCurrentDownLoadPath(); + fileName = fileName ?? $"{DateTime.Now.ToString("yyyyMMddHHmmss")}.csv"; + int insertCount = 0; + string csv = DataTableToCsv(table); + FileHelper.WriteFile(tmpPath, fileName, csv); + string path = tmpPath + fileName; + try + { + if (Connection.State == ConnectionState.Closed) + Connection.Open(); + using (IDbTransaction tran = Connection.BeginTransaction()) + { + MySqlBulkLoader bulk = new MySqlBulkLoader(Connection as MySqlConnection) + { + FieldTerminator = ",", + FieldQuotationCharacter = '"', + EscapeCharacter = '"', + LineTerminator = "\r\n", + FileName = path.ReplacePath(), + NumberOfLinesToSkip = 0, + TableName = tableName, + }; + bulk.Columns.AddRange(table.Columns.Cast().Select(colum => colum.ColumnName).ToList()); + insertCount = bulk.Load(); + tran.Commit(); + } + } + catch (Exception ex) + { + throw; + } + finally + { + Connection?.Dispose(); + Connection?.Close(); + } + return insertCount; + // File.Delete(path); + } + /// + ///将DataTable转换为标准的CSV + /// + /// 数据表 + /// 返回标准的CSV + private string DataTableToCsv(DataTable table) + { + //以半角逗号(即,)作分隔符,列为空也要表达其存在。 + //列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。 + //列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。 + StringBuilder sb = new StringBuilder(); + DataColumn colum; + Type typeString = typeof(string); + Type typeDate = typeof(DateTime); + + foreach (DataRow row in table.Rows) + { + for (int i = 0; i < table.Columns.Count; i++) + { + colum = table.Columns[i]; + if (i != 0) sb.Append(","); + if (colum.DataType == typeString && row[colum].ToString().Contains(",")) + sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\""); + else if (colum.DataType == typeDate) + { + //centos系统里把datatable里的日期转换成了10/18/18 3:26:15 PM格式 + bool b = DateTime.TryParse(row[colum].ToString(), out DateTime dt); + sb.Append(b ? dt.ToString("yyyy-MM-dd HH:mm:ss") : ""); + } + else sb.Append(row[colum].ToString()); + } + sb.AppendLine(); + } + + return sb.ToString(); + } + + /// + /// 返回DataTable + /// + /// + /// + /// + /// + public DataTable GetDataTable(string cmd, object param, CommandType? commandType = null) + { + DataTable table = new DataTable("MyTable"); + var reader = ExecuteReader(cmd, param, commandType); + table.Load(reader); + return table; + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Utilities/DbAccess.cs b/Tiobon.Core.Common/DB/Dapper/Utilities/DbAccess.cs new file mode 100644 index 00000000..e5254a6b --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Utilities/DbAccess.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using Tiobon.Core.Common.DB.Dapper.Const; +using Tiobon.Core.Common.DB.Dapper.DBManager; +using Tiobon.Core.Common.DB.Dapper.Enums; + +namespace Tiobon.Core.Common.DB.Dapper; + +public class DbAccess +{ + public static ISqlDapper Instance + { + get + { + return DBServerProvider.SqlDapper; + } + } + + + public static bool MySql + { + get + { + bool result = false; + if (DBType.Name == DbCurrentType.MySql.ToString()) + { + result = true; + } + return result; + } + } + + #region 获取SQL插入语句 + /// + /// 获取SQL插入语句 + /// + /// 表名 + /// 列名 + /// 列值 + /// SQL插入语句 + public StringBuilder GetInsertSql(string tableName, string columnName, string columnValue) + { + try + { + DbInsert di = null; + string sql = null; + StringBuilder sqls = new StringBuilder(); + DbSelect ds = new DbSelect(tableName + " A", "A"); + ds.IsInitDefaultValue = false; + ds.Where("A." + columnName, "=", columnValue); + DataTable dt = Instance.GetDataTable(ds.GetSql(), null); + for (int i = 0; i < dt.Rows.Count; i++) + { + di = new DbInsert(tableName, "GetInsertSql"); + di.IsInitDefaultValue = false; + for (int j = 0; j < dt.Columns.Count; j++) + { + di.Values(dt.Columns[j].ColumnName, dt.Rows[i][dt.Columns[j].ColumnName].ToString()); + } + sql = di.GetSql(); + sqls.Append(sql + ";\n"); + } + return sqls; + } + catch (Exception) { throw; } + } + + #endregion + + #region 数据库名称 + /// + /// 数据库名称 + /// + public static string DatabaseName + { + get + { + return Instance.Connection.Database; + } + } + + #endregion + + + public static DataTable GetDataTable(string sql, object param = null, CommandType? commandType = null, bool beginTransaction = false) => Instance.GetDataTable(sql, param, commandType, beginTransaction); + public static async Task GetDataTableAsync(string cmd, object param = null, IDbTransaction transaction = null, CommandType? commandType = null, int? commandTimeout = null) => await Instance.GetDataTableAsync(cmd, param, transaction, commandType, commandTimeout); + + public static List QueryList(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class => Instance.QueryList(cmd, param, commandType, beginTransaction); + + public static async Task> QueryListAsync(string cmd, object param = null, bool beginTransaction = false, CommandType? commandType = null, int? commandTimeout = null) where T : class => await Instance.QueryListAsync(cmd, param, commandType, beginTransaction, commandTimeout); + + public static T QueryFirst(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) where T : class => Instance.QueryFirst(cmd, param, commandType, beginTransaction); + public static async Task QueryFirstAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class => await Instance.QueryFirstAsync(cmd, param, commandType, beginTransaction, commandTimeout); + + public static object ExecuteScalar(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) => Instance.ExecuteScalar(cmd, param, commandType, beginTransaction); + + public static async Task ExecuteScalarAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) => await Instance.ExecuteScalarAsync(cmd, param, commandType, beginTransaction); + public static int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) => Instance.ExcuteNonQuery(cmd, param, commandType, beginTransaction); + public static (List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) => Instance.QueryMultiple(cmd, param, commandType, beginTransaction); + + public static (List, List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) => Instance.QueryMultiple(cmd, param, commandType, beginTransaction); + + public static int ExecuteDML(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) => Instance.ExecuteDML(cmd, param, commandType, dbTransaction); + + public static async Task ExecuteDMLAsync(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) => await Instance.ExecuteDMLAsync(cmd, param, commandType, dbTransaction); + + public static int Add(T entity, Expression> updateFileds = null, bool beginTransaction = false) + { + return Instance.Add(entity, updateFileds, beginTransaction); + } + /// + /// + /// + /// + /// + /// 指定插入的字段 + /// 是否开启事务 + /// + public static int AddRange(IEnumerable entities, Expression> addFileds = null, bool beginTransaction = true) + { + return Instance.AddRange(entities, addFileds, beginTransaction); + } + + public static int Update(T entity, Expression> updateFileds = null, bool beginTransaction = true) + { + return Instance.Update(entity, updateFileds, beginTransaction); + } + + /// + ///(根据主键批量更新实体) sqlserver使用的临时表参数化批量更新,mysql待优化 + /// + /// + /// 实体必须带主键 + /// 批定更新字段 + /// + /// + public static int UpdateRange(IEnumerable entities, Expression> updateFileds = null, bool beginTransaction = true) + { + return Instance.UpdateRange(entities, updateFileds, beginTransaction); + } + + public static int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null) + { + return Instance.BulkInsert(table, tableName, sqlBulkCopyOptions, fileName, tmpPath); + } + +} + diff --git a/Tiobon.Core.Common/DB/Dapper/Utilities/FileHelper.cs b/Tiobon.Core.Common/DB/Dapper/Utilities/FileHelper.cs new file mode 100644 index 00000000..f265d81c --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Utilities/FileHelper.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Text; +using Tiobon.Core.Common.DB.Dapper.Extensions; + +namespace Tiobon.Core.Common.DB.Dapper.Utilities; +public class FileHelper : IDisposable +{ + public static string GetCurrentDownLoadPath() + { + return ("Download\\").MapPath(); + } + + #region 取得文件后缀名 + /// + /// + /// + /// 路径 + /// 文件名 + /// 写入的内容 + /// 是否将内容添加到未尾,默认不添加 + public static void WriteFile(string path, string fileName, string content, bool appendToLast = false) + { + try + { + path = path.ReplacePath(); + fileName = fileName.ReplacePath(); + if (!Directory.Exists(path))//如果不存在就创建file文件夹 + Directory.CreateDirectory(path); + + using (FileStream stream = File.Open(path + fileName, FileMode.OpenOrCreate, FileAccess.Write)) + { + byte[] by = Encoding.Default.GetBytes($"-- {DateTime.Now:yyyy-MM-dd HH:mm:ss}" + "\r\n" + content); + if (appendToLast) + { + stream.Position = stream.Length; + } + else + { + stream.SetLength(0); + } + stream.Write(by, 0, by.Length); + } + } + catch (Exception Ex) + { + string error = Ex.Message; + } + } + #endregion + + + private bool _alreadyDispose = false; + + #region 构造函数 + public FileHelper() + { + // + // TODO: 在此处添加构造函数逻辑 + // + } + ~FileHelper() + { + Dispose(); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_alreadyDispose) return; + _alreadyDispose = true; + } + #endregion + + #region IDisposable 成员 + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion +} diff --git a/Tiobon.Core.Common/DB/Dapper/Utilities/HttpContext.cs b/Tiobon.Core.Common/DB/Dapper/Utilities/HttpContext.cs new file mode 100644 index 00000000..c17ef6e2 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Utilities/HttpContext.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Http; + +namespace Tiobon.Core.Common.DB.Dapper.Utilities; + +public static class HttpContext +{ + private static IHttpContextAccessor _accessor; + + public static Microsoft.AspNetCore.Http.HttpContext Current => _accessor.HttpContext; + + internal static void Configure(IHttpContextAccessor accessor) + { + _accessor = accessor; + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Utilities/StringHelper.cs b/Tiobon.Core.Common/DB/Dapper/Utilities/StringHelper.cs new file mode 100644 index 00000000..bea94565 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Utilities/StringHelper.cs @@ -0,0 +1,15 @@ +using System; + +namespace Tiobon.Core.Common.DB.Dapper.Utilities; +public class StringHelper +{ + public static string Id + { + get + { + Guid id = Guid.NewGuid(); + return id.ToString(); + //return GuidRandomGenerator.Instance.Generate(); + } + } +} diff --git a/Tiobon.Core.Common/DB/Dapper/Utilities/UtilConvert.cs b/Tiobon.Core.Common/DB/Dapper/Utilities/UtilConvert.cs new file mode 100644 index 00000000..c94a5929 --- /dev/null +++ b/Tiobon.Core.Common/DB/Dapper/Utilities/UtilConvert.cs @@ -0,0 +1,33 @@ +using System; +namespace Tiobon.Core.Common.DB.Dapper; + +/// +/// +/// +public static class UtilConvert +{ + /// + /// + /// + /// + /// + public static string ObjToString(this object thisValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return ""; + } + /// + /// + /// + /// + /// + public static bool ObjToBool(this object thisValue) + { + bool reval = false; + if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } +} diff --git a/Tiobon.Core.Common/DB/DbSql/DbInsert.cs b/Tiobon.Core.Common/DB/DbSql/DbInsert.cs index a48593a6..2bf925e3 100644 --- a/Tiobon.Core.Common/DB/DbSql/DbInsert.cs +++ b/Tiobon.Core.Common/DB/DbSql/DbInsert.cs @@ -84,6 +84,13 @@ namespace Tiobon.Core.Common FormatValue(fieldName.ToUpper(), s); } + public void Values(string fieldName, string value) + { + string s = "N'{0}'"; + s = string.Format(s, value); + + FormatValue(fieldName.ToUpper(), s); + } public void Values(string fieldName, Guid value) { string value1 = value.ToString(); @@ -263,7 +270,7 @@ namespace Tiobon.Core.Common //create_by var createBy = UserContext.Current.User_Id; - Values("CreatedBy", createBy); + Values("CreatedBy", createBy); //create_date DateTime createDate = DateTime.Now; diff --git a/Tiobon.Core.Common/Seed/FrameSeed.cs b/Tiobon.Core.Common/Seed/FrameSeed.cs index aac820ba..d022baf9 100644 --- a/Tiobon.Core.Common/Seed/FrameSeed.cs +++ b/Tiobon.Core.Common/Seed/FrameSeed.cs @@ -1,4 +1,4 @@ -using SimpleDapper; +using Tiobon.Core.Common.DB.Dapper; using SqlSugar; using System.Text; diff --git a/Tiobon.Core.Common/Tiobon.Core.Common.csproj b/Tiobon.Core.Common/Tiobon.Core.Common.csproj index 5839e741..e8d06175 100644 --- a/Tiobon.Core.Common/Tiobon.Core.Common.csproj +++ b/Tiobon.Core.Common/Tiobon.Core.Common.csproj @@ -9,11 +9,14 @@ + + + @@ -22,6 +25,7 @@ + @@ -32,6 +36,7 @@ + @@ -49,10 +54,4 @@ - - - ..\Lib\SimpleDapper.dll - - - diff --git a/Tiobon.Core.Tests/Common_Test/DbAccess_Should.cs b/Tiobon.Core.Tests/Common_Test/DbAccess_Should.cs index 75d9404e..2061b010 100644 --- a/Tiobon.Core.Tests/Common_Test/DbAccess_Should.cs +++ b/Tiobon.Core.Tests/Common_Test/DbAccess_Should.cs @@ -1,5 +1,5 @@ using System.Data; -using SimpleDapper; +using Tiobon.Core.Common.DB.Dapper; using Tiobon.Core.Model.Models; using Xunit; diff --git a/Tiobon.Core.Tests/Tiobon.Core.Tests.csproj b/Tiobon.Core.Tests/Tiobon.Core.Tests.csproj index 053d455e..d6d38e07 100644 --- a/Tiobon.Core.Tests/Tiobon.Core.Tests.csproj +++ b/Tiobon.Core.Tests/Tiobon.Core.Tests.csproj @@ -32,12 +32,6 @@ - - - ..\Lib\SimpleDapper.dll - - - Always