Repository: overtly/core-data Branch: master Commit: 51cd7e43c9d1 Files: 125 Total size: 508.3 KB Directory structure: gitextract_jy2ln_jg/ ├── .gitattributes ├── .gitignore ├── README.md ├── core-data.sln └── src/ ├── .vs/ │ ├── Sodao.Core/ │ │ └── v15/ │ │ └── Server/ │ │ └── sqlite3/ │ │ └── storage.ide │ └── config/ │ └── applicationhost.config ├── Overt.Core.Data/ │ ├── Attribute/ │ │ └── SubmeterAttribute.cs │ ├── Constants.cs │ ├── Contract/ │ │ ├── IBaseRepository.cs │ │ └── IPropertyAssist.cs │ ├── DataContext/ │ │ ├── DataContext.cs │ │ └── DataSettings.cs │ ├── Enums/ │ │ ├── DataCustomType.cs │ │ ├── DatabaseType.cs │ │ └── FieldSortType.cs │ ├── Expressions/ │ │ ├── ExpressionHelper.cs │ │ ├── Extensions/ │ │ │ └── Expression.Extensions.cs │ │ └── SqlExpression/ │ │ ├── Resolve/ │ │ │ ├── Basic/ │ │ │ │ ├── BaseSqlExpression.cs │ │ │ │ └── ISqlExpression.cs │ │ │ ├── BinarySqlExpression.cs │ │ │ ├── ConstantSqlExpression.cs │ │ │ ├── LambdaSqlExpression.cs │ │ │ ├── ListInitSqlExpression.cs │ │ │ ├── MemberSqlExpression.cs │ │ │ ├── MethodCallSqlExpression.cs │ │ │ ├── NewArraySqlExpression.cs │ │ │ ├── NewSqlExpression.cs │ │ │ └── UnarySqlExpression.cs │ │ ├── SqlExpression.cs │ │ ├── SqlExpressionCompiler.cs │ │ ├── SqlExpressionCore.cs │ │ ├── SqlExpressionFingerprint.cs │ │ ├── SqlExpressionProvider.cs │ │ └── SqlGenerate.cs │ ├── Extensions/ │ │ ├── Dapper.Async.Extensions.cs │ │ ├── Dapper.Extensions.cs │ │ ├── EntityDefinition.Extensions.cs │ │ ├── Expression.Extensions.cs │ │ ├── SqlAlias.Extensions.cs │ │ └── Table.Extensions.cs │ ├── Overt.Core.Data.csproj │ ├── Overt.Core.Data.xml │ ├── Params/ │ │ └── OrderByField.cs │ └── Repository/ │ ├── BaseRepository.cs │ └── PropertyAssist.cs ├── core/ │ ├── Overt.Core.DataConsole/ │ │ ├── Overt.Core.DataConsole.csproj │ │ ├── Program.cs │ │ └── appsettings.json │ ├── Overt.Core.Test/ │ │ ├── ApplicationTest.cs │ │ ├── BaseTest.cs │ │ ├── Overt.Core.Test.csproj │ │ └── appsettings.json │ ├── Overt.User.Application/ │ │ ├── AutoMapperProfiles.cs │ │ ├── Constracts/ │ │ │ ├── ISubDbUser2Service.cs │ │ │ ├── ISubDbUserService.cs │ │ │ ├── ISubUserService.cs │ │ │ ├── IUserLongIdService.cs │ │ │ └── IUserService.cs │ │ ├── Extensions/ │ │ │ └── ModelValidationExtensions.cs │ │ ├── Models/ │ │ │ ├── UserModel.cs │ │ │ ├── UserPostModel.cs │ │ │ └── UserSearchModel.cs │ │ ├── Overt.User.Application.csproj │ │ ├── ServiceCollectionExtensions.cs │ │ └── Services/ │ │ ├── SubDbUser2Service.cs │ │ ├── SubDbUserService.cs │ │ ├── SubUserService.cs │ │ ├── UserLongIdService.cs │ │ └── UserService.cs │ └── Overt.User.Domain/ │ ├── Contracts/ │ │ ├── ISubDbUser2Repository.cs │ │ ├── ISubDbUserRepository.cs │ │ ├── ISubUserRepository.cs │ │ ├── IUserLongIdRepository.cs │ │ └── IUserRepository.cs │ ├── Entities/ │ │ ├── SubDbUser2Entity.cs │ │ ├── SubDbUserEntity.cs │ │ ├── SubUserEntity.cs │ │ ├── UserEntity.cs │ │ └── UserLongIdEntity.cs │ ├── Overt.User.Domain.csproj │ ├── Repositories/ │ │ ├── SubDbUser2Repository.cs │ │ ├── SubDbUserRepository.cs │ │ ├── SubUserRepository.cs │ │ ├── UserLongIdRepository.cs │ │ └── UserRepository.cs │ └── ServiceCollectionExtensions.cs └── net46/ ├── Overt.Core.DataConsole/ │ ├── App.config │ ├── AutofacContainer.cs │ ├── Overt.Core.DataConsole.csproj │ ├── Program.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ └── packages.config ├── Overt.User.Application/ │ ├── AutoMapperProfiles.cs │ ├── AutofacExtensions.cs │ ├── Constracts/ │ │ ├── ISubDbUser2Service.cs │ │ ├── ISubDbUserService.cs │ │ ├── ISubUserService.cs │ │ └── IUserService.cs │ ├── Extensions/ │ │ └── ModelValidationExtensions.cs │ ├── Models/ │ │ ├── UserModel.cs │ │ ├── UserPostModel.cs │ │ └── UserSearchModel.cs │ ├── Overt.User.Application.csproj │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Services/ │ │ ├── SubDbUser2Service.cs │ │ ├── SubDbUserService.cs │ │ ├── SubUserService.cs │ │ └── UserService.cs │ └── packages.config └── Overt.User.Domain/ ├── AutofacExtensions.cs ├── Contracts/ │ ├── ISubDbUser2Repository.cs │ ├── ISubDbUserRepository.cs │ ├── ISubUserRepository.cs │ └── IUserRepository.cs ├── Entities/ │ ├── SubDbUser2Entity.cs │ ├── SubDbUserEntity.cs │ ├── SubUserEntity.cs │ └── UserEntity.cs ├── Overt.User.Domain.csproj ├── Properties/ │ └── AssemblyInfo.cs ├── Repositories/ │ ├── SubDbUser2Repository.cs │ ├── SubDbUserRepository.cs │ ├── SubUserRepository.cs │ └── UserRepository.cs └── packages.config ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ================================================ FILE: .gitignore ================================================ # #ignore thumbnails created by windows Thumbs.db #Ignore files build by Visual Studio *.obj *.exe *.pdb *.user *.aps *.pch *.vspscc *_i.c *_p.c *.ncb *.suo *.tlb *.tlh *.bak *.cache *.ilk *.log [Bb]in [Dd]ebug*/ *.sbr obj/ [Rr]elease*/ _ReSharper*/ [Tt]est[Rr]esult* packages log.txt /.vs /src/core/Overt.Core.DataConsole/appsettings.txt /src/net46/Overt.Core.DataConsole/App.txt ================================================ FILE: README.md ================================================ 如有疑问可直接加QQ:2292709323,微信:yaofengv,联系 # Overt.Core.Data ### 项目层次说明 > Overt.Core.Data v2.2.3 > 基于dapper封装的expression om,让CURD更简单 #### 1. 项目目录 ``` |-Attrubute 特性 | |-SubmeterAttrubute.cs 分表标识特性 (已废弃) | |-Contract 契约层 | |-IPropertyAssist.cs 统一的接口定义文件,所有的数据层接口均继承 | |-IBaseRepository.cs 基础接口类,包含增删改查,以及继承IDbRepository | |-DataContext 数据库连接层 | |-DataContext.cs 数据库连接工厂类 | |-DataContextConfig.cs 数据库配置信息获取类 | |-Enums 枚举 | |-DatabaseType.cs 数据库类型 | |-FieldSortType.cs Asc / Desc | |-Expressions 表达式解析器(略) | |-Extensions Dapper扩展 | |-Dapper.Extension.cs 扩展类 | |-Params 参数实体 | |-OrderByField.cs 排序类 | |-Repositry 契约实现层 | |-PropertyAssist.cs 抽象类,继承IPropertyAssist,virtual | |-BaseRepository.cs 抽象类,实现 IBaseRepository,virtual ``` #### 2. 版本及支持 > * Nuget版本:V2.2.3 > * 框架支持: Framework4.6.1 - NetStandard 2.0 > * 数据库支持:MySql / SqlServer / SQLite [使用详见下文] #### 3. 项目依赖 > * Framework 4.6.1 ``` Dapper 2.0.35 MySql.Data 8.0.20 System.Data.SqlClient 4.8.1 System.Data.SQLite 1.0.113.1 System.ComponentModel.DataAnnotations 4.7.0 ``` > * NetStandard 2.0 ``` Dapper 2.0.35 Microsoft.Data.Sqlite 3.1.5 MySql.Data 8.0.20 System.Data.SqlClient 4.8.1 System.ComponentModel.DataAnnotations 4.7.0 Microsoft.Extensions.Configuration 2.0.0 ``` ### 使用 #### 1. 配置信息 > * 支持 IConfiguration 对象注入 > * 支持默认配置文件appsettings.json > * 支持环境变量,或者使用外部的第三方配置中心(appolo),最终还是依赖于微软自身Configuration > * Core(DbType=MySql|SqlServer|SQLite): ``` [mysql]: DataSource=127.0.0.1;Database=TestDb;uid=root;pwd=123456;Allow Zero Datetime=True;DbType=MySql [sqlserver]: Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123456;DbType=SqlServer [sqlite]: Data Source=testdb.db;DbType=SQLite; // 默认使用App_Data目录 ``` > * Framework: 正常的连接字符串,使用ProviderName来区分数据库类型 #### 2. Nuget包引用 ``` Install-Package Overt.Core.Data -Version 2.2.3 ``` #### 3. 约定 > * 服务层契约需继承IBaseRepository<> > * 服务层契约实现实现自定义契约,并继承BaseRepository > * 外部执行Sql使用 BaseRepository 中 Execute 方法 内部管理连接 ``` return await Execute(async (connection) => { // 执行方法 }, true); ``` #### 4. 分表实现 ``` IBaseRepository的实现 // 自定义重写TableNameFunc // 使用GetTableName()可获取到实际的表名 // 内置方法均从上述方法中获取表名,默认表名为实体定义的[Table("主表名")] public override Func TableNameFunc => () => { var tableName = $"{GetMainTableName()}_{DateTime.Now.ToString("yyyyMMdd")}"; return tableName; }; // 重写创建表的脚本,可在调用过程中,自动创建表,一般用于动态分表 public override Func CreateScriptFunc => (tableName) => { return "创建表的Sql脚本"; // 将在增删改操作中执行,查询操作中,表不存在则直接返回空数据 } ``` #### 5. 分库实现 > * 连接字符串配置中以key - value 模式定义,key使用默认的读写分离关键字【master / secondary】表示写入连接字符串和读取连接字符串 > * 前缀添加 xxx.即可简单定义不同数据库,代码中,对于IBaseRepository的实现,在构造函数中直接常量定义 xxx,如下所示 ``` using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using System.Linq; using Microsoft.Extensions.Configuration; namespace Overt.User.Domain.Repositories { public class UserRepository : BaseRepository, IUserRepository { public UserRepository(IConfiguration configuration) : base(configuration, "xxx") // dbStoreKey 可用于不同数据库切换,连接字符串key前缀:xxx.master xxx.secondary { } } } ``` #### 6. 事务实现 > * Framework: 使用TransactionScope > * DotNetCore: 使用TransactionScope ``` // Service层 public async Task ExecuteInTransactionAsync() { // 分布式事务 // 异步中需要增加该参数:TransactionScopeAsyncFlowOption.Enabled using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { var result = false; try { result = await _userRepository.AddAsync(new UserEntity()); result &= await _subUserRepository.AddAsync(new SubUserEntity()); scope.Complete(); return result; } catch { // logger } return false; } } ``` #### 7. Lambda表达式支持 > * == > * ! > * != > * null > * In > * Equals > * Contains > * !Contains > * StartWith > * EndWith > * && > * || > * & > * | ##### 不支持案例 ~~var list = _repository.GetList(1, 1, oo=>"test".Contains(oo.UserName));~~ ~~var list = _repository.GetList(1, 1, oo=>oo.UserName.IndexOf("abc") > -1);~~ #### 8. 案例使用 * 添加记录 ``` // 单条记录插入 _repository.Add(obj); // 多条记录批量插入,建议不要超过500条 _repository.Add(obj0, obj1, ...); ``` * 修改记录 ``` // 修改整份数据 _repository.Set(obj); // 修改部分字段(基于字典对象) var setDic = new Dictionary() { { "UserName", "1" } }; _repository.Set(() => setDic, oo => oo.UserId == 1); // 修改部分字段(基于匿名对象) var setObj = new { UserName = "1" }; _repository.Set(() => setObj, oo => oo.UserId == 1); // 修改值类型字段进行增减,比如数量的增减,年龄的增减等 _repository.Incr("Age", 1, oo => oo.UserId == 1); ``` * 删除记录 ``` _repository.Delete(oo => oo.UserId == 1); ``` * 单记录查询 ``` var entity = _repository.Get(oo => oo.UserId == 1); ``` * 列表记录查询 ``` var ary = new string[]{ "1", "2" }; var list = _repository.GetList(1, 1, oo=>ary.Contains(oo.UserId)); var ary = new List(){ "1", "2" }; var list = _repository.GetList(1, 1, oo=>ary.Contains(oo.UserId)); var key = "abc"; var list = _repository.GetList(1, 1, oo=>oo.UserName.Contains(key)); var list = _repository.GetList(1, 1, oo=>oo.UserName.Equals(key)); var list = _repository.GetList(1, 1, oo=>oo.UserName.Contains("abc")); var list = _repository.GetList(1, 1, oo=>oo.UserName.StartWith("abc")); var list = _repository.GetList(1, 1, oo=>oo.UserName.EndWith("abc")); var list = _repository.GetList(1, 1, oo=>ary.Contains(oo.UserId) && !IsSex); var list = _repository.GetList(1, 1, oo=>oo.UserName != null); var list = _repository.GetList(1, 1, oo=>oo.UserName == null); ``` #### 9. 更新说明 - 2023-02-23 v2.2.3 > 1. Insert 支持long自增 - 2022-11-22 v2.2.2 > 1. GetMainTableName开放使用 - 2022-09-29 v2.2.1 > 1. 增加支持& | - 2021-12-29 v2.2.0 > 1. 支持PG数据库 感谢 @liuzhenbao0505 > 2. 修正 StartWith EndWith 的拼接错误 @Zhang-Pengyuan - 2021-03-26 v2.1.4 > 1. 将基础方法Execute变更为virtual,可允许重写 - 2021-02-24 v2.1.3 > 1. 基础方法增加根据某个字段增减数据的方法:IncrAsync(string field, TValue value, Expression> whereExpress) - 2021-01-14 v2.0.1 > 1. 修复多数据库情况下并发查询切换数据库导致连接字符串混乱的问题 > 2. Repository层回执SQL脚本的位置调整,防止数据库执行异常无法获知实际的SQL脚本 - 2020-06-30 v2.0.0 > 1. 升级底层依赖的驱动:Dapper、SqlClient、MySql、SQLite > 2. 去除原有老版本中使用的Transaction属性,全部统一使用TransactionScope实现事务业务。PS:DotNetCore中的事务只支持单服务器数据,Framework支持分布式数据库!!! > 3. 如需兼容老版本的的代码,请使用v1.x.x版本的驱动或者代码 --- ================================================ FILE: core-data.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Overt.Core.Data", "src\Overt.Core.Data\Overt.Core.Data.csproj", "{8B1B556F-1D2C-49D2-8CB9-F9D2C7080786}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{9DD521BB-DEE6-40A1-B042-EA89EB889546}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net462", "net462", "{B159908C-321A-45AF-998B-C431D7D02070}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Overt.User.Domain", "src\net46\Overt.User.Domain\Overt.User.Domain.csproj", "{F34F6B13-B939-47A3-827E-E7015FD1B434}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Overt.Core.DataConsole", "src\core\Overt.Core.DataConsole\Overt.Core.DataConsole.csproj", "{D2349F1D-B2AB-40EE-980F-0F0AB8E3F970}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Overt.User.Application", "src\core\Overt.User.Application\Overt.User.Application.csproj", "{5D9B9D31-33CA-4378-AA58-D788C7EBD4CF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Overt.User.Domain", "src\core\Overt.User.Domain\Overt.User.Domain.csproj", "{0558E8F8-BB9E-4C62-A8D0-03011ED77284}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Overt.User.Application", "src\net46\Overt.User.Application\Overt.User.Application.csproj", "{5ADB2A33-42E4-484B-997E-3F8B942539E4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Overt.Core.DataConsole", "src\net46\Overt.Core.DataConsole\Overt.Core.DataConsole.csproj", "{F92F116B-A438-447B-9475-4D62AF197742}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8B1B556F-1D2C-49D2-8CB9-F9D2C7080786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B1B556F-1D2C-49D2-8CB9-F9D2C7080786}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B1B556F-1D2C-49D2-8CB9-F9D2C7080786}.Release|Any CPU.ActiveCfg = Release|Any CPU {8B1B556F-1D2C-49D2-8CB9-F9D2C7080786}.Release|Any CPU.Build.0 = Release|Any CPU {F34F6B13-B939-47A3-827E-E7015FD1B434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F34F6B13-B939-47A3-827E-E7015FD1B434}.Debug|Any CPU.Build.0 = Debug|Any CPU {F34F6B13-B939-47A3-827E-E7015FD1B434}.Release|Any CPU.ActiveCfg = Release|Any CPU {F34F6B13-B939-47A3-827E-E7015FD1B434}.Release|Any CPU.Build.0 = Release|Any CPU {D2349F1D-B2AB-40EE-980F-0F0AB8E3F970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D2349F1D-B2AB-40EE-980F-0F0AB8E3F970}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2349F1D-B2AB-40EE-980F-0F0AB8E3F970}.Release|Any CPU.ActiveCfg = Release|Any CPU {D2349F1D-B2AB-40EE-980F-0F0AB8E3F970}.Release|Any CPU.Build.0 = Release|Any CPU {5D9B9D31-33CA-4378-AA58-D788C7EBD4CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5D9B9D31-33CA-4378-AA58-D788C7EBD4CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5D9B9D31-33CA-4378-AA58-D788C7EBD4CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5D9B9D31-33CA-4378-AA58-D788C7EBD4CF}.Release|Any CPU.Build.0 = Release|Any CPU {0558E8F8-BB9E-4C62-A8D0-03011ED77284}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0558E8F8-BB9E-4C62-A8D0-03011ED77284}.Debug|Any CPU.Build.0 = Debug|Any CPU {0558E8F8-BB9E-4C62-A8D0-03011ED77284}.Release|Any CPU.ActiveCfg = Release|Any CPU {0558E8F8-BB9E-4C62-A8D0-03011ED77284}.Release|Any CPU.Build.0 = Release|Any CPU {5ADB2A33-42E4-484B-997E-3F8B942539E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5ADB2A33-42E4-484B-997E-3F8B942539E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {5ADB2A33-42E4-484B-997E-3F8B942539E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ADB2A33-42E4-484B-997E-3F8B942539E4}.Release|Any CPU.Build.0 = Release|Any CPU {F92F116B-A438-447B-9475-4D62AF197742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F92F116B-A438-447B-9475-4D62AF197742}.Debug|Any CPU.Build.0 = Debug|Any CPU {F92F116B-A438-447B-9475-4D62AF197742}.Release|Any CPU.ActiveCfg = Release|Any CPU {F92F116B-A438-447B-9475-4D62AF197742}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {F34F6B13-B939-47A3-827E-E7015FD1B434} = {B159908C-321A-45AF-998B-C431D7D02070} {D2349F1D-B2AB-40EE-980F-0F0AB8E3F970} = {9DD521BB-DEE6-40A1-B042-EA89EB889546} {5D9B9D31-33CA-4378-AA58-D788C7EBD4CF} = {9DD521BB-DEE6-40A1-B042-EA89EB889546} {0558E8F8-BB9E-4C62-A8D0-03011ED77284} = {9DD521BB-DEE6-40A1-B042-EA89EB889546} {5ADB2A33-42E4-484B-997E-3F8B942539E4} = {B159908C-321A-45AF-998B-C431D7D02070} {F92F116B-A438-447B-9475-4D62AF197742} = {B159908C-321A-45AF-998B-C431D7D02070} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {63C9732F-DFAB-438C-B2F2-7C0399EF8701} EndGlobalSection EndGlobal ================================================ FILE: src/.vs/config/applicationhost.config ================================================ 
================================================ FILE: src/Overt.Core.Data/Attribute/SubmeterAttribute.cs ================================================ using System; namespace Overt.Core.Data { /// /// 分表标识 /// [Obsolete("请使用TableNameFunc")] public class SubmeterAttribute : Attribute { /// /// 16进制位数 /// 1 16 /// 2 256 /// 3 4096 /// ... /// public int Bit { get; set; } } } ================================================ FILE: src/Overt.Core.Data/Constants.cs ================================================ namespace Overt.Core.Data { internal class Constants { public static class MSSQLVersion { /// /// SQLServer2012版本 /// public const int SQLServer2012Bv = 11; } } } ================================================ FILE: src/Overt.Core.Data/Contract/IBaseRepository.cs ================================================ using System; using System.Collections.Generic; using System.Data; using System.Linq.Expressions; using System.Threading.Tasks; namespace Overt.Core.Data { /// /// 接口 /// public interface IBaseRepository : IPropertyAssist where TEntity : class, new() { #region Sync Method /// /// 获取主表名 /// /// string GetMainTableName(); /// /// 获取表名:内部会调用TableNameFunc 从主库中查询 /// /// string GetTableName(); /// /// 获取表名:以Submeter分表位数获取表名 /// /// /// [Obsolete("请使用GetTableName")] string GetTableName(string key); /// /// 是否存在表 /// /// /// /// bool IsExistTable(string tableName, bool isMaster = true); /// /// 是否存在字段 /// /// /// /// /// bool IsExistField(string tableName, string fieldName, bool isMaster = true); /// /// 添加 /// /// 数据实体 /// 是否赋值最后一次的自增ID /// 添加后的数据实体 bool Add(TEntity entity, bool returnLastIdentity = false); /// /// 批量添加 /// /// 数据实体 /// bool bool Add(params TEntity[] entities); /// /// 删除 /// /// 删除条件 /// 是否成功 bool Delete(Expression> expression); /// /// 更新 /// /// 数据实体 /// x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } /// 是否成功 bool Set(TEntity entity, Expression> fields = null); /// /// 根据字段修改 /// /// 修改字段表达式 object =>dynamic 是一个匿名类 /// 条件表达式 /// 是否成功 bool Set(Expression> setExpress, Expression> whereExpress); /// /// 根据条件 在原字段上增减数据 /// /// 增减的字段 /// 增减的值 /// 条件表达式 /// bool Incr(string field, TValue value, Expression> whereExpress) where TValue : struct; /// /// 获取一条数据 /// /// 查询条件 /// 按字段返回 /// 是否主从 /// 实体 TEntity Get(Expression> expression, Expression> fieldExpressison = null, bool isMaster = false); /// /// 获取列表 /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// IEnumerable GetList(int page, int rows, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields); /// /// 获取列表 Offset /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// IEnumerable GetOffsets(int offset, int size, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields); /// /// 获取数量 /// /// 条件表达式 /// 是否主从 /// int Count(Expression> expression = null, bool isMaster = false); #endregion #region Async Method /// /// 是否存在表 /// /// /// /// Task IsExistTableAsync(string tableName, bool isMaster = true); /// /// 是否存在字段 /// /// /// /// /// Task IsExistFieldAsync(string tableName, string fieldName, bool isMaster = true); /// /// 异步添加 /// /// /// 是否赋值最后一次的自增ID /// Task AddAsync(TEntity entity, bool returnLastIdentity = false); /// /// 异步批量添加 /// /// 数据实体 /// bool /// Task AddAsync(params TEntity[] entities); /// /// 异步删除 /// /// 删除条件 /// 是否成功 Task DeleteAsync(Expression> expression); /// /// 异步更新 /// /// 数据实体 /// x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } /// 是否成功 Task SetAsync(TEntity entity, Expression> fields = null); /// /// 异步根据字段修改 /// /// 修改字段表达式 /// 条件表达式 /// 是否成功 Task SetAsync(Expression> setExpress, Expression> whereExpress); /// /// 根据条件 在原字段上增减数据 /// /// 增减的字段 /// 增减的值 /// 条件表达式 /// Task IncrAsync(string field, TValue value, Expression> whereExpress) where TValue : struct; /// /// 异步获取一条数据 /// /// 查询条件 /// 按字段返回 /// 是否主从 /// 实体 Task GetAsync(Expression> expression, Expression> fieldExpressison = null, bool isMaster = false); /// /// 异步获取列表 /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// Task> GetListAsync(int page, int rows, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields); /// /// 异步获取列表 Offset /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// Task> GetOffsetsAsync(int offset, int size, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields); /// /// 异步获取数量 /// /// 条件表达式 /// 是否主从 /// Task CountAsync(Expression> expression = null, bool isMaster = false); #endregion } } ================================================ FILE: src/Overt.Core.Data/Contract/IPropertyAssist.cs ================================================ using System; using System.Data; #if !ASP_NET_CORE using System.Configuration; #endif namespace Overt.Core.Data { /// /// 通用的接口 /// public interface IPropertyAssist { #region DbStoreKey /// /// 数据库key /// string DbStoreKey { get; set; } #endregion #region TableName /// /// 表名生成方法 /// Func TableNameFunc { get; set; } /// /// 创建表的sql语句 /// 参数:表名 /// Func CreateScriptFunc { get; set; } #endregion #region ConnectionString /// /// 连接方法创建 /// #if ASP_NET_CORE Func ConnectionFunc { get; set; } #else Func ConnectionFunc { get; set; } #endif #endregion #region Connection /// /// 打开连接 /// /// IDbConnection OpenConnection(bool isMaster = false); #endregion #region ExecuteScript /// /// 执行的sql脚本 /// string ExecuteScript { get; set; } #endregion } } ================================================ FILE: src/Overt.Core.Data/DataContext/DataContext.cs ================================================ #if ASP_NET_CORE using Microsoft.Extensions.Configuration; using System.IO; #endif using MySql.Data.MySqlClient; using System; #if !ASP_NET_CORE using System.Configuration; #endif using System.Data; using System.Data.Common; using System.Data.SqlClient; namespace Overt.Core.Data { /// /// 数据库连接工具类 /// public class DataContext : IDisposable { #region Private Members readonly bool _isMaster; readonly string _dbStoreKey; DbProviderFactory _dbFactory; #if ASP_NET_CORE readonly IConfiguration _configuration; readonly Func _connectionFunc; #else readonly Func _connectionFunc; #endif #endregion #region Public Members /// /// 连接对象 /// public IDbConnection DbConnection { get; private set; } #endregion #region Constructor #if ASP_NET_CORE /// /// 构造函数 /// /// if null 则从appsettings.json中获取 /// 是否从库 /// 存储字符串标识 /// 连接字符串Func public DataContext(IConfiguration configuration, bool isMaster = false, string dbStoreKey = "", Func connectionFunc = null) { _configuration = configuration; _isMaster = isMaster; _dbStoreKey = dbStoreKey; _connectionFunc = connectionFunc; // 打开连接 CreateAndOpen(); } #else /// /// 构造函数 /// /// 是否从库 /// 存储字符串标识 /// 连接字符串Func public DataContext(bool isMaster = false, string dbStoreKey = "", Func connectionFunc = null) { _isMaster = isMaster; _dbStoreKey = dbStoreKey; _connectionFunc = connectionFunc; // 打开连接 CreateAndOpen(); } #endif #endregion #region Private Method /// /// 打开链接 /// private void CreateAndOpen() { var connectionString = string.Empty; var settings = DataSettings.Default; // 获取连接 #if ASP_NET_CORE var connectionSetting = settings.Get(_configuration, _isMaster, _dbStoreKey, _connectionFunc); connectionString = connectionSetting.Item1; _dbFactory = GetFactory(connectionSetting.Item2); #else var connectionSetting = settings.Get(_isMaster, _dbStoreKey, _connectionFunc); connectionString = connectionSetting?.ConnectionString; _dbFactory = DbProviderFactories.GetFactory(connectionSetting?.ProviderName); #endif if (string.IsNullOrEmpty(connectionString)) throw new Exception($"连接字符串获取为空,请检查Repository是否指定了dbStoreKey以及检查配置文件是否存在"); DbConnection = _dbFactory.CreateConnection(); DbConnection.ConnectionString = connectionString; if (DbConnection.State != ConnectionState.Open) DbConnection.Open(); } /// /// 获取Factory /// /// /// private DbProviderFactory GetFactory(DatabaseType dbType) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: return SqlClientFactory.Instance; case DatabaseType.MySql: return MySqlClientFactory.Instance; case DatabaseType.PostgreSQL: return Npgsql.NpgsqlFactory.Instance; case DatabaseType.SQLite: #if ASP_NET_CORE AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data")); return Microsoft.Data.Sqlite.SqliteFactory.Instance; #else return System.Data.SQLite.SQLiteFactory.Instance; #endif } return null; } #endregion #region Public Method /// /// 垃圾回收 /// public void Dispose() { if (DbConnection == null) return; try { DbConnection.Dispose(); } catch { } } #endregion } } ================================================ FILE: src/Overt.Core.Data/DataContext/DataSettings.cs ================================================ using System; using System.Text.RegularExpressions; #if ASP_NET_CORE using Microsoft.Extensions.Configuration; #else using System.Configuration; #endif namespace Overt.Core.Data { /// /// 连接配置信息获取 /// 1. master / secondary /// 2. xx.master / xx.secondary /// public class DataSettings { #region Static Private Members const string _connNmeOfMaster = "master"; const string _connNameOfSecondary = "secondary"; const string _connNameOfPoint = "."; #endregion #region Single Instance static readonly object lockHelper = new object(); static volatile DataSettings _Default; /// /// 单例模式 /// static public DataSettings Default { get { if (_Default == null) { lock (lockHelper) { _Default = _Default ?? new DataSettings(); } } return _Default; } } #endregion #region Construct Method /// /// 构造函数 /// private DataSettings() { } #endregion #region Public Method #if ASP_NET_CORE /// /// 获取连接字符串 /// /// /// /// /// /// public (string, DatabaseType) Get(IConfiguration configuration, bool isMaster, string dbStoreKey, Func connectionFunc = null) { string connectionString; if (connectionFunc != null) { connectionString = connectionFunc.Invoke(isMaster); return ResolveConnectionString(connectionString); } if (configuration == null) { throw new Exception($"请注入IConfiguration"); //configuration = new ConfigurationBuilder() // .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) // .AddJsonFile("appsettings.json") // .Build(); } var connectionKey = GetKey(isMaster, dbStoreKey); connectionString = configuration.GetConnectionString(connectionKey); if (string.IsNullOrEmpty(connectionString) && !isMaster) { // 从库转主库 connectionKey = GetKey(true, dbStoreKey); connectionString = configuration.GetConnectionString(connectionKey); } return ResolveConnectionString(connectionString); } /// /// 解析 /// /// /// /// private (string, DatabaseType) ResolveConnectionString(string connectionString, string param = "DbType") { var dbTypeRegex = new Regex($@"(^|;){param}=(?[A-Za-z]+)(;|$)"); var m = dbTypeRegex.Match(connectionString); var dbTypeString = m?.Groups["dbtype"].Value; DatabaseType dbType; var parseResult = Enum.TryParse(dbTypeString, out dbType); if (!parseResult) dbType = DatabaseType.MySql; connectionString = Regex.Replace(connectionString, $@"{param}=([A-Za-z]+)(;|$)", ""); return (connectionString, dbType); } #else /// /// 获取连接字符串 /// /// /// /// /// public ConnectionStringSettings Get(bool isMaster, string dbStoreKey, Func connectionFunc = null) { if (connectionFunc != null) return connectionFunc.Invoke(isMaster); var connectionKey = GetKey(isMaster, dbStoreKey); var connectionSetting = ConfigurationManager.ConnectionStrings[connectionKey]; if (string.IsNullOrEmpty(connectionSetting?.ConnectionString) && !isMaster) { connectionKey = GetKey(true, dbStoreKey); connectionSetting = ConfigurationManager.ConnectionStrings[connectionKey]; } return connectionSetting; } #endif #endregion #region Private Method /// /// 获取 /// /// /// 不能包含点 /// private string GetKey(bool isMaster = false, string dbStoreKey = "") { var connNameOfPrefix = string.IsNullOrWhiteSpace(dbStoreKey) ? "" : $"{dbStoreKey}{_connNameOfPoint}"; string connName; if (isMaster) connName = $"{connNameOfPrefix}{_connNmeOfMaster}"; else connName = $"{connNameOfPrefix}{_connNameOfSecondary}"; return connName; } #endregion } } ================================================ FILE: src/Overt.Core.Data/Enums/DataCustomType.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Overt.Core.Data { /// /// 自定义字段类型 /// public enum DataCustomType { /// /// 未定义 /// None = 0, /// /// jsonb类型 /// Jsonb = 1, } } ================================================ FILE: src/Overt.Core.Data/Enums/DatabaseType.cs ================================================ namespace Overt.Core.Data { /// /// 数据库类型 /// public enum DatabaseType { /// /// SqlServer /// SqlServer, /// /// >=SqlServer2012 /// GteSqlServer2012, /// /// Mysql /// MySql, /// /// Sqlite /// SQLite, /// /// PostgreSQL /// PostgreSQL } } ================================================ FILE: src/Overt.Core.Data/Enums/FieldSortType.cs ================================================ namespace Overt.Core.Data { /// /// 字段排序类型 /// public enum FieldSortType { /// /// 顺序 /// Asc = 1, /// /// 倒序 /// Desc = 2 } } ================================================ FILE: src/Overt.Core.Data/Expressions/ExpressionHelper.cs ================================================ using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { /// /// 解析为 Dictionary 获取表达式中的key - value /// 只需要有等于号的 /// internal class ExpressionHelper { /// /// 获取参数 /// /// /// public static void Resolve(Expression expression, ref Dictionary dictionary) { if (expression == null) return; if (expression is BinaryExpression) { var binaryExpression = ((BinaryExpression)expression); ResolveBinary(binaryExpression, ref dictionary); } } #region Binary private static void ResolveBinary(BinaryExpression expression, ref Dictionary dictionary) { object left = null, right = null; if (expression.NodeType == ExpressionType.AndAlso || expression.NodeType == ExpressionType.OrElse) { if (expression.Left.IsBooleanComparison()) left = ResolveMemberOrConstant(expression.Left); else Resolve(expression.Left, ref dictionary); if (expression.Right.IsBooleanComparison()) right = ResolveMemberOrConstant(expression.Right); else Resolve(expression.Right, ref dictionary); } else if (expression.NodeType == ExpressionType.Equal) { left = ResolveMemberOrConstant(expression.Left); right = ResolveMemberOrConstant(expression.Right); } if (left != null && !dictionary.ContainsKey(left)) { dictionary.Add(left, right); } } #endregion #region Member / Constant private static object ResolveMemberOrConstant(Expression expression) { if (expression == null) return null; if (expression is MemberExpression) return ResolveMember((MemberExpression)expression); if (expression is ConstantExpression) return ResolveConstant((ConstantExpression)expression); return null; } private static object ResolveMember(MemberExpression expression) { if (expression.Expression != null) { if (expression.Member.DeclaringType.IsNullableType()) return null; if (expression.IsParameterOrConvertAccess()) return expression.Member.Name; } var val = SqlExpressionCompiler.Evaluate(expression); return val; } private static object ResolveConstant(ConstantExpression expression) { return expression.Value; } #endregion } } ================================================ FILE: src/Overt.Core.Data/Expressions/Extensions/Expression.Extensions.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { /// /// 表达式扩展 /// public static class ExpressionExtensions { /// /// 是否为空类型 /// /// /// public static bool IsNullableType(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } /// /// 是否是 /// /// /// /// public static bool IsOrHasGenericInterfaceTypeOf(this Type type, Type genericTypeDefinition) { return (type.GetTypeWithGenericTypeDefinitionOf(genericTypeDefinition) != null) || (type == genericTypeDefinition); } /// /// 是否是泛型 /// /// /// /// public static Type GetTypeWithGenericTypeDefinitionOf(this Type type, Type genericTypeDefinition) { foreach (var t in type.GetInterfaces()) { if (t.IsGenericType && t.GetGenericTypeDefinition() == genericTypeDefinition) { return t; } } var genericType = type.FirstGenericType(); if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition) { return genericType; } return null; } /// /// /// /// /// public static Type FirstGenericType(this Type type) { while (type != null) { if (type.IsGenericType) return type; type = type.BaseType; } return null; } /// /// /// /// /// public static string GetMemberExpression(this MemberExpression m) { return m.Member.Name; } /// /// Determines whether the expression is the parameter. /// /// Returns true if the specified expression is parameter; /// otherwise, false. public static bool IsParameterAccess(Expression e) { return e.CheckExpressionForTypes(new[] { ExpressionType.Parameter }); } /// /// /// /// /// public static bool IsParameterOrConvertAccess(this Expression e) { return e.CheckExpressionForTypes(new[] { ExpressionType.Parameter, ExpressionType.Convert }); } /// /// 检查类型 /// /// /// /// public static bool CheckExpressionForTypes(this Expression e, ExpressionType[] types) { while (e != null) { if (types.Contains(e.NodeType)) { var subUnaryExpr = e as UnaryExpression; var isSubExprAccess = subUnaryExpr?.Operand is IndexExpression; if (!isSubExprAccess) return true; } var binaryExpr = e as BinaryExpression; if (binaryExpr != null) { if (CheckExpressionForTypes(binaryExpr.Left, types)) return true; if (CheckExpressionForTypes(binaryExpr.Right, types)) return true; } var methodCallExpr = e as MethodCallExpression; if (methodCallExpr != null) { for (var i = 0; i < methodCallExpr.Arguments.Count; i++) { if (CheckExpressionForTypes(methodCallExpr.Arguments[i], types)) return true; } if (CheckExpressionForTypes(methodCallExpr.Object, types)) return true; } var unaryExpr = e as UnaryExpression; if (unaryExpr != null) { if (CheckExpressionForTypes(unaryExpr.Operand, types)) return true; } var condExpr = e as ConditionalExpression; if (condExpr != null) { if (CheckExpressionForTypes(condExpr.Test, types)) return true; if (CheckExpressionForTypes(condExpr.IfTrue, types)) return true; if (CheckExpressionForTypes(condExpr.IfFalse, types)) return true; } var memberExpr = e as MemberExpression; e = memberExpr?.Expression; } return false; } /// /// 是否是Boolean /// /// /// public static bool IsBooleanComparison(this Expression e) { if (!(e is MemberExpression)) return false; var m = (MemberExpression)e; if (m.Member.DeclaringType.IsNullableType() && m.Member.Name == "HasValue") //nameof(Nullable.HasValue) return false; return IsParameterAccess(m); } /// /// 获取Value /// /// /// public static object GetValue(this MemberBinding binding) { switch (binding.BindingType) { case MemberBindingType.Assignment: var assign = (MemberAssignment)binding; if (assign.Expression is ConstantExpression constant) return constant.Value; try { return SqlExpressionCompiler.Evaluate(assign.Expression); } catch (Exception ex) { var member = Expression.Convert(assign.Expression, typeof(object)); var lambda = Expression.Lambda>(member); var getter = lambda.Compile(); return getter(); } } return null; } /// /// 获取Fields /// /// /// /// public static string[] GetFieldNames(this Expression> expr) { if (expr == null) return null; if (expr.Body is MemberExpression member) { if (member.Member.DeclaringType.IsAssignableFrom(typeof(T))) return new[] { member.Member.Name }; var array = SqlExpressionCompiler.Evaluate(member); if (array is IEnumerable strEnum) return strEnum.ToArray(); } if (expr.Body is NewExpression newExpr) return newExpr.Arguments.OfType().Select(x => x.Member.Name).ToArray(); if (expr.Body is MemberInitExpression init) return init.Bindings.Select(x => x.Member.Name).ToArray(); if (expr.Body is UnaryExpression unary) { member = unary.Operand as MemberExpression; if (member != null) return new[] { member.Member.Name }; } throw new ArgumentException("Invalid Fields List Expression: " + expr); } /// /// /// /// /// public static List Flatten(this IEnumerable list) { var ret = new List(); if (list == null) return ret; foreach (var item in list) { if (item == null) continue; var arr = item as IEnumerable; if (arr != null && !(item is string)) { ret.AddRange(arr.Cast()); } else { ret.Add(item); } } return ret; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/Basic/BaseSqlExpression.cs ================================================ using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { /// /// BaseSqlExpression /// /// public abstract class BaseSqlExpression : ISqlExpression where T : Expression { /// /// Update /// /// /// /// protected virtual SqlGenerate Update(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Update方法"); } /// /// Select /// /// /// /// protected virtual SqlGenerate Select(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Select方法"); } /// /// Where /// /// /// /// protected virtual SqlGenerate Where(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Where方法"); } /// /// In /// /// /// /// protected virtual SqlGenerate In(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.In方法"); } /// /// OrderBy /// /// /// /// protected virtual SqlGenerate OrderBy(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.OrderBy方法"); } /// /// Max /// /// /// /// protected virtual SqlGenerate Max(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Max方法"); } /// /// Min /// /// /// /// protected virtual SqlGenerate Min(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Min方法"); } /// /// Avg /// /// /// /// protected virtual SqlGenerate Avg(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Avg方法"); } /// /// Count /// /// /// /// protected virtual SqlGenerate Count(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Count方法"); } /// /// Sum /// /// /// /// protected virtual SqlGenerate Sum(T expression, SqlGenerate sqlGenerate) { throw new NotImplementedException("未实现" + typeof(T).Name + "2Sql.Sum方法"); } /// /// Update /// /// /// /// public SqlGenerate Update(Expression expression, SqlGenerate sqlGenerate) { return Update((T)expression, sqlGenerate); } /// /// Select /// /// /// /// public SqlGenerate Select(Expression expression, SqlGenerate sqlGenerate) { return Select((T)expression, sqlGenerate); } /// /// Where /// /// /// /// public SqlGenerate Where(Expression expression, SqlGenerate sqlGenerate) { return Where((T)expression, sqlGenerate); } /// /// In /// /// /// /// public SqlGenerate In(Expression expression, SqlGenerate sqlGenerate) { return In((T)expression, sqlGenerate); } /// /// OrderBy /// /// /// /// public SqlGenerate OrderBy(Expression expression, SqlGenerate sqlGenerate) { return OrderBy((T)expression, sqlGenerate); } /// /// Max /// /// /// /// public SqlGenerate Max(Expression expression, SqlGenerate sqlGenerate) { return Max((T)expression, sqlGenerate); } /// /// Min /// /// /// /// public SqlGenerate Min(Expression expression, SqlGenerate sqlGenerate) { return Min((T)expression, sqlGenerate); } /// /// Avg /// /// /// /// public SqlGenerate Avg(Expression expression, SqlGenerate sqlGenerate) { return Avg((T)expression, sqlGenerate); } /// /// Count /// /// /// /// public SqlGenerate Count(Expression expression, SqlGenerate sqlGenerate) { return Count((T)expression, sqlGenerate); } /// /// Sum /// /// /// /// public SqlGenerate Sum(Expression expression, SqlGenerate sqlGenerate) { return Sum((T)expression, sqlGenerate); } #region Internal Method /// /// 是否是静态集合方法 /// /// /// internal static bool IsStaticArrayMethod(MethodCallExpression m) { return (m.Object == null && m.Arguments.Count == 2); } /// /// 是否是集合方法 /// /// /// internal static bool IsEnumerableMethod(MethodCallExpression m) { return m.Object != null && m.Object.Type.IsOrHasGenericInterfaceTypeOf(typeof(IEnumerable<>)) && m.Object.Type != typeof(string) && m.Arguments.Count == 1; } #endregion } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/Basic/ISqlExpression.cs ================================================ using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { /// /// interface /// public interface ISqlExpression { /// /// Update /// /// /// /// SqlGenerate Update(Expression expression, SqlGenerate sqlGenerate); /// /// Select /// /// /// /// SqlGenerate Select(Expression expression, SqlGenerate sqlGenerate); /// /// Where /// /// /// /// SqlGenerate Where(Expression expression, SqlGenerate sqlGenerate); /// /// In /// /// /// /// SqlGenerate In(Expression expression, SqlGenerate sqlGenerate); /// /// OrderBy /// /// /// /// SqlGenerate OrderBy(Expression expression, SqlGenerate sqlGenerate); /// /// Max /// /// /// /// SqlGenerate Max(Expression expression, SqlGenerate sqlGenerate); /// /// Min /// /// /// /// SqlGenerate Min(Expression expression, SqlGenerate sqlGenerate); /// /// Avg /// /// /// /// SqlGenerate Avg(Expression expression, SqlGenerate sqlGenerate); /// /// Count /// /// /// /// SqlGenerate Count(Expression expression, SqlGenerate sqlGenerate); /// /// Sum /// /// /// /// SqlGenerate Sum(Expression expression, SqlGenerate sqlGenerate); } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/BinarySqlExpression.cs ================================================ using System; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class BinarySqlExpression : BaseSqlExpression { private void OperatorParser(ExpressionType expressionNodeType, int operatorIndex, SqlGenerate sqlGenerate, bool useIs = false) { switch (expressionNodeType) { case ExpressionType.And: sqlGenerate.Sql.Insert(operatorIndex, " & "); break; case ExpressionType.AndAlso: sqlGenerate.Sql.Insert(operatorIndex, " and "); break; case ExpressionType.Equal: if (useIs) sqlGenerate.Sql.Insert(operatorIndex, " is "); else sqlGenerate.Sql.Insert(operatorIndex, " = "); break; case ExpressionType.GreaterThan: sqlGenerate.Sql.Insert(operatorIndex, " >"); break; case ExpressionType.GreaterThanOrEqual: sqlGenerate.Sql.Insert(operatorIndex, " >="); break; case ExpressionType.NotEqual: if (useIs) sqlGenerate.Sql.Insert(operatorIndex, " is not "); else sqlGenerate.Sql.Insert(operatorIndex, " <> "); break; case ExpressionType.Or: sqlGenerate.Sql.Insert(operatorIndex, " | "); break; case ExpressionType.OrElse: sqlGenerate.Sql.Insert(operatorIndex, " or "); break; case ExpressionType.LessThan: sqlGenerate.Sql.Insert(operatorIndex, " < "); break; case ExpressionType.LessThanOrEqual: sqlGenerate.Sql.Insert(operatorIndex, " <= "); break; default: throw new NotImplementedException("未实现的节点类型" + expressionNodeType); } } protected override SqlGenerate Where(BinaryExpression expression, SqlGenerate sqlGenerate) { int leftBracketIndex = -1, rightBracketIndex = -1, signIndex = -1, sqlLength = -1; leftBracketIndex = sqlGenerate.Length; #region 内部内容 if (expression.NodeType == ExpressionType.AndAlso || expression.NodeType == ExpressionType.OrElse) { if (expression.Left.IsBooleanComparison()) { SqlExpressionProvider.Where(expression.Left, sqlGenerate); sqlGenerate += $" = 1"; } else { SqlExpressionProvider.Where(expression.Left, sqlGenerate); } signIndex = sqlGenerate.Length; if (expression.Right.IsBooleanComparison()) { SqlExpressionProvider.Where(expression.Right, sqlGenerate); sqlGenerate += $" = 1"; } else { SqlExpressionProvider.Where(expression.Right, sqlGenerate); } sqlLength = sqlGenerate.Length; } else { SqlExpressionProvider.Where(expression.Left, sqlGenerate); signIndex = sqlGenerate.Length; SqlExpressionProvider.Where(expression.Right, sqlGenerate); sqlLength = sqlGenerate.Length; } if (sqlLength - signIndex == 5 && sqlGenerate.ToString().EndsWith("null")) OperatorParser(expression.NodeType, signIndex, sqlGenerate, true); else OperatorParser(expression.NodeType, signIndex, sqlGenerate); #endregion if (expression.NodeType == ExpressionType.OrElse || expression.NodeType == ExpressionType.AndAlso || expression.NodeType == ExpressionType.Or || expression.NodeType == ExpressionType.And) { sqlGenerate.Sql.Insert(leftBracketIndex, " ( "); rightBracketIndex = sqlGenerate.Length; sqlGenerate.Sql.Insert(rightBracketIndex, " ) "); } return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/ConstantSqlExpression.cs ================================================ using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class ConstantSqlExpression : BaseSqlExpression { protected override SqlGenerate Where(ConstantExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.AddDbParameter(expression.Value); return sqlGenerate; } protected override SqlGenerate In(ConstantExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.AddDbParameter(expression.Value); sqlGenerate += ","; return sqlGenerate; } protected override SqlGenerate Select(ConstantExpression expression, SqlGenerate sqlGenerate) { if (expression.Value == null) sqlGenerate.SelectFields = new List() { "*" }; else sqlGenerate.SelectFields = new List() { expression.Value.ToString() }; return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/LambdaSqlExpression.cs ================================================ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Text; namespace Overt.Core.Data.Expressions { public class LambdaSqlExpression : BaseSqlExpression { protected override SqlGenerate Update(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Update(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Select(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Select(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Where(LambdaExpression expression, SqlGenerate sqlGenerate) { if (expression.Body.NodeType == ExpressionType.MemberAccess) { var memberExpression = expression.Body as MemberExpression; if (memberExpression.Expression == null) return sqlGenerate; //添加属性 SqlExpressionProvider.Where(memberExpression, sqlGenerate); if (memberExpression.Expression.Type.IsNullableType()) return sqlGenerate; sqlGenerate += " = 1"; return sqlGenerate; } SqlExpressionProvider.Where(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate In(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.In(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate OrderBy(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.OrderBy(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Max(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Max(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Min(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Min(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Avg(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Avg(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Count(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Count(expression.Body, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Sum(LambdaExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Sum(expression.Body, sqlGenerate); return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/ListInitSqlExpression.cs ================================================ using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class ListInitSqlExpression : BaseSqlExpression { protected override SqlGenerate In(ListInitExpression expression, SqlGenerate sqlGenerate) { var list = new List(); foreach (var elementInit in expression.Initializers) { foreach (var expre in elementInit.Arguments) { var obj = SqlExpressionCompiler.Evaluate(expre); list.Add(obj); } } sqlGenerate.AddDbParameter(list); return sqlGenerate; } protected override SqlGenerate Select(ListInitExpression expression, SqlGenerate sqlGenerate) { foreach (var elementInit in expression.Initializers) { foreach (var expre in elementInit.Arguments) { var obj = SqlExpressionCompiler.Evaluate(expre); if (obj == null) continue; var fieldName = obj.ToString(); if (string.IsNullOrEmpty(fieldName)) continue; sqlGenerate.SelectFields.Add(fieldName.ParamSql(sqlGenerate)); } } return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/MemberSqlExpression.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class MemberSqlExpression : BaseSqlExpression { protected override SqlGenerate Select(MemberExpression expression, SqlGenerate sqlGenerate) { if (IsEnumerable(expression)) { var result = SqlExpressionCompiler.Evaluate(expression); var fields = (result as IEnumerable).Flatten(); if (fields?.Count > 0) sqlGenerate.SelectFields.AddRange(fields.Select(field => field.ToString().ParamSql(sqlGenerate))); else sqlGenerate.SelectFields = new List() { "*" }; return sqlGenerate; } sqlGenerate.SelectFields.Add(expression.Member.Name.ParamSql(sqlGenerate)); return sqlGenerate; } protected override SqlGenerate Where(MemberExpression expression, SqlGenerate sqlGenerate) { if (expression.Expression != null) { if (expression.Member.DeclaringType.IsNullableType()) { if (expression.Member.Name == "Value") //Can't use C# 6 yet: nameof(Nullable.Value) { SqlExpressionProvider.Where(expression.Expression, sqlGenerate); return sqlGenerate; } if (expression.Member.Name == "HasValue") { var doesNotEqualNull = Expression.MakeBinary(ExpressionType.NotEqual, expression.Expression, Expression.Constant(null)); SqlExpressionProvider.Where(doesNotEqualNull, sqlGenerate); return sqlGenerate; } throw new ArgumentException($"Expression '{expression}' accesses unsupported property '{expression.Member}' of Nullable"); } if (expression.IsParameterOrConvertAccess()) { sqlGenerate += $" {expression.Member.Name.ParamSql(sqlGenerate)}"; return sqlGenerate; } } var val = SqlExpressionCompiler.Evaluate(expression); sqlGenerate.AddDbParameter(val); return sqlGenerate; } protected override SqlGenerate In(MemberExpression expression, SqlGenerate sqlGenerate) { var result = SqlExpressionCompiler.Evaluate(expression); var inArgs = (result as IEnumerable).Flatten(); if (sqlGenerate.DatabaseType == DatabaseType.PostgreSQL) // pg的in查询需要手动拼接,不然npgsql客户不支持list类型参数化 sqlGenerate.CombineInParameters(inArgs); else sqlGenerate.AddDbParameter(inArgs); return sqlGenerate; } protected override SqlGenerate OrderBy(MemberExpression expression, SqlGenerate sqlGenerate) { sqlGenerate += expression.Member.Name.ParamSql(sqlGenerate); return sqlGenerate; } protected override SqlGenerate Update(MemberExpression expression, SqlGenerate sqlGenerate) { var obj = SqlExpressionCompiler.Evaluate(expression); if (obj == null) throw new ArgumentException($"Expression '{expression}' accesses unsupported property '{expression.Member}' of Nullable"); if (obj.GetType().IsValueType) throw new ArgumentException($"Expression '{expression}' accesses unsupported valuetype"); if (obj.GetType() == typeof(string)) { sqlGenerate += obj.ToString(); } else if (obj is IDictionary dictionary) { foreach (string key in dictionary.Keys) { sqlGenerate += $"{key.ParamSql(sqlGenerate)} = "; sqlGenerate.AddDbParameter(dictionary[key]); sqlGenerate += ","; } } else { var pis = obj.GetType().GetProperties(); foreach (var p in pis) { sqlGenerate += $"{p.Name.ParamSql(sqlGenerate)} = "; sqlGenerate.AddDbParameter(p.GetValue(obj)); sqlGenerate += ","; } } if (sqlGenerate[sqlGenerate.Length - 1] == ',') sqlGenerate.Sql.Remove(sqlGenerate.Length - 1, 1); return sqlGenerate; } protected override SqlGenerate Max(MemberExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.Sql.AppendFormat("select max({0}) from {1}", expression.Member.Name.ParamSql(sqlGenerate), sqlGenerate.TableName); return sqlGenerate; } protected override SqlGenerate Min(MemberExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.Sql.AppendFormat("select min({0}) from {1}", expression.Member.Name.ParamSql(sqlGenerate), sqlGenerate.TableName); return sqlGenerate; } protected override SqlGenerate Avg(MemberExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.Sql.AppendFormat("select avg({0}) from {1}", expression.Member.Name.ParamSql(sqlGenerate), sqlGenerate.TableName); return sqlGenerate; } protected override SqlGenerate Count(MemberExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.Sql.AppendFormat("select count({0}) from {1}", expression.Member.Name.ParamSql(sqlGenerate), sqlGenerate.TableName); return sqlGenerate; } protected override SqlGenerate Sum(MemberExpression expression, SqlGenerate sqlGenerate) { sqlGenerate.Sql.AppendFormat("select sum({0}) from {1}", expression.Member.Name.ParamSql(sqlGenerate), sqlGenerate.TableName); return sqlGenerate; } /// /// 是否是集合方法 /// /// /// internal static bool IsEnumerable(MemberExpression m) { return m.Type.IsOrHasGenericInterfaceTypeOf(typeof(IEnumerable<>)) && m.Type != typeof(string); } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/MethodCallSqlExpression.cs ================================================ using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class MethodCallSqlExpression : BaseSqlExpression { static Dictionary> _Methods = new Dictionary> { {"In", In}, {"Equals", Equals}, {"Contains", Contains}, {"StartsWith", StartsWith}, {"EndsWith", EndsWith}, {"Format", Format} }; private static new void In(MethodCallExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Arguments[0], sqlGenerate); sqlGenerate += " in "; SqlExpressionProvider.In(expression.Arguments[1], sqlGenerate); } private static void Equals(MethodCallExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Object, sqlGenerate); sqlGenerate += " = "; SqlExpressionProvider.Where(expression.Arguments[0], sqlGenerate); } private static void Contains(MethodCallExpression expression, SqlGenerate sqlGenerate) { if (IsStaticArrayMethod(expression)) { DoStaticArrayMethodCall(expression, sqlGenerate); return; } if (IsEnumerableMethod(expression)) { DoEnumerableMethodCall(expression, sqlGenerate); return; } SqlExpressionProvider.Where(expression.Object, sqlGenerate); sqlGenerate += " like "; var val = SqlExpressionCompiler.Evaluate(expression.Arguments[0]); sqlGenerate.AddDbParameter($"%{val}%"); } private static void EndsWith(MethodCallExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Object, sqlGenerate); //SqlExpressionProvider.Where(expression.Arguments[0], sqlGenerate); sqlGenerate += " like "; var val = SqlExpressionCompiler.Evaluate(expression.Arguments[0]); sqlGenerate.AddDbParameter($"%{val}"); } private static void StartsWith(MethodCallExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Object, sqlGenerate); //SqlExpressionProvider.Where(expression.Arguments[0], sqlGenerate); sqlGenerate += " like "; var val = SqlExpressionCompiler.Evaluate(expression.Arguments[0]); sqlGenerate.AddDbParameter($"{val}%"); } private static void Format(MethodCallExpression expression, SqlGenerate sqlGenerate) { var formatString = SqlExpressionCompiler.Evaluate(expression.Arguments[0]); var formatArgs = new List(); var args = expression.Arguments; if (args.Count > 1) { for (int i = 1; i < args.Count; i++) { var val = SqlExpressionCompiler.Evaluate(expression.Arguments[i]); formatArgs.Add(val?.ToString()); } } sqlGenerate += string.Format(formatString.ToString(), formatArgs.ToArray()); } protected override SqlGenerate Where(MethodCallExpression expression, SqlGenerate sqlGenerate) { var key = expression.Method; if (key.IsGenericMethod) key = key.GetGenericMethodDefinition(); Action action; if (_Methods.TryGetValue(key.Name, out action)) { action(expression, sqlGenerate); return sqlGenerate; } throw new NotImplementedException("无法解析方法" + expression.Method); } protected override SqlGenerate Update(MethodCallExpression expression, SqlGenerate sqlGenerate) { var key = expression.Method; if (key.IsGenericMethod) key = key.GetGenericMethodDefinition(); Action action; if (_Methods.TryGetValue(key.Name, out action)) { action(expression, sqlGenerate); return sqlGenerate; } throw new NotImplementedException("无法解析方法" + expression.Method); } #region Internal Method internal static void DoEnumerableMethodCall(MethodCallExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Arguments[0], sqlGenerate); sqlGenerate += " in "; SqlExpressionProvider.In(expression.Object, sqlGenerate); } internal static void DoStaticArrayMethodCall(MethodCallExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Arguments[expression.Arguments.Count - 1], sqlGenerate); sqlGenerate += " in "; var memberExpr = expression.Arguments[0]; if (memberExpr.NodeType == ExpressionType.MemberAccess) memberExpr = expression.Arguments[0] as MemberExpression; SqlExpressionProvider.In(memberExpr, sqlGenerate); } #endregion } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/NewArraySqlExpression.cs ================================================ using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class NewArraySqlExpression : BaseSqlExpression { protected override SqlGenerate In(NewArrayExpression expression, SqlGenerate sqlGenerate) { var list = new List(); foreach (var expressionItem in expression.Expressions) { var obj = SqlExpressionCompiler.Evaluate(expressionItem); list.Add(obj); } sqlGenerate.AddDbParameter(list); return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/NewSqlExpression.cs ================================================ using System.Linq.Expressions; using System.Reflection; namespace Overt.Core.Data.Expressions { class NewSqlExpression : BaseSqlExpression { protected override SqlGenerate Update(NewExpression expression, SqlGenerate sqlGenerate) { for (int i = 0; i < expression.Members.Count; i++) { var m = expression.Members[i]; sqlGenerate += $"{m.Name.ParamSql(sqlGenerate)} = "; var val = SqlExpressionCompiler.Evaluate(expression.Arguments[i]); sqlGenerate.AddDbParameter(val); sqlGenerate += ","; } if (sqlGenerate[sqlGenerate.Length - 1] == ',') sqlGenerate.Sql.Remove(sqlGenerate.Length - 1, 1); return sqlGenerate; } protected override SqlGenerate Select(NewExpression expression, SqlGenerate sqlGenerate) { foreach (Expression item in expression.Arguments) { SqlExpressionProvider.Select(item, sqlGenerate); } return sqlGenerate; } protected override SqlGenerate OrderBy(NewExpression expression, SqlGenerate sqlGenerate) { foreach (Expression item in expression.Arguments) { SqlExpressionProvider.OrderBy(item, sqlGenerate); sqlGenerate += ","; } if (sqlGenerate[sqlGenerate.Length - 1] == ',') sqlGenerate.Sql.Remove(sqlGenerate.Length - 1, 1); return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/Resolve/UnarySqlExpression.cs ================================================ using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { class UnarySqlExpression : BaseSqlExpression { protected override SqlGenerate Select(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Select(expression.Operand, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Where(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Where(expression.Operand, sqlGenerate); switch (expression.NodeType) { case ExpressionType.Not: if (expression.Operand is MethodCallExpression) { if (IsStaticArrayMethod(expression.Operand as MethodCallExpression) || IsEnumerableMethod(expression.Operand as MethodCallExpression)) { sqlGenerate.RelaceLast("in", "not in"); } else { sqlGenerate.RelaceLast("like", "not like"); } } else sqlGenerate += " = 0"; break; } return sqlGenerate; } protected override SqlGenerate OrderBy(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.OrderBy(expression.Operand, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Max(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Max(expression.Operand, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Min(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Min(expression.Operand, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Avg(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Avg(expression.Operand, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Count(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Count(expression.Operand, sqlGenerate); return sqlGenerate; } protected override SqlGenerate Sum(UnaryExpression expression, SqlGenerate sqlGenerate) { SqlExpressionProvider.Sum(expression.Operand, sqlGenerate); return sqlGenerate; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/SqlExpression.cs ================================================ using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { /// /// Expression => Sql /// public static class SqlExpression { /// /// /// /// /// /// /// /// public static SqlExpressionCore Insert(DatabaseType dbType, string tableName = "", bool returnLastIdentity = false) { return new SqlExpressionCore(dbType, tableName).Insert(returnLastIdentity); } /// /// 删除 /// /// /// 数据库类型 /// /// public static SqlExpressionCore Delete(DatabaseType dbType, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Delete(); } /// /// 修改 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Update(DatabaseType dbType, Expression> expression = null, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Update(expression); } /// /// 修改 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Update(DatabaseType dbType, IEnumerable fields = null, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Update(fields); } /// /// 查询 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Select(DatabaseType dbType, Expression> expression = null, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Select(expression); } /// /// 数量 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Count(DatabaseType dbType, Expression> expression = null, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Count(expression); } /// /// 最大 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Max(DatabaseType dbType, Expression> expression, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Max(expression); } /// /// 最小 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Min(DatabaseType dbType, Expression> expression, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Min(expression); } /// /// 平均值 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Avg(DatabaseType dbType, Expression> expression, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Avg(expression); } /// /// 求和 /// /// /// 数据库类型 /// /// /// public static SqlExpressionCore Sum(DatabaseType dbType, Expression> expression, string tableName = "") { return new SqlExpressionCore(dbType, tableName).Sum(expression); } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/SqlExpressionCompiler.cs ================================================ using System; using System.Linq.Expressions; using static Overt.Core.Data.Expressions.SqlExpressionFingerprint; namespace Overt.Core.Data.Expressions { /// /// 参数编译器 /// base ServiceStack /// public static class SqlExpressionCompiler { private static readonly ParameterExpression _unusedParameterExpr = Expression.Parameter(typeof(object), "_unused"); /// /// 编译 /// /// /// /// /// public static Func Compile(this Expression> lambdaExpression) { if (lambdaExpression == null) throw new ArgumentNullException(nameof(lambdaExpression)); return ExpressionCompiler.Process(lambdaExpression); } /// /// 编译 /// /// /// public static object Evaluate(Expression arg) { if (arg == null) throw new ArgumentNullException(nameof(arg)); var func = Wrap(arg); return func(null); } private static Func Wrap(Expression arg) { var lambdaExpr = Expression.Lambda>(Expression.Convert(arg, typeof(object)), _unusedParameterExpr); return ExpressionCompiler.Process(lambdaExpr); } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/SqlExpressionCore.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Linq.Expressions; using System.Text; namespace Overt.Core.Data.Expressions { /// /// Expression核心 /// /// public class SqlExpressionCore { private SqlGenerate sqlGenerate = new SqlGenerate(); /// /// 脚本 /// public string Script { get { return sqlGenerate.ToString(); } } /// /// 参数 /// public Dictionary DbParams { get { return sqlGenerate.DbParams; } } /// /// 构造函数 /// /// /// public SqlExpressionCore(DatabaseType dbType, string tableName = "") { if (string.IsNullOrEmpty(tableName)) tableName = typeof(T).Name; sqlGenerate.DatabaseType = dbType; sqlGenerate.TableName = tableName.ParamSql(dbType); } /// /// 清除 /// public void Clear() { sqlGenerate.Clear(); } /// /// 新增 /// /// /// public SqlExpressionCore Insert(bool returnLastIdentity) { var addFields = new List(); var atFields = new List(); var identityPi = typeof(T).GetIdentityField(); var customPis = typeof(T).GetCustomFields(); var pis = typeof(T).GetProperties(); foreach (var pi in pis) { if (identityPi?.Name == pi.Name) continue; addFields.Add($"{pi.Name.ParamSql(sqlGenerate.DatabaseType)}"); atFields.Add($"{pi.Name.ParamValue(sqlGenerate.DatabaseType, customPis)}"); } sqlGenerate.Clear(); sqlGenerate += $"insert into {sqlGenerate.TableName}({string.Join(", ", addFields)}) values({string.Join(", ", atFields)});"; if (identityPi != null && returnLastIdentity) sqlGenerate += sqlGenerate.DatabaseType.SelectLastIdentity(); return this; } /// /// 查询 /// /// /// 排序字段 /// public SqlExpressionCore Select(Expression> expression = null, string orderBy = "") { var sql = $"select {{0}}{Environment.NewLine}from {sqlGenerate.TableName}"; var fields = string.Empty; if (expression == null) fields = "*"; else { SqlExpressionProvider.Select(expression, sqlGenerate); fields = sqlGenerate.SelectFieldsStr; } sqlGenerate.Sql.AppendFormat(sql, fields); return this; } /// /// Where条件 /// /// /// public SqlExpressionCore Where(Expression> expression) { if (expression == null) return this; sqlGenerate += $"{Environment.NewLine}where"; SqlExpressionProvider.Where(expression, sqlGenerate); return this; } /// /// OrderBy /// /// /// public SqlExpressionCore OrderBy(string orderBy) { if (string.IsNullOrEmpty(orderBy)) { var property = typeof(T).GetProperty(); if (property == null) property = typeof(T).GetProperties()[0]; var propertyName = property.Name; if (sqlGenerate.DatabaseType == DatabaseType.PostgreSQL) propertyName = $"\"{propertyName}\""; orderBy = $"order by {propertyName} desc"; } if (!orderBy.StartsWith("order by")) orderBy = $"order by {orderBy}"; switch (sqlGenerate.DatabaseType) { case DatabaseType.SqlServer: // 2012版本支持 内部数据库版本706 【select DATABASEPROPERTYEX('master','version')】 sqlGenerate.Sql.Replace("select", $"select row_number() over({orderBy}) as RowNumber,"); break; case DatabaseType.GteSqlServer2012: sqlGenerate += $"{Environment.NewLine}{orderBy}"; break; case DatabaseType.MySql: sqlGenerate += $"{Environment.NewLine}{orderBy}"; break; case DatabaseType.SQLite: sqlGenerate += $"{Environment.NewLine}{orderBy}"; break; case DatabaseType.PostgreSQL: sqlGenerate += $"{Environment.NewLine}{orderBy}"; break; } return this; } /// /// Top1 /// /// public SqlExpressionCore TopOne() { switch (sqlGenerate.DatabaseType) { case DatabaseType.SqlServer: // 2012版本支持 内部数据库版本706 【select DATABASEPROPERTYEX('master','version')】 sqlGenerate.Sql.Replace("select", $"select top 1{Environment.NewLine}"); break; case DatabaseType.GteSqlServer2012: sqlGenerate.Sql.Replace("select", $"select top 1{Environment.NewLine}"); break; case DatabaseType.MySql: sqlGenerate += $"{Environment.NewLine}limit 1"; break; case DatabaseType.SQLite: sqlGenerate += $"{Environment.NewLine}limit 1"; break; case DatabaseType.PostgreSQL: sqlGenerate += $"{Environment.NewLine}limit 1"; break; } return this; } /// /// Limit /// /// /// /// public SqlExpressionCore Limit(int page, int rows) { var skip = (page - 1) * rows; switch (sqlGenerate.DatabaseType) { case DatabaseType.SqlServer: sqlGenerate.Sql = new StringBuilder($"SELECT it.* FROM ({sqlGenerate.Sql}) it where it.RowNumber > {skip} AND it.RowNumber <= {page * rows}"); break; case DatabaseType.GteSqlServer2012: sqlGenerate += $" OFFSET {skip} ROW FETCH NEXT {rows} rows only"; break; case DatabaseType.MySql: sqlGenerate += $" limit {skip}, {rows}"; break; case DatabaseType.SQLite: sqlGenerate += $" limit {rows} offset {skip}"; break; case DatabaseType.PostgreSQL: sqlGenerate += $" limit {rows} offset {skip}"; break; default: break; } return this; } /// /// Offset /// /// /// /// public SqlExpressionCore Offset(int offset, int size) { switch (sqlGenerate.DatabaseType) { case DatabaseType.SqlServer: sqlGenerate.Sql = new StringBuilder($"SELECT it.* FROM ({sqlGenerate.Sql}) it where it.RowNumber > {offset} AND it.RowNumber <= {offset + size}"); break; case DatabaseType.GteSqlServer2012: sqlGenerate += $" OFFSET {offset} ROW FETCH NEXT {size} rows only"; break; case DatabaseType.MySql: sqlGenerate += $" limit {offset}, {size}"; break; case DatabaseType.SQLite: sqlGenerate += $" limit {size} offset {offset}"; break; case DatabaseType.PostgreSQL: sqlGenerate += $" limit {size} offset {offset}"; break; default: break; } return this; } /// /// 最大 /// /// /// public SqlExpressionCore Max(Expression> expression) { sqlGenerate.Clear(); SqlExpressionProvider.Max(expression, sqlGenerate); return this; } /// /// 最小值 /// /// /// public SqlExpressionCore Min(Expression> expression) { sqlGenerate.Clear(); SqlExpressionProvider.Min(expression, sqlGenerate); return this; } /// /// 平均值 /// /// /// public SqlExpressionCore Avg(Expression> expression) { sqlGenerate.Clear(); SqlExpressionProvider.Avg(expression, sqlGenerate); return this; } /// /// 行数 /// /// /// public SqlExpressionCore Count(Expression> expression = null) { sqlGenerate.Clear(); if (expression == null) sqlGenerate.Sql.Append($"select count(*) from {sqlGenerate.TableName}"); else SqlExpressionProvider.Count(expression, this.sqlGenerate); return this; } /// /// 总计 /// /// /// public SqlExpressionCore Sum(Expression> expression) { sqlGenerate.Clear(); SqlExpressionProvider.Sum(expression, sqlGenerate); return this; } /// /// 删除 /// /// public SqlExpressionCore Delete() { sqlGenerate.Clear(); sqlGenerate += $"delete from {sqlGenerate.TableName}"; return this; } /// /// 修改 /// /// /// public SqlExpressionCore Update(Expression> expression = null) { sqlGenerate.Clear(); sqlGenerate += $"update {sqlGenerate.TableName} set "; SqlExpressionProvider.Update(expression, sqlGenerate); return this; } /// /// 修改 /// /// /// public SqlExpressionCore Update(IEnumerable fields = null) { var setFields = new List(); var whereFields = new List(); var pis = typeof(T).GetProperties(); var customPis = typeof(T).GetCustomFields(); foreach (var pi in pis) { var obs = pi.GetCustomAttributes(typeof(KeyAttribute), false); if (obs?.Count() > 0) whereFields.Add($"{pi.Name.ParamSql(sqlGenerate.DatabaseType)} = @{pi.Name}"); else { if ((fields?.Count() ?? 0) <= 0 || fields.Contains(pi.Name)) setFields.Add($"{pi.Name.ParamSql(sqlGenerate.DatabaseType)} = {pi.Name.ParamValue(sqlGenerate.DatabaseType, customPis)}"); } } if (whereFields.Count <= 0) throw new Exception($"实体未设置主键Key属性"); if (setFields.Count <= 0) throw new Exception($"实体未标记任何更新字段"); sqlGenerate.Clear(); sqlGenerate += $"update {sqlGenerate.TableName} set {string.Join(", ", setFields)} where {string.Join(", ", whereFields)}"; return this; } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/SqlExpressionFingerprint.cs ================================================ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using System.Text; namespace Overt.Core.Data.Expressions { /// /// base ServiceStack /// public class SqlExpressionFingerprint { internal sealed class BinaryExpressionFingerprint : ExpressionFingerprint { public BinaryExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method) : base(nodeType, type) { // Other properties on BinaryExpression (like IsLifted / IsLiftedToNull) are simply derived // from Type and NodeType, so they're not necessary for inclusion in the fingerprint. Method = method; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.binaryexpression.method.aspx public MethodInfo Method { get; private set; } public override bool Equals(object obj) { BinaryExpressionFingerprint other = obj as BinaryExpressionFingerprint; return (other != null) && Equals(this.Method, other.Method) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Method); base.AddToHashCodeCombiner(combiner); } } internal static class ExpressionCompiler { // This is the entry point to the cached expression compilation system. The system // will try to turn the expression into an actual delegate as quickly as possible, // relying on cache lookups and other techniques to save time if appropriate. // If the provided expression is particularly obscure and the system doesn't know // how to handle it, we'll just compile the expression as normal. public static Func Process(Expression> lambdaExpression) { return Compiler.Compile(lambdaExpression); } private static class Compiler { private static Func _identityFunc; private static readonly ConcurrentDictionary> _simpleMemberAccessDict = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> _constMemberAccessDict = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> _fingerprintedCache = new ConcurrentDictionary>(); public static Func Compile(Expression> expr) { return CompileFromIdentityFunc(expr) ?? CompileFromConstLookup(expr) ?? CompileFromMemberAccess(expr) ?? CompileFromFingerprint(expr) ?? CompileSlow(expr); } private static Func CompileFromConstLookup(Expression> expr) { ConstantExpression constExpr = expr.Body as ConstantExpression; if (constExpr != null) { // model => {const} TOut constantValue = (TOut)constExpr.Value; return _ => constantValue; } return null; } private static Func CompileFromIdentityFunc(Expression> expr) { if (expr.Body == expr.Parameters[0]) { // model => model // don't need to lock, as all identity funcs are identical if (_identityFunc == null) { _identityFunc = expr.Compile(); } return _identityFunc; } return null; } private static Func CompileFromFingerprint(Expression> expr) { List capturedConstants; ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants); if (fingerprint != null) { var del = _fingerprintedCache.GetOrAdd(fingerprint, _ => { // Fingerprinting succeeded, but there was a cache miss. Rewrite the expression // and add the rewritten expression to the cache. var hoistedExpr = HoistingExpressionVisitor.Hoist(expr); return hoistedExpr.Compile(); }); return model => del(model, capturedConstants); } // couldn't be fingerprinted return null; } private static Func CompileFromMemberAccess(Expression> expr) { // Performance tests show that on the x64 platform, special-casing static member and // captured local variable accesses is faster than letting the fingerprinting system // handle them. On the x86 platform, the fingerprinting system is faster, but only // by around one microsecond, so it's not worth it to complicate the logic here with // an architecture check. MemberExpression memberExpr = expr.Body as MemberExpression; if (memberExpr != null) { if (memberExpr.Expression == expr.Parameters[0] || memberExpr.Expression == null) { // model => model.Member or model => StaticMember return _simpleMemberAccessDict.GetOrAdd(memberExpr.Member, _ => expr.Compile()); } ConstantExpression constExpr = memberExpr.Expression as ConstantExpression; if (constExpr != null) { // model => {const}.Member (captured local variable) var del = _constMemberAccessDict.GetOrAdd(memberExpr.Member, _ => { // rewrite as capturedLocal => ((TDeclaringType)capturedLocal).Member var constParamExpr = Expression.Parameter(typeof(object), "capturedLocal"); var constCastExpr = Expression.Convert(constParamExpr, memberExpr.Member.DeclaringType); var newMemberAccessExpr = memberExpr.Update(constCastExpr); var newLambdaExpr = Expression.Lambda>(newMemberAccessExpr, constParamExpr); return newLambdaExpr.Compile(); }); object capturedLocal = constExpr.Value; return _ => del(capturedLocal); } } return null; } private static Func CompileSlow(Expression> expr) { // fallback compilation system - just compile the expression directly return expr.Compile(); } } } internal sealed class ConditionalExpressionFingerprint : ExpressionFingerprint { public ConditionalExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on ConditionalExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { ConditionalExpressionFingerprint other = obj as ConditionalExpressionFingerprint; return (other != null) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } } internal sealed class ConstantExpressionFingerprint : ExpressionFingerprint { public ConstantExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on ConstantExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { ConstantExpressionFingerprint other = obj as ConstantExpressionFingerprint; return (other != null) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } } internal sealed class DefaultExpressionFingerprint : ExpressionFingerprint { public DefaultExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on DefaultExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { DefaultExpressionFingerprint other = obj as DefaultExpressionFingerprint; return (other != null) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } } internal abstract class ExpressionFingerprint { protected ExpressionFingerprint(ExpressionType nodeType, Type type) { NodeType = nodeType; Type = type; } // the type of expression node, e.g. OP_ADD, MEMBER_ACCESS, etc. public ExpressionType NodeType { get; private set; } // the CLR type resulting from this expression, e.g. int, string, etc. public Type Type { get; private set; } internal virtual void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddInt32((int)NodeType); combiner.AddObject(Type); } protected bool Equals(ExpressionFingerprint other) { return (other != null) && (this.NodeType == other.NodeType) && Equals(this.Type, other.Type); } public override bool Equals(object obj) { return Equals(obj as ExpressionFingerprint); } public override int GetHashCode() { HashCodeCombiner combiner = new HashCodeCombiner(); AddToHashCodeCombiner(combiner); return combiner.CombinedHash; } } internal sealed class ExpressionFingerprintChain : IEquatable { public readonly List Elements = new List(); public bool Equals(ExpressionFingerprintChain other) { // Two chains are considered equal if two elements appearing in the same index in // each chain are equal (value equality, not referential equality). if (other == null) { return false; } if (this.Elements.Count != other.Elements.Count) { return false; } for (int i = 0; i < this.Elements.Count; i++) { if (!Equals(this.Elements[i], other.Elements[i])) { return false; } } return true; } public override bool Equals(object obj) { return Equals(obj as ExpressionFingerprintChain); } public override int GetHashCode() { HashCodeCombiner combiner = new HashCodeCombiner(); Elements.ForEach(combiner.AddFingerprint); return combiner.CombinedHash; } } internal sealed class FingerprintingExpressionVisitor : ExpressionVisitor { private readonly List _seenConstants = new List(); private readonly List _seenParameters = new List(); private readonly ExpressionFingerprintChain _currentChain = new ExpressionFingerprintChain(); private bool _gaveUp; private FingerprintingExpressionVisitor() { } private T GiveUp(T node) { // We don't understand this node, so just quit. _gaveUp = true; return node; } // Returns the fingerprint chain + captured constants list for this expression, or null // if the expression couldn't be fingerprinted. public static ExpressionFingerprintChain GetFingerprintChain(Expression expr, out List capturedConstants) { FingerprintingExpressionVisitor visitor = new FingerprintingExpressionVisitor(); visitor.Visit(expr); if (visitor._gaveUp) { capturedConstants = null; return null; } else { capturedConstants = visitor._seenConstants; return visitor._currentChain; } } public override Expression Visit(Expression node) { if (node == null) { _currentChain.Elements.Add(null); return null; } else { return base.Visit(node); } } protected override Expression VisitBinary(BinaryExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new BinaryExpressionFingerprint(node.NodeType, node.Type, node.Method)); return base.VisitBinary(node); } protected override Expression VisitBlock(BlockExpression node) { return GiveUp(node); } protected override CatchBlock VisitCatchBlock(CatchBlock node) { return GiveUp(node); } protected override Expression VisitConditional(ConditionalExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new ConditionalExpressionFingerprint(node.NodeType, node.Type)); return base.VisitConditional(node); } protected override Expression VisitConstant(ConstantExpression node) { if (_gaveUp) { return node; } _seenConstants.Add(node.Value); _currentChain.Elements.Add(new ConstantExpressionFingerprint(node.NodeType, node.Type)); return base.VisitConstant(node); } protected override Expression VisitDebugInfo(DebugInfoExpression node) { return GiveUp(node); } protected override Expression VisitDefault(DefaultExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new DefaultExpressionFingerprint(node.NodeType, node.Type)); return base.VisitDefault(node); } protected #if !ASP_NET_CORE override #endif Expression VisitDynamic(DynamicExpression node) { return GiveUp(node); } protected override ElementInit VisitElementInit(ElementInit node) { return GiveUp(node); } protected override Expression VisitExtension(Expression node) { return GiveUp(node); } protected override Expression VisitGoto(GotoExpression node) { return GiveUp(node); } protected override Expression VisitIndex(IndexExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new IndexExpressionFingerprint(node.NodeType, node.Type, node.Indexer)); return base.VisitIndex(node); } protected override Expression VisitInvocation(InvocationExpression node) { return GiveUp(node); } protected override Expression VisitLabel(LabelExpression node) { return GiveUp(node); } protected override LabelTarget VisitLabelTarget(LabelTarget node) { return GiveUp(node); } protected override Expression VisitLambda(Expression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new LambdaExpressionFingerprint(node.NodeType, node.Type)); return base.VisitLambda(node); } protected override Expression VisitListInit(ListInitExpression node) { return GiveUp(node); } protected override Expression VisitLoop(LoopExpression node) { return GiveUp(node); } protected override Expression VisitMember(MemberExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new MemberExpressionFingerprint(node.NodeType, node.Type, node.Member)); return base.VisitMember(node); } protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) { return GiveUp(node); } protected override MemberBinding VisitMemberBinding(MemberBinding node) { return GiveUp(node); } protected override Expression VisitMemberInit(MemberInitExpression node) { return GiveUp(node); } protected override MemberListBinding VisitMemberListBinding(MemberListBinding node) { return GiveUp(node); } protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) { return GiveUp(node); } protected override Expression VisitMethodCall(MethodCallExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new MethodCallExpressionFingerprint(node.NodeType, node.Type, node.Method)); return base.VisitMethodCall(node); } protected override Expression VisitNew(NewExpression node) { return GiveUp(node); } protected override Expression VisitNewArray(NewArrayExpression node) { return GiveUp(node); } protected override Expression VisitParameter(ParameterExpression node) { if (_gaveUp) { return node; } int parameterIndex = _seenParameters.IndexOf(node); if (parameterIndex < 0) { // first time seeing this parameter parameterIndex = _seenParameters.Count; _seenParameters.Add(node); } _currentChain.Elements.Add(new ParameterExpressionFingerprint(node.NodeType, node.Type, parameterIndex)); return base.VisitParameter(node); } protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { return GiveUp(node); } protected override Expression VisitSwitch(SwitchExpression node) { return GiveUp(node); } protected override SwitchCase VisitSwitchCase(SwitchCase node) { return GiveUp(node); } protected override Expression VisitTry(TryExpression node) { return GiveUp(node); } protected override Expression VisitTypeBinary(TypeBinaryExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new TypeBinaryExpressionFingerprint(node.NodeType, node.Type, node.TypeOperand)); return base.VisitTypeBinary(node); } protected override Expression VisitUnary(UnaryExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new UnaryExpressionFingerprint(node.NodeType, node.Type, node.Method)); return base.VisitUnary(node); } } internal class HashCodeCombiner { private long _combinedHash64 = 0x1505L; public int CombinedHash { get { return _combinedHash64.GetHashCode(); } } public void AddFingerprint(ExpressionFingerprint fingerprint) { if (fingerprint != null) { fingerprint.AddToHashCodeCombiner(this); } else { AddInt32(0); } } public void AddEnumerable(IEnumerable e) { if (e == null) { AddInt32(0); } else { int count = 0; foreach (object o in e) { AddObject(o); count++; } AddInt32(count); } } public void AddInt32(int i) { _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; } public void AddObject(object o) { int hashCode = (o != null) ? o.GetHashCode() : 0; AddInt32(hashCode); } } internal delegate TValue Hoisted(TModel model, List capturedConstants); internal sealed class HoistingExpressionVisitor : ExpressionVisitor { private static readonly ParameterExpression _hoistedConstantsParamExpr = Expression.Parameter(typeof(List), "hoistedConstants"); private int _numConstantsProcessed; // factory will create instance private HoistingExpressionVisitor() { } public static Expression> Hoist(Expression> expr) { // rewrite Expression> as Expression> var visitor = new HoistingExpressionVisitor(); var rewrittenBodyExpr = visitor.Visit(expr.Body); var rewrittenLambdaExpr = Expression.Lambda>(rewrittenBodyExpr, expr.Parameters[0], _hoistedConstantsParamExpr); return rewrittenLambdaExpr; } protected override Expression VisitConstant(ConstantExpression node) { // rewrite the constant expression as (TConst)hoistedConstants[i]; return Expression.Convert(Expression.Property(_hoistedConstantsParamExpr, "Item", Expression.Constant(_numConstantsProcessed++)), node.Type); } } internal sealed class IndexExpressionFingerprint : ExpressionFingerprint { public IndexExpressionFingerprint(ExpressionType nodeType, Type type, PropertyInfo indexer) : base(nodeType, type) { // Other properties on IndexExpression (like the argument count) are simply derived // from Type and Indexer, so they're not necessary for inclusion in the fingerprint. Indexer = indexer; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.indexexpression.indexer.aspx public PropertyInfo Indexer { get; private set; } public override bool Equals(object obj) { IndexExpressionFingerprint other = obj as IndexExpressionFingerprint; return (other != null) && Equals(this.Indexer, other.Indexer) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Indexer); base.AddToHashCodeCombiner(combiner); } } internal sealed class LambdaExpressionFingerprint : ExpressionFingerprint { public LambdaExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on LambdaExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { LambdaExpressionFingerprint other = obj as LambdaExpressionFingerprint; return (other != null) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } } internal sealed class MemberExpressionFingerprint : ExpressionFingerprint { public MemberExpressionFingerprint(ExpressionType nodeType, Type type, MemberInfo member) : base(nodeType, type) { Member = member; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.memberexpression.member.aspx public MemberInfo Member { get; private set; } public override bool Equals(object obj) { MemberExpressionFingerprint other = obj as MemberExpressionFingerprint; return (other != null) && Equals(this.Member, other.Member) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Member); base.AddToHashCodeCombiner(combiner); } } internal sealed class MethodCallExpressionFingerprint : ExpressionFingerprint { public MethodCallExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method) : base(nodeType, type) { // Other properties on MethodCallExpression (like the argument count) are simply derived // from Type and Indexer, so they're not necessary for inclusion in the fingerprint. Method = method; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.methodcallexpression.method.aspx public MethodInfo Method { get; private set; } public override bool Equals(object obj) { MethodCallExpressionFingerprint other = obj as MethodCallExpressionFingerprint; return (other != null) && Equals(this.Method, other.Method) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Method); base.AddToHashCodeCombiner(combiner); } } internal sealed class ParameterExpressionFingerprint : ExpressionFingerprint { public ParameterExpressionFingerprint(ExpressionType nodeType, Type type, int parameterIndex) : base(nodeType, type) { ParameterIndex = parameterIndex; } // Parameter position within the overall expression, used to maintain alpha equivalence. public int ParameterIndex { get; private set; } public override bool Equals(object obj) { ParameterExpressionFingerprint other = obj as ParameterExpressionFingerprint; return (other != null) && (this.ParameterIndex == other.ParameterIndex) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddInt32(ParameterIndex); base.AddToHashCodeCombiner(combiner); } } internal sealed class TypeBinaryExpressionFingerprint : ExpressionFingerprint { public TypeBinaryExpressionFingerprint(ExpressionType nodeType, Type type, Type typeOperand) : base(nodeType, type) { TypeOperand = typeOperand; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.typebinaryexpression.typeoperand.aspx public Type TypeOperand { get; private set; } public override bool Equals(object obj) { TypeBinaryExpressionFingerprint other = obj as TypeBinaryExpressionFingerprint; return (other != null) && Equals(this.TypeOperand, other.TypeOperand) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(TypeOperand); base.AddToHashCodeCombiner(combiner); } } internal sealed class UnaryExpressionFingerprint : ExpressionFingerprint { public UnaryExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method) : base(nodeType, type) { // Other properties on UnaryExpression (like IsLifted / IsLiftedToNull) are simply derived // from Type and NodeType, so they're not necessary for inclusion in the fingerprint. Method = method; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.unaryexpression.method.aspx public MethodInfo Method { get; private set; } public override bool Equals(object obj) { UnaryExpressionFingerprint other = obj as UnaryExpressionFingerprint; return (other != null) && Equals(this.Method, other.Method) && this.Equals(other); } public override int GetHashCode() { return base.GetHashCode(); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Method); base.AddToHashCodeCombiner(combiner); } } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/SqlExpressionProvider.cs ================================================ using System; using System.Linq.Expressions; namespace Overt.Core.Data.Expressions { internal class SqlExpressionProvider { internal static ISqlExpression GetSqlExpression(Expression expression) { if (expression == null) { throw new ArgumentNullException("expression", "不能为null"); } if (expression is LambdaExpression) { return new LambdaSqlExpression(); } if (expression is BinaryExpression) { return new BinarySqlExpression(); } if (expression is BlockExpression) { throw new NotImplementedException("未实现的BlockSqlExpression"); } if (expression is ConditionalExpression) { throw new NotImplementedException("未实现的ConditionalSqlExpression"); } if (expression is ConstantExpression) { return new ConstantSqlExpression(); } if (expression is DebugInfoExpression) { throw new NotImplementedException("未实现的DebugInfoSqlExpression"); } if (expression is DefaultExpression) { throw new NotImplementedException("未实现的DefaultSqlExpression"); } if (expression is DynamicExpression) { throw new NotImplementedException("未实现的DynamicSqlExpression"); } if (expression is GotoExpression) { throw new NotImplementedException("未实现的GotoSqlExpression"); } if (expression is IndexExpression) { throw new NotImplementedException("未实现的IndexSqlExpression"); } if (expression is InvocationExpression) { throw new NotImplementedException("未实现的InvocationSqlExpression"); } if (expression is LabelExpression) { throw new NotImplementedException("未实现的LabelSqlExpression"); } if (expression is LambdaExpression) { throw new NotImplementedException("未实现的LambdaSqlExpression"); } if (expression is ListInitExpression) { return new ListInitSqlExpression(); } if (expression is LoopExpression) { throw new NotImplementedException("未实现的LoopSqlExpression"); } if (expression is MemberExpression) { return new MemberSqlExpression(); } if (expression is MemberInitExpression) { throw new NotImplementedException("未实现的MemberInitSqlExpression"); } if (expression is MethodCallExpression) { return new MethodCallSqlExpression(); } if (expression is NewArrayExpression) { return new NewArraySqlExpression(); } if (expression is NewExpression) { return new NewSqlExpression(); } if (expression is ParameterExpression) { throw new NotImplementedException("未实现的ParameterSqlExpression"); } if (expression is RuntimeVariablesExpression) { throw new NotImplementedException("未实现的RuntimeVariablesSqlExpression"); } if (expression is SwitchExpression) { throw new NotImplementedException("未实现的SwitchSqlExpression"); } if (expression is TryExpression) { throw new NotImplementedException("未实现的TrySqlExpression"); } if (expression is TypeBinaryExpression) { throw new NotImplementedException("未实现的TypeBinarySqlExpression"); } if (expression is UnaryExpression) { return new UnarySqlExpression(); } throw new NotImplementedException("未实现的SqlExpression"); } public static void Update(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Update(expression, sqlGenerate); } public static void Select(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Select(expression, sqlGenerate); } public static void Where(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Where(expression, sqlGenerate); } public static void In(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).In(expression, sqlGenerate); } public static void OrderBy(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).OrderBy(expression, sqlGenerate); } public static void Max(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Max(expression, sqlGenerate); } public static void Min(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Min(expression, sqlGenerate); } public static void Avg(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Avg(expression, sqlGenerate); } public static void Count(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Count(expression, sqlGenerate); } public static void Sum(Expression expression, SqlGenerate sqlGenerate) { GetSqlExpression(expression).Sum(expression, sqlGenerate); } } } ================================================ FILE: src/Overt.Core.Data/Expressions/SqlExpression/SqlGenerate.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Overt.Core.Data.Expressions { /// /// SqlGenrate /// public class SqlGenerate { #region Private Property private static readonly List S_listEnglishWords = new List { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", }; private Dictionary _dicTableName = new Dictionary(); private Queue _queueEnglishWords = new Queue(S_listEnglishWords); #endregion #region Public Property /// /// 字段 /// public List SelectFields { get; set; } /// /// 表名 /// public string TableName { get; set; } /// /// 字段字符串 /// public string SelectFieldsStr { get { return string.Join(", ", this.SelectFields); } } /// /// sql长度 /// public int Length { get { return Sql.Length; } } /// /// 脚本 /// public StringBuilder Sql { get; set; } /// /// 数据库类型 /// public DatabaseType DatabaseType { get; set; } /// /// 数据库参数 /// public Dictionary DbParams { get; private set; } /// /// 索引数据 /// /// /// public char this[int index] { get { return this.Sql[index]; } } #endregion #region Constructor /// /// 构造函数 /// public SqlGenerate() { DbParams = new Dictionary(); Sql = new StringBuilder(); SelectFields = new List(); } /// /// /// /// /// /// public static SqlGenerate operator +(SqlGenerate sqlGenerate, string sql) { sqlGenerate.Sql.Append(sql); return sqlGenerate; } #endregion #region Public Method /// /// 清除 /// public void Clear() { SelectFields.Clear(); Sql.Clear(); DbParams.Clear(); _dicTableName.Clear(); _queueEnglishWords = new Queue(S_listEnglishWords); } /// /// 替换最后一次出现字符 /// /// /// public void RelaceLast(string oldStr, string newStr) { if (Sql.ToString().LastIndexOf(oldStr) == -1) return; Sql = Sql.Replace(oldStr, newStr, Sql.ToString().LastIndexOf(oldStr), newStr.Length); } /// /// 添加参数 /// /// public void AddDbParameter(object parameterValue) { if (parameterValue == null || parameterValue == DBNull.Value) Sql.Append(" null"); else { var name = DatabaseType.ParamPrefix() + "param" + DbParams.Count; DbParams.Add(name, parameterValue); Sql.Append(" " + name); } } /// /// 拼接in参数 /// /// public void CombineInParameters(List parameterValues) { if (parameterValues == null || parameterValues.Count == 0) return; Sql.Append(" " + $"({string.Join(",", parameterValues.Select(n => $"'{n}'"))})"); } /// /// ToString /// /// public override string ToString() { return Sql.ToString(); } #endregion } } ================================================ FILE: src/Overt.Core.Data/Extensions/Dapper.Async.Extensions.cs ================================================ using Dapper; using Overt.Core.Data.Expressions; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; namespace Overt.Core.Data { /// /// Dapper扩展 /// public static partial class DapperExtensions { #region Public Method /// /// 是否存在表 /// /// /// /// /// public static async Task IsExistTableAsync(this IDbConnection connection, string tableName, Action outSqlAction = null) { if (string.IsNullOrEmpty(tableName)) return false; var dbType = connection.GetDbType(); var dbName = connection.Database; var sql = dbType.ExistTableSql(dbName, tableName); outSqlAction?.Invoke(sql); // 返回sql var result = await connection.QueryFirstOrDefaultAsync(sql); return result > 0; } /// /// 是否存在字段 /// /// /// /// /// /// public static async Task IsExistFieldAsync(this IDbConnection connection, string tableName, string fieldName, Action outSqlAction = null) { if (string.IsNullOrEmpty(tableName)) return false; var dbType = connection.GetDbType(); var dbName = connection.Database; var sql = dbType.ExistFieldSql(dbName, tableName, fieldName); outSqlAction?.Invoke(sql); var result = await connection.QueryFirstOrDefaultAsync(sql); return result > 0; } /// /// 插入数据 /// /// /// /// /// /// 是否返回自增的数据 /// 返回sql语句 /// -1 参数为空 public static async Task InsertAsync(this IDbConnection connection, string tableName, TEntity entity, bool returnLastIdentity = false, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (entity == null) throw new ArgumentNullException(nameof(entity)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Insert(dbType, tableName, returnLastIdentity); outSqlAction?.Invoke(sqlExpression.Script); var identityPI = typeof(TEntity).GetIdentityField(); if (identityPI != null && returnLastIdentity) { if (identityPI.PropertyType == typeof(int)) { var intResult = await connection.ExecuteScalarAsync(sqlExpression.Script, entity); if (intResult > 0) identityPI.SetValue(entity, intResult); return intResult > 0; } var longResult = await connection.ExecuteScalarAsync(sqlExpression.Script, entity); if (longResult > 0) identityPI.SetValue(entity, longResult); return longResult > 0; } var result = await connection.ExecuteAsync(sqlExpression.Script, entity); return result > 0; } /// /// 批量插入数据 /// /// /// /// /// /// 返回sql语句 /// -1 参数为空 public static async Task InsertAsync(this IDbConnection connection, string tableName, IEnumerable entities, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if ((entities?.Count() ?? 0) <= 0) throw new ArgumentNullException(nameof(entities)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Insert(dbType, tableName); outSqlAction?.Invoke(sqlExpression.Script); var result = await connection.ExecuteAsync(sqlExpression.Script, entities); return result; } /// /// 删除数据 /// /// /// /// /// /// 返回sql语句 /// -1 参数为空 public static async Task DeleteAsync(this IDbConnection connection, string tableName, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (whereExpress == null) throw new ArgumentNullException(nameof(whereExpress)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Delete(dbType, tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); var result = await connection.ExecuteAsync(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 对象修改 /// /// /// /// /// /// 选择字段 /// 返回sql语句 /// public static async Task SetAsync(this IDbConnection connection, string tableName, TEntity entity, IEnumerable fields = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (entity == null) throw new ArgumentNullException(nameof(entity)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Update(dbType, fields, tableName); outSqlAction?.Invoke(sqlExpression.Script); var result = await connection.ExecuteAsync(sqlExpression.Script, entity); return result > 0; } /// /// 条件修改 /// /// /// 连接 /// 表名 /// 修改内容表达式 /// 条件表达式 /// 返回sql语句 /// public static async Task SetAsync(this IDbConnection connection, string tableName, Expression> setExpress, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (setExpress == null || whereExpress == null) throw new ArgumentNullException($"{nameof(setExpress)} / {nameof(whereExpress)}"); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Update(dbType, setExpress, tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = await connection.ExecuteAsync(sqlExpression.Script, sqlExpression.DbParams); return result > 0; } /// /// 条件修改 在字段上增减 /// /// /// /// 连接 /// 表名 /// 增减的字段 /// 增减的值 /// 条件表达式 /// 返回sql语句 /// public static async Task IncrAsync(this IDbConnection connection, string tableName, string field, TValue value, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (string.IsNullOrEmpty(field)) throw new ArgumentNullException(field, "增减字段不能为空"); var dbType = connection.GetDbType(); var setExpressString = $"{field.ParamSql(dbType)} = {field.ParamSql(dbType)} + ({value})"; var sqlExpression = SqlExpression.Update(dbType, () => setExpressString, tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = await connection.ExecuteAsync(sqlExpression.Script, sqlExpression.DbParams); return result > 0; } /// /// 获取单条数据 /// /// /// /// 表名 /// 条件表达式 /// 选择字段,默认为* /// 返回sql语句 /// public static async Task GetAsync(this IDbConnection connection, string tableName, Expression> whereExpress, Expression> fieldExpress = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (whereExpress == null) throw new ArgumentNullException(nameof(whereExpress)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Select(dbType, fieldExpress, tableName).Where(whereExpress); sqlExpression = sqlExpression.TopOne(); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = await connection.QueryFirstOrDefaultAsync(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 获取分页数据 /// /// /// /// /// /// /// 条件表达式 /// 选择字段,默认为* /// 排序字段集合 /// 返回sql语句 /// public static async Task> GetListAsync(this IDbConnection connection, string tableName, int page, int rows, Expression> whereExpress, Expression> fieldExpress = null, List orderByFields = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Select(dbType, fieldExpress, tableName); if (whereExpress != null) sqlExpression.Where(whereExpress); var orderBy = string.Empty; if ((orderByFields?.Count ?? 0) > 0) orderBy = $" {string.Join(", ", orderByFields.Select(oo => oo.Field.ParamSql(dbType) + " " + oo.OrderBy))}"; sqlExpression.OrderBy(orderBy).Limit(page, rows); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = await connection.QueryAsync(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 获取分页数据 Offset /// /// /// /// /// /// /// 条件表达式 /// 选择字段,默认为* /// 排序字段集合 /// 返回sql语句 /// public static async Task> GetOffsetsAsync(this IDbConnection connection, string tableName, int offset, int size, Expression> whereExpress, Expression> fieldExpress = null, List orderByFields = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Select(dbType, fieldExpress, tableName); if (whereExpress != null) sqlExpression.Where(whereExpress); var orderBy = string.Empty; if ((orderByFields?.Count ?? 0) > 0) orderBy = $" {string.Join(", ", orderByFields.Select(oo => oo.Field.ParamSql(dbType) + " " + oo.OrderBy))}"; sqlExpression.OrderBy(orderBy).Offset(offset, size); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = await connection.QueryAsync(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 获取数量 /// /// /// /// /// 条件表达式 /// 返回sql语句 /// public static async Task CountAsync(this IDbConnection connection, string tableName, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Count(dbType, tableName: tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = await connection.QueryFirstOrDefaultAsync(sqlExpression.Script, sqlExpression.DbParams); return result; } #endregion } } ================================================ FILE: src/Overt.Core.Data/Extensions/Dapper.Extensions.cs ================================================ using Dapper; using MySql.Data.MySqlClient; using Overt.Core.Data.Expressions; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Security.Cryptography; using System.Text; namespace Overt.Core.Data { /// /// Dapper扩展 /// public static partial class DapperExtensions { #region TableName /// /// 获取主表名称 /// /// /// public static string GetMainTableName(this Type entity) { var attribute = entity.GetAttribute(); string mTableName; if (attribute == null) mTableName = entity.Name; else mTableName = attribute.Name; return mTableName; } /// /// 获取表名 /// /// /// /// public static string GetTableName(this string val, Func tableNameFunc = null) where TEntity : class, new() { if (tableNameFunc != null) return tableNameFunc.Invoke(); var t = typeof(TEntity); var mTableName = t.GetMainTableName(); var propertyInfo = t.GetProperty(); if (propertyInfo == null) // 代表没有分表特性 return mTableName; // 获取分表 var suffix = propertyInfo.GetSuffix(val); return $"{mTableName}_{suffix}"; } /// /// 获取表名 /// /// 实体实例 /// /// public static string GetTableName(this TEntity entity, Func tableNameFunc = null) where TEntity : class, new() { if (tableNameFunc != null) return tableNameFunc.Invoke(); var t = typeof(TEntity); var mTableName = t.GetMainTableName(); var propertyInfo = t.GetProperty(); if (propertyInfo == null) // 代表没有分表特性 return mTableName; // 获取分表 var suffix = propertyInfo.GetSuffix(entity); return $"{mTableName}_{suffix}"; } /// /// 获取表名 /// /// 表达式数据 /// /// public static string GetTableName(this Expression> expression, Func tableNameFunc = null) where TEntity : class, new() { if (tableNameFunc != null) return tableNameFunc.Invoke(); var t = typeof(TEntity); var mTableName = t.GetMainTableName(); var propertyInfo = t.GetProperty(); if (propertyInfo == null) // 代表没有分表特性 return mTableName; // 获取分表 var suffix = propertyInfo.GetSuffix(expression); return $"{mTableName}_{suffix}"; } #endregion #region Field /// /// 获取自增字段 /// /// /// public static PropertyInfo GetIdentityField(this Type type) { var propertyInfos = type.GetProperties(); if ((propertyInfos?.Count ?? 0) <= 0) // 代表没有主键 return null; foreach (var pi in propertyInfos) { var attribute = pi.GetAttribute(); if (attribute != null && attribute.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity) { return pi; } } return null; } /// /// 获取自定义类型的字段列表 /// /// /// public static List GetCustomFields(this Type type) { var propertyInfos = type.GetProperties(); if ((propertyInfos?.Count ?? 0) <= 0) // 代表没有自定义字段 return null; var result = new List(); foreach (var pi in propertyInfos) { var attribute = pi.GetAttribute(); if (attribute != null) { result.Add(pi); } } return result; } #endregion #region Public Method /// /// 是否存在表 /// /// /// /// /// public static bool IsExistTable(this IDbConnection connection, string tableName, Action outSqlAction = null) { if (string.IsNullOrEmpty(tableName)) return false; var dbType = connection.GetDbType(); var dbName = connection.Database; var sql = dbType.ExistTableSql(dbName, tableName); outSqlAction?.Invoke(sql); // 返回sql var result = connection.QueryFirstOrDefault(sql); return result > 0; } /// /// 是否存在字段 /// /// /// /// /// /// public static bool IsExistField(this IDbConnection connection, string tableName, string fieldName, Action outSqlAction = null) { if (string.IsNullOrEmpty(tableName)) return false; var dbType = connection.GetDbType(); var dbName = connection.Database; var sql = dbType.ExistFieldSql(dbName, tableName, fieldName); outSqlAction?.Invoke(sql); var result = connection.QueryFirstOrDefault(sql); return result > 0; } /// /// 插入数据 /// /// /// /// /// /// 是否返回自增的数据 /// 返回sql语句 /// -1 参数为空 public static bool Insert(this IDbConnection connection, string tableName, TEntity entity, bool returnLastIdentity = false, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (entity == null) throw new ArgumentNullException(nameof(entity)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Insert(dbType, tableName, returnLastIdentity); outSqlAction?.Invoke(sqlExpression.Script); var identityPI = typeof(TEntity).GetIdentityField(); if (identityPI != null && returnLastIdentity) { if (identityPI.PropertyType == typeof(int)) { var intResult = connection.ExecuteScalar(sqlExpression.Script, entity); if (intResult > 0) identityPI.SetValue(entity, intResult); return intResult > 0; } var longResult = connection.ExecuteScalar(sqlExpression.Script, entity); if (longResult > 0) identityPI.SetValue(entity, longResult); return longResult > 0; } var result = connection.Execute(sqlExpression.Script, entity); return result > 0; } /// /// 批量插入数据 /// /// /// /// /// /// 返回sql语句 /// 执行条数 public static int Insert(this IDbConnection connection, string tableName, IEnumerable entities, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if ((entities?.Count() ?? 0) <= 0) throw new ArgumentNullException(nameof(entities)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Insert(dbType, tableName); outSqlAction?.Invoke(sqlExpression.Script); var result = connection.Execute(sqlExpression.Script, entities); return result; } /// /// 删除数据 /// /// /// /// /// /// 返回sql语句 /// -1 参数为空 public static int Delete(this IDbConnection connection, string tableName, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (whereExpress == null) throw new ArgumentNullException(nameof(whereExpress)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Delete(dbType, tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); var result = connection.Execute(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 对象修改 /// /// /// /// /// /// 选择字段 /// 返回sql语句 /// public static bool Set(this IDbConnection connection, string tableName, TEntity entity, IEnumerable fields = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (entity == null) throw new ArgumentNullException(nameof(entity)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Update(dbType, fields, tableName); outSqlAction?.Invoke(sqlExpression.Script); var result = connection.Execute(sqlExpression.Script, entity); return result > 0; } /// /// 条件修改 /// /// /// 连接 /// 表名 /// 修改内容表达式 /// 条件表达式 /// 返回sql语句 /// public static bool Set(this IDbConnection connection, string tableName, Expression> setExpress, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (setExpress == null || whereExpress == null) throw new ArgumentNullException($"{nameof(setExpress)} / {nameof(whereExpress)}"); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Update(dbType, setExpress, tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = connection.Execute(sqlExpression.Script, sqlExpression.DbParams); return result > 0; } /// /// 条件修改 在字段上增减 /// /// /// /// 连接 /// 表名 /// 增减的字段 /// 增减的值 /// 条件表达式 /// 返回sql语句 /// public static bool Incr(this IDbConnection connection, string tableName, string field, TValue value, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (string.IsNullOrEmpty(field)) throw new ArgumentNullException(field, "增减字段不能为空"); var dbType = connection.GetDbType(); var setExpressString = $"{field.ParamSql(dbType)} = {field.ParamSql(dbType)} + ({value})"; var sqlExpression = SqlExpression.Update(dbType, () => setExpressString, tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = connection.Execute(sqlExpression.Script, sqlExpression.DbParams); return result > 0; } /// /// 获取单条数据 /// /// /// /// 表名 /// 条件表达式 /// 选择字段,默认为* /// 返回sql语句 /// public static TEntity Get(this IDbConnection connection, string tableName, Expression> whereExpress, Expression> fieldExpress = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); if (whereExpress == null) throw new ArgumentNullException(nameof(whereExpress)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Select(dbType, fieldExpress, tableName).Where(whereExpress); sqlExpression = sqlExpression.TopOne(); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = connection.QueryFirstOrDefault(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 获取分页数据 /// /// /// /// /// /// /// 条件表达式 /// 选择字段,默认为* /// 排序字段集合 /// 返回sql语句 /// public static IEnumerable GetList(this IDbConnection connection, string tableName, int page, int rows, Expression> whereExpress, Expression> fieldExpress = null, List orderByFields = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Select(dbType, fieldExpress, tableName); if (whereExpress != null) sqlExpression.Where(whereExpress); var orderBy = string.Empty; if ((orderByFields?.Count ?? 0) > 0) orderBy = $" {string.Join(", ", orderByFields.Select(oo => oo.Field.ParamSql(dbType) + " " + oo.OrderBy))}"; sqlExpression.OrderBy(orderBy).Limit(page, rows); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = connection.Query(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 获取分页数据 Offset /// /// /// /// /// /// /// 条件表达式 /// 选择字段,默认为* /// 排序字段集合 /// 返回sql语句 /// public static IEnumerable GetOffsets(this IDbConnection connection, string tableName, int offset, int size, Expression> whereExpress, Expression> fieldExpress = null, List orderByFields = null, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Select(dbType, fieldExpress, tableName); if (whereExpress != null) sqlExpression.Where(whereExpress); var orderBy = string.Empty; if ((orderByFields?.Count ?? 0) > 0) orderBy = $" {string.Join(", ", orderByFields.Select(oo => oo.Field.ParamSql(dbType) + " " + oo.OrderBy))}"; sqlExpression.OrderBy(orderBy).Offset(offset, size); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = connection.Query(sqlExpression.Script, sqlExpression.DbParams); return result; } /// /// 获取数量 /// /// /// /// /// 条件表达式 /// 返回sql语句 /// public static int Count(this IDbConnection connection, string tableName, Expression> whereExpress, Action outSqlAction = null) where TEntity : class, new() { if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); var dbType = connection.GetDbType(); var sqlExpression = SqlExpression.Count(dbType, tableName: tableName).Where(whereExpress); outSqlAction?.Invoke(sqlExpression.Script); // 返回sql var result = connection.QueryFirstOrDefault(sqlExpression.Script, sqlExpression.DbParams); return result; } #endregion #region Private Method static ConcurrentDictionary MSSqlDbType = new ConcurrentDictionary(); /// /// 获取db类型 /// /// /// internal static DatabaseType GetDbType(this IDbConnection connection) { if (connection is MySqlConnection) return DatabaseType.MySql; if (connection is SqlConnection) { return MSSqlDbType.GetOrAdd(connection.ConnectionString, (connectionString) => { var sqlConnection = (SqlConnection)connection; var v = sqlConnection.ServerVersion; int.TryParse(v.Substring(0, v.IndexOf(".")), out int bV); if (bV >= Constants.MSSQLVersion.SQLServer2012Bv) return DatabaseType.GteSqlServer2012; return DatabaseType.SqlServer; }); } #if ASP_NET_CORE if (connection is Microsoft.Data.Sqlite.SqliteConnection) #else if (connection is System.Data.SQLite.SQLiteConnection) #endif return DatabaseType.SQLite; if (connection is Npgsql.NpgsqlConnection) return DatabaseType.PostgreSQL; return DatabaseType.MySql; } /// /// 获取值 /// /// /// /// /// internal static object GetValueFromExpression(this PropertyInfo propertyInfo, Expression> expression) { var dictionary = new Dictionary(); ExpressionHelper.Resolve(expression.Body, ref dictionary); if ((dictionary?.Count ?? 0) <= 0) throw new ArgumentNullException($"Property [{propertyInfo.Name}] 数据为空"); dictionary.TryGetValue(propertyInfo.Name, out object val); return val; } /// /// 获取位数 /// /// /// [Obsolete("请使用TableNameFunc")] internal static int GetBit(this PropertyInfo propertyInfo) { if (propertyInfo == null) return -1; var bit = ((SubmeterAttribute)propertyInfo.GetCustomAttribute(typeof(SubmeterAttribute)))?.Bit ?? -1; return bit; } /// /// 获取后缀 /// /// /// /// [Obsolete("请使用TableNameFunc")] internal static string GetSuffix(string val, int bit = 2) { if (string.IsNullOrEmpty(val)) throw new ArgumentNullException($"分表数据为空"); if (bit <= 0) throw new ArgumentOutOfRangeException("length", "length必须是大于零的值。"); var result = Encoding.Default.GetBytes(val.ToString()); //tbPass为输入密码的文本框 var md5Provider = new MD5CryptoServiceProvider(); var output = md5Provider.ComputeHash(result); var hash = BitConverter.ToString(output).Replace("-", ""); //tbMd5pass为输出加密文本 var suffix = hash.Substring(0, bit).ToUpper(); return suffix; } /// /// 获取分表名 base md5 /// /// /// /// [Obsolete("请使用TableNameFunc")] internal static string GetSuffix(this PropertyInfo propertyInfo, string val) { var bit = propertyInfo.GetBit(); return GetSuffix(val.ToString(), bit); } /// /// 获取分表名 base md5 /// /// /// /// /// [Obsolete("请使用TableNameFunc")] internal static string GetSuffix(this PropertyInfo propertyInfo, TEntity entity) where TEntity : class, new() { var val = propertyInfo.GetValue(entity); var bit = propertyInfo.GetBit(); return GetSuffix(val.ToString(), bit); } /// /// 获取分表名 base md5 /// /// /// /// 表达式数据 /// [Obsolete("请使用TableNameFunc")] internal static string GetSuffix(this PropertyInfo propertyInfo, Expression> expression) where TEntity : class, new() { var val = propertyInfo.GetValueFromExpression(expression); var bit = propertyInfo.GetBit(); return GetSuffix(val.ToString(), bit); } #endregion } } ================================================ FILE: src/Overt.Core.Data/Extensions/EntityDefinition.Extensions.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Overt.Core.Data { /// /// 字段定义说明 /// public static class EntityDefinitionExtensions { private static ConcurrentDictionary> _cacheSubmeter = new ConcurrentDictionary>(); /// /// /// /// 属性 /// /// internal static TAttribute GetAttribute(this Type entity) where TAttribute : Attribute { var attrs = entity.GetCustomAttributes(typeof(TAttribute)); if (attrs?.Count() > 0) return (TAttribute)attrs.First(); return null; } /// /// /// /// 属性 /// /// internal static TAttribute GetAttribute(this PropertyInfo property) where TAttribute : Attribute { var obs = property.GetCustomAttributes(typeof(TAttribute), false); if (obs?.Count() > 0) return (TAttribute)obs.First(); return null; } /// /// /// /// /// /// internal static PropertyInfo GetProperty(this Type entity) where TAttribute : Attribute { var propertyInfos = _cacheSubmeter.GetOrAdd($"{entity.Name}_{typeof(TAttribute).Name}", key => entity.GetPropertyByAttribute()); return propertyInfos.FirstOrDefault(); } /// /// /// /// /// /// internal static List GetProperties(this Type entity) where TAttribute : Attribute { var propertyInfos = _cacheSubmeter.GetOrAdd($"{entity.Name}_{typeof(TAttribute).Name}", key => entity.GetPropertyByAttribute()); return propertyInfos; } /// /// /// /// 属性 /// /// internal static List GetPropertyByAttribute(this Type entity) where TAttribute : Attribute { var list = new List(); var pis = entity.GetProperties(); foreach (var item in pis) { var obs = item.GetCustomAttributes(typeof(TAttribute), false); if (obs?.Count() > 0) list.Add(item); } return list; } } } ================================================ FILE: src/Overt.Core.Data/Extensions/Expression.Extensions.cs ================================================ using System; using System.Linq; using System.Linq.Expressions; namespace Overt.Core.Data { /// /// expression 扩展 /// public static class ExpressionExtensions { /// /// and /// /// /// /// /// public static Expression> And(this Expression> expr1, Expression> expr2) { var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); return Expression.Lambda> (Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters); } /// /// or /// /// /// /// /// public static Expression> Or(this Expression> expr1, Expression> expr2) { var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); return Expression.Lambda> (Expression.OrElse(expr1.Body, expr2.Body), expr1.Parameters); } } } ================================================ FILE: src/Overt.Core.Data/Extensions/SqlAlias.Extensions.cs ================================================ using Overt.Core.Data.Expressions; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; namespace Overt.Core.Data { /// /// Sql别名 /// public static class SqlAliasExtensions { /// /// 参数前缀 /// /// /// public static string ParamPrefix(this DatabaseType dbType) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: return "@"; case DatabaseType.MySql: return "?"; case DatabaseType.SQLite: return "@"; case DatabaseType.PostgreSQL: return "@"; default: return string.Empty; } } /// /// 获取添加左右标记 防止有关键字作为字段名/表名 /// /// /// /// public static string ParamSql(this string columnName, SqlGenerate sqlGenerate) { return columnName.ParamSql(sqlGenerate?.DatabaseType); } /// /// 获取添加左右标记 防止有关键字作为字段名/表名 /// /// /// /// public static string ParamSql(this string columnName, DatabaseType? dbType) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: if (columnName.StartsWith("[")) return columnName; return $"[{columnName}]"; case DatabaseType.MySql: if (columnName.StartsWith("`")) return columnName; return $"`{columnName}`"; case DatabaseType.SQLite: if (columnName.StartsWith("`")) return columnName; return $"`{columnName}`"; case DatabaseType.PostgreSQL: if (columnName.StartsWith("\"")) return columnName; return $"\"{columnName}\""; default: return columnName; } } /// /// 获取添加字段 /// /// /// /// /// public static string ParamValue(this string columnName, DatabaseType? dbType, List customFields) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: return $"@{columnName}"; case DatabaseType.MySql: return $"@{columnName}"; case DatabaseType.SQLite: return $"@{columnName}"; case DatabaseType.PostgreSQL: var customPi = customFields?.FirstOrDefault(p=>p.Name.Equals(columnName)); if (customPi != null) { var attribute = customPi.GetAttribute(); if (attribute != null && attribute.CustomDataType.ToLower() == DataCustomType.Jsonb.ToString().ToLower()) return $"CAST(@{columnName} AS json)"; return $"@{columnName}"; } return $"@{columnName}"; default: return $"@{columnName}"; } } /// /// 获取最后一次Insert /// /// /// public static string SelectLastIdentity(this DatabaseType dbType) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: return " select @@Identity"; case DatabaseType.MySql: return " select LAST_INSERT_ID();"; case DatabaseType.SQLite: return " select last_insert_rowid();"; case DatabaseType.PostgreSQL: return " select LASTVAL();"; default: return string.Empty; } } /// /// 是否存在表 /// /// /// /// /// public static string ExistTableSql(this DatabaseType dbType, string dbName, string tableName) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: return $"select count(1) from sys.tables where name='{tableName}' and type = 'u'"; case DatabaseType.MySql: return $"select count(1) from information_schema.tables where table_schema = '{dbName}' and table_name = '{tableName}'"; case DatabaseType.SQLite: return $"select count(1) from sqlite_master where type = 'table' and name='{tableName}'"; case DatabaseType.PostgreSQL: return $"select count(1) from pg_class where relname = '{tableName}';"; default: return string.Empty; } } /// /// 是否存在字段 /// /// /// /// /// /// public static string ExistFieldSql(this DatabaseType dbType, string dbName, string tableName, string fieldName) { switch (dbType) { case DatabaseType.SqlServer: case DatabaseType.GteSqlServer2012: return $"select count(1) sys.columns where object_id = object_id('{tableName}') and name='{fieldName}'"; case DatabaseType.MySql: return $"select count(1) from information_schema.columns where table_schema = '{dbName}' and table_name = '{tableName}' and column_name = '{fieldName}'"; case DatabaseType.SQLite: return $"select * from sqlite_master where name='{tableName}' and sql like '%{fieldName}%';"; case DatabaseType.PostgreSQL: return $"select count(1) from information_schema.columns WHERE table_schema = '{dbName}' and table_name = '{tableName}' and column_name = '{fieldName}'"; default: return string.Empty; } } } } ================================================ FILE: src/Overt.Core.Data/Extensions/Table.Extensions.cs ================================================ using Dapper; using System; using System.Collections.Concurrent; using System.Transactions; namespace Overt.Core.Data { /// /// 表管理 /// public static class TableExtensions { private static ConcurrentDictionary lockObjectMap = new ConcurrentDictionary(); private static ConcurrentDictionary tableExistMap = new ConcurrentDictionary(); /// /// 检查表是否存在 /// /// /// /// /// false, 直接返回默认数据即可 public static bool CheckTableIfMissingCreate(this IBaseRepository repository, bool isMaster) where T : class, new() { var tableName = repository.TableNameFunc?.Invoke(); var createScript = repository.CreateScriptFunc?.Invoke(tableName); if (string.IsNullOrEmpty(createScript)) return true; if (string.IsNullOrEmpty(tableName)) throw new Exception($"CheckTableIfMissingCreate: TableNameFunc 必须提供"); var tableKey = $"{repository.DbStoreKey}_{tableName}_{isMaster}"; lock (lockObjectMap.GetOrAdd(tableKey, new object())) { var existTable = tableExistMap.GetOrAdd(tableKey, k => repository.IsExistTable(tableName, isMaster)); if (existTable) return true; tableExistMap.TryRemove(tableKey, out existTable); if (!isMaster) return false; try { using (var scope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var connection = repository.OpenConnection(true)) { connection.Execute(createScript); } return true; } catch (Exception ex) { throw ex; } } } } } ================================================ FILE: src/Overt.Core.Data/Overt.Core.Data.csproj ================================================  net461;netstandard2.0 Overt.Core.Data Yaofeng Overt.Core.Data false lambda表达式解析增加 !Contains 生成 not like Overt true 1.0.0.0 1.0.0.0 2.3.3 http://github.com/overtly/core-data Overt.Core.Data.xml bin\Release\netstandard2.0\Overt.Core.Data.xml TRACE;DEBUG;NET461 ASP.NET Core 2 integration for Overt.Core.Data - .NET Standard 2 $(DefineConstants);ASP_NET_CORE;ASP_NET_CORE2 ================================================ FILE: src/Overt.Core.Data/Overt.Core.Data.xml ================================================ Overt.Core.Data 分表标识 16进制位数 1 16 2 256 3 4096 ... SQLServer2012版本 接口 获取主表名 获取表名:内部会调用TableNameFunc 从主库中查询 获取表名:以Submeter分表位数获取表名 是否存在表 是否存在字段 添加 数据实体 是否赋值最后一次的自增ID 添加后的数据实体 批量添加 数据实体 bool 删除 删除条件 是否成功 更新 数据实体 x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } 是否成功 根据字段修改 修改字段表达式 object =>dynamic 是一个匿名类 条件表达式 是否成功 根据条件 在原字段上增减数据 增减的字段 增减的值 条件表达式 获取一条数据 查询条件 按字段返回 是否主从 实体 获取列表 条件表达式 按字段返回 是否主从 排序字段集合 获取列表 Offset 条件表达式 按字段返回 是否主从 排序字段集合 获取数量 条件表达式 是否主从 是否存在表 是否存在字段 异步添加 是否赋值最后一次的自增ID 异步批量添加 数据实体 bool 异步删除 删除条件 是否成功 异步更新 数据实体 x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } 是否成功 异步根据字段修改 修改字段表达式 条件表达式 是否成功 根据条件 在原字段上增减数据 增减的字段 增减的值 条件表达式 异步获取一条数据 查询条件 按字段返回 是否主从 实体 异步获取列表 条件表达式 按字段返回 是否主从 排序字段集合 异步获取列表 Offset 条件表达式 按字段返回 是否主从 排序字段集合 异步获取数量 条件表达式 是否主从 通用的接口 数据库key 表名生成方法 创建表的sql语句 参数:表名 连接方法创建 打开连接 执行的sql脚本 数据库连接工具类 连接对象 构造函数 是否从库 存储字符串标识 连接字符串Func 打开链接 获取Factory 垃圾回收 连接配置信息获取 1. master / secondary 2. xx.master / xx.secondary 单例模式 构造函数 获取连接字符串 获取 不能包含点 数据库类型 SqlServer >=SqlServer2012 Mysql Sqlite PostgreSQL 自定义字段类型 未定义 jsonb类型 字段排序类型 顺序 倒序 解析为 Dictionary 获取表达式中的key - value 只需要有等于号的 获取参数 表达式扩展 是否为空类型 是否是 是否是泛型 Determines whether the expression is the parameter. Returns true if the specified expression is parameter; otherwise, false. 检查类型 是否是Boolean 获取Value 获取Fields BaseSqlExpression Update Select Where In OrderBy Max Min Avg Count Sum Update Select Where In OrderBy Max Min Avg Count Sum 是否是静态集合方法 是否是集合方法 interface Update Select Where In OrderBy Max Min Avg Count Sum 是否是集合方法 Expression => Sql 删除 数据库类型 修改 数据库类型 修改 数据库类型 查询 数据库类型 数量 数据库类型 最大 数据库类型 最小 数据库类型 平均值 数据库类型 求和 数据库类型 参数编译器 base ServiceStack 编译 编译 Expression核心 脚本 参数 构造函数 清除 新增 查询 排序字段 Where条件 OrderBy Top1 Limit Offset 最大 最小值 平均值 行数 总计 删除 修改 修改 base ServiceStack SqlGenrate 字段 表名 字段字符串 sql长度 脚本 数据库类型 数据库参数 索引数据 构造函数 清除 替换最后一次出现字符 添加参数 拼接in参数 ToString Dapper扩展 Dapper扩展 是否存在表 是否存在字段 插入数据 是否返回自增的数据 返回sql语句 -1 参数为空 批量插入数据 返回sql语句 -1 参数为空 删除数据 返回sql语句 -1 参数为空 对象修改 选择字段 返回sql语句 条件修改 连接 表名 修改内容表达式 条件表达式 返回sql语句 条件修改 在字段上增减 连接 表名 增减的字段 增减的值 条件表达式 返回sql语句 获取单条数据 表名 条件表达式 选择字段,默认为* 返回sql语句 获取分页数据 条件表达式 选择字段,默认为* 排序字段集合 返回sql语句 获取分页数据 Offset 条件表达式 选择字段,默认为* 排序字段集合 返回sql语句 获取数量 条件表达式 返回sql语句 获取主表名称 获取表名 获取表名 实体实例 获取表名 表达式数据 获取自增字段 获取自定义类型的字段列表 是否存在表 是否存在字段 插入数据 是否返回自增的数据 返回sql语句 -1 参数为空 批量插入数据 返回sql语句 执行条数 删除数据 返回sql语句 -1 参数为空 对象修改 选择字段 返回sql语句 条件修改 连接 表名 修改内容表达式 条件表达式 返回sql语句 条件修改 在字段上增减 连接 表名 增减的字段 增减的值 条件表达式 返回sql语句 获取单条数据 表名 条件表达式 选择字段,默认为* 返回sql语句 获取分页数据 条件表达式 选择字段,默认为* 排序字段集合 返回sql语句 获取分页数据 Offset 条件表达式 选择字段,默认为* 排序字段集合 返回sql语句 获取数量 条件表达式 返回sql语句 获取db类型 获取值 获取位数 获取后缀 获取分表名 base md5 获取分表名 base md5 获取分表名 base md5 表达式数据 字段定义说明 属性 属性 属性 expression 扩展 and or Sql别名 参数前缀 获取添加左右标记 防止有关键字作为字段名/表名 获取添加左右标记 防止有关键字作为字段名/表名 获取添加字段 获取最后一次Insert 是否存在表 是否存在字段 表管理 检查表是否存在 false, 直接返回默认数据即可 排序字段对象 字段 使用nameof(Class.Property) 排序类型 构造函数 静态构造对象 扩展类 顺序构造对象扩展方法 倒序构造对象扩展方法 实现IBaseRepository 构造函数 数据库前缀 获取主表名 获取表名,调用TableNameFunc则是调用主库查询 获取表名,以Submeter分表标识值取模 是否存在表 是否存在字段 添加 数据实体 是否赋值最后一次的自增ID 添加后的数据实体 添加 数据实体 添加后的数据实体 删除 删除条件 是否成功 更新 数据实体 x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } 是否成功 根据条件 在原字段上增减数据 增减的字段 增减的值 条件表达式 查找数据 查询条件 按字段返回 是否主从 实体 获取列表 条件表达式 按字段返回 是否主从 排序字段集合 获取列表 Offset 条件表达式 按字段返回 是否主从 排序字段集合 获取数量 条件表达式 是否主从 是否存在表 是否存在字段 异步添加 是否赋值最后一次的自增ID 异步批量添加 异步删除 删除条件 是否成功 异步更新 数据实体 x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } 是否成功 根据条件 在原字段上增减数据 增减的字段,底层未验证改字段是否存在,由数据库异常抛出 增减的值 条件表达式 是否成功 异步获取一条数据 查询条件 按字段返回 是否主从 实体 异步获取列表 条件表达式 按字段返回 是否主从 排序字段集合 异步获取列表 Offset 条件表达式 按字段返回 是否主从 排序字段集合 异步获取数量 条件表达式 是否主从 包含connection的方法执行 包含connection的方法执行 填充ExecuteScript DbRespository 抽象类 实例化 数据库前缀 数据库名key 打开连接 已赋值 connection 属性 数据库连接方法 表名方法 创建表的脚本 执行的sql脚本 ================================================ FILE: src/Overt.Core.Data/Params/OrderByField.cs ================================================ namespace Overt.Core.Data { /// /// 排序字段对象 /// public class OrderByField { /// /// 字段 使用nameof(Class.Property) /// public string Field { get; set; } /// /// 排序类型 /// public FieldSortType OrderBy { get; set; } /// /// 构造函数 /// /// /// public OrderByField(string field, FieldSortType orderBy = FieldSortType.Asc) { Field = field; OrderBy = orderBy; } /// /// 静态构造对象 /// /// /// /// public static OrderByField Create(string field, FieldSortType orderBy = FieldSortType.Asc) { return new OrderByField(field, orderBy); } } /// /// 扩展类 /// public static class OrderByFieldExtension { /// /// 顺序构造对象扩展方法 /// /// /// public static OrderByField OrderBy(this string field) { return new OrderByField(field, FieldSortType.Asc); } /// /// 倒序构造对象扩展方法 /// /// /// public static OrderByField OrderByDesc(this string field) { return new OrderByField(field, FieldSortType.Desc); } } } ================================================ FILE: src/Overt.Core.Data/Repository/BaseRepository.cs ================================================ #if ASP_NET_CORE using Microsoft.Extensions.Configuration; #endif using Overt.Core.Data.Expressions; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; namespace Overt.Core.Data { /// /// 实现IBaseRepository /// public abstract class BaseRepository : PropertyAssist, IBaseRepository where TEntity : class, new() { #region Constructor #if ASP_NET_CORE /// /// 构造函数 /// /// 配置注入 /// 数据库前缀 public BaseRepository(IConfiguration configuration, string dbStoreKey = "") : base(configuration, dbStoreKey) { } #else /// /// 构造函数 /// /// 数据库前缀 public BaseRepository(string dbStoreKey = "") : base(dbStoreKey) { } #endif #endregion #region Sync Method /// /// 获取主表名 /// /// public string GetMainTableName() { return typeof(TEntity).GetMainTableName(); } /// /// 获取表名,调用TableNameFunc则是调用主库查询 /// /// public string GetTableName() { var tableName = TableNameFunc?.Invoke() ?? typeof(TEntity).GetMainTableName(); return tableName; } /// /// 获取表名,以Submeter分表标识值取模 /// /// /// [Obsolete("请使用GetTableName()")] public string GetTableName(string key) { var tableName = key.GetTableName(TableNameFunc); return tableName; } /// /// 是否存在表 /// /// /// /// public bool IsExistTable(string tableName, bool isMaster = true) { if (string.IsNullOrEmpty(tableName)) return false; using (var connection = OpenConnection(isMaster)) { return connection.IsExistTable(tableName, OutSqlAction); } } /// /// 是否存在字段 /// /// /// /// /// public bool IsExistField(string tableName, string fieldName, bool isMaster = true) { if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(fieldName)) return false; using (var conneciton = OpenConnection(isMaster)) { return conneciton.IsExistField(tableName, fieldName, OutSqlAction); } } /// /// 添加 /// /// 数据实体 /// 是否赋值最后一次的自增ID /// 添加后的数据实体 public bool Add(TEntity entity, bool returnLastIdentity = false) { if (entity == null) return false; return Execute((connection) => { var tableName = entity.GetTableName(TableNameFunc); var result = connection.Insert(tableName, entity, returnLastIdentity, OutSqlAction); return result; }, true); } /// /// 添加 /// /// 数据实体 /// 添加后的数据实体 public bool Add(params TEntity[] entities) { if ((entities?.Count() ?? 0) <= 0) return false; return Execute((connection) => { var tableName = entities.First().GetTableName(TableNameFunc); var result = connection.Insert(tableName, entities, OutSqlAction); return result > 0; }, true); } /// /// 删除 /// /// 删除条件 /// 是否成功 public bool Delete(Expression> expression) { return Execute((connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = connection.Delete(tableName, expression, OutSqlAction); return task > 0; }, true); } /// /// 更新 /// /// 数据实体 /// x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } /// 是否成功 public bool Set(TEntity entity, Expression> fields = null) { if (entity == null) return false; return Execute((connection) => { var tableName = entity.GetTableName(TableNameFunc); var fieldNames = fields.GetFieldNames()?.ToList(); var task = connection.Set(tableName, entity, fieldNames, OutSqlAction); return task; }, true); } /// /// 根据字段修改 /// /// 修改字段表达式:() => new { SomeProperty1 = "", SomeProperty2 = "" } or Dictionary /// 条件表达式 /// 是否成功 public bool Set(Expression> setExpress, Expression> whereExpress) { if (setExpress == null || whereExpress == null) return false; return Execute((connection) => { var tableName = whereExpress.GetTableName(TableNameFunc); var task = connection.Set(tableName, setExpress, whereExpress, OutSqlAction); return task; }, true); } /// /// 根据条件 在原字段上增减数据 /// /// /// 增减的字段 /// 增减的值 /// 条件表达式 /// public bool Incr(string field, TValue value, Expression> whereExpress) where TValue : struct { if (string.IsNullOrWhiteSpace(field)) throw new ArgumentNullException(nameof(field), "字段值必须提供"); return Execute((connection) => { var tableName = whereExpress.GetTableName(TableNameFunc); var task = connection.Incr(tableName, field, value, whereExpress, OutSqlAction); return task; }, true); } /// /// 查找数据 /// /// 查询条件 /// 按字段返回 /// 是否主从 /// 实体 public TEntity Get(Expression> expression, Expression> fieldExpressison = null, bool isMaster = false) { if (expression == null) return default(TEntity); return Execute((connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = connection.Get(tableName, expression, fieldExpressison, OutSqlAction); return task; }, isMaster); } /// /// 获取列表 /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// public IEnumerable GetList( int page, int rows, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields) { if (page <= 0 || rows <= 0) return default(IEnumerable); return Execute((connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = connection.GetList(tableName, page, rows, expression, fieldExpressison, orderByFields?.ToList(), OutSqlAction); return task; }, isMaster); } /// /// 获取列表 Offset /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// public IEnumerable GetOffsets( int offset, int size, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields) { if (offset < 0 || size <= 0) return default(IEnumerable); return Execute((connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = connection.GetOffsets(tableName, offset, size, expression, fieldExpressison, orderByFields?.ToList(), OutSqlAction); return task; }, isMaster); } /// /// 获取数量 /// /// 条件表达式 /// 是否主从 /// public int Count(Expression> expression = null, bool isMaster = false) { return Execute((connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = connection.Count(tableName, expression, OutSqlAction); return task; }, isMaster); } #endregion #region Async Method /// /// 是否存在表 /// /// /// /// public async Task IsExistTableAsync(string tableName, bool isMaster = true) { if (string.IsNullOrEmpty(tableName)) return false; using (var connection = OpenConnection(isMaster)) { return await connection.IsExistTableAsync(tableName, OutSqlAction); } } /// /// 是否存在字段 /// /// /// /// /// public async Task IsExistFieldAsync(string tableName, string fieldName, bool isMaster = true) { if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(fieldName)) return false; using (var conneciton = OpenConnection(isMaster)) { return await conneciton.IsExistFieldAsync(tableName, fieldName, OutSqlAction); } } /// /// 异步添加 /// /// /// 是否赋值最后一次的自增ID /// public async Task AddAsync(TEntity entity, bool returnLastIdentity = false) { if (entity == null) return false; return await Execute(async (connection) => { var tableName = entity.GetTableName(TableNameFunc); var result = await connection.InsertAsync(tableName, entity, returnLastIdentity, OutSqlAction); return result; }, true); } /// /// 异步批量添加 /// /// /// public async Task AddAsync(params TEntity[] entities) { if ((entities?.Count() ?? 0) <= 0) return false; return await Execute(async (connection) => { var tableName = entities.First().GetTableName(TableNameFunc); var result = await connection.InsertAsync(tableName, entities, OutSqlAction); return result > 0; }, true); } /// /// 异步删除 /// /// 删除条件 /// 是否成功 public async Task DeleteAsync(Expression> expression) { return await Execute(async (connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = await connection.DeleteAsync(tableName, expression, OutSqlAction); return task > 0; }, true); } /// /// 异步更新 /// /// 数据实体 /// x=> x.SomeProperty1 or x=> new { x.SomeProperty1, x.SomeProperty2 } /// 是否成功 public async Task SetAsync(TEntity entity, Expression> fields = null) { if (entity == null) return false; var fieldNames = fields.GetFieldNames()?.ToList(); return await Execute(async (connection) => { var tableName = entity.GetTableName(TableNameFunc); var task = await connection.SetAsync(tableName, entity, fieldNames, OutSqlAction); return task; }, true); } /// /// 异步根据字段修改 /// /// 修改字段表达式:() => new { SomeProperty1 = "", SomeProperty2 = "" } or Dictionary /// 条件表达式 /// 是否成功 public async Task SetAsync(Expression> setExpress, Expression> whereExpress) { if (setExpress == null || whereExpress == null) return false; return await Execute(async (connection) => { var tableName = whereExpress.GetTableName(TableNameFunc); var task = await connection.SetAsync(tableName, setExpress, whereExpress, OutSqlAction); return task; }, true); } /// /// 根据条件 在原字段上增减数据 /// /// 增减的字段,底层未验证改字段是否存在,由数据库异常抛出 /// 增减的值 /// 条件表达式 /// 是否成功 public async Task IncrAsync(string field, TValue value, Expression> whereExpress) where TValue : struct { if (string.IsNullOrWhiteSpace(field)) throw new ArgumentNullException(nameof(field), "字段值必须提供"); return await Execute(async (connection) => { var tableName = whereExpress.GetTableName(TableNameFunc); var task = await connection.IncrAsync(tableName, field, value, whereExpress, OutSqlAction); return task; }, true); } /// /// 异步获取一条数据 /// /// 查询条件 /// 按字段返回 /// 是否主从 /// 实体 public async Task GetAsync(Expression> expression, Expression> fieldExpressison = null, bool isMaster = false) { if (expression == null) return default(TEntity); return await Execute(async (connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = await connection.GetAsync(tableName, expression, fieldExpressison, OutSqlAction); return task; }, isMaster); } /// /// 异步获取列表 /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// public async Task> GetListAsync( int page, int rows, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields) { if (page <= 0 || rows <= 0) return default(IEnumerable); return await Execute(async (connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = await connection.GetListAsync(tableName, page, rows, expression, fieldExpressison, orderByFields?.ToList(), OutSqlAction); return task; }, isMaster); } /// /// 异步获取列表 Offset /// /// /// /// 条件表达式 /// 按字段返回 /// 是否主从 /// 排序字段集合 /// public async Task> GetOffsetsAsync( int offset, int size, Expression> expression = null, Expression> fieldExpressison = null, bool isMaster = false, params OrderByField[] orderByFields) { if (offset < 0 || size <= 0) return default(IEnumerable); return await Execute(async (connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = await connection.GetOffsetsAsync(tableName, offset, size, expression, fieldExpressison, orderByFields?.ToList(), OutSqlAction); return task; }, isMaster); } /// /// 异步获取数量 /// /// 条件表达式 /// 是否主从 /// public async Task CountAsync(Expression> expression = null, bool isMaster = false) { return await Execute(async (connection) => { var tableName = expression.GetTableName(TableNameFunc); var task = await connection.CountAsync(tableName, expression, OutSqlAction); return task; }, isMaster); } #endregion #region Protected /// /// 包含connection的方法执行 /// /// /// /// /// protected virtual T Execute(Func func, bool isMaster = true) { if (!this.CheckTableIfMissingCreate(isMaster)) return default(T); using (var connection = OpenConnection(isMaster)) { return func(connection); } } /// /// 包含connection的方法执行 /// /// /// /// /// protected virtual async Task Execute(Func> func, bool isMaster = true) { if (!this.CheckTableIfMissingCreate(isMaster)) return default(T); using (var connection = OpenConnection(isMaster)) { return await func(connection); } } #endregion #region Private Method /// /// 填充ExecuteScript /// /// private void OutSqlAction(string sql) { ExecuteScript = sql; } #endregion } } ================================================ FILE: src/Overt.Core.Data/Repository/PropertyAssist.cs ================================================ using System; using System.Data; #if ASP_NET_CORE using Microsoft.Extensions.Configuration; #else using System.Configuration; #endif namespace Overt.Core.Data { /// /// DbRespository 抽象类 /// public abstract class PropertyAssist : IPropertyAssist { #region Private Property #if ASP_NET_CORE private readonly IConfiguration _configuration; #endif #endregion #region Constructor #if ASP_NET_CORE /// /// 实例化 /// /// 配置注入 /// 数据库前缀 public PropertyAssist(IConfiguration configuration, string dbStoreKey = "") { _configuration = configuration; DbStoreKey = dbStoreKey; } #else /// /// 实例化 /// /// 数据库前缀 public PropertyAssist(string dbStoreKey = "") { DbStoreKey = dbStoreKey; } #endif #endregion #region Public Method /// /// 数据库名key /// public string DbStoreKey { get; set; } /// /// 打开连接 已赋值 connection 属性 /// /// public virtual IDbConnection OpenConnection(bool isMaster = false) { IDbConnection connection; #if ASP_NET_CORE connection = new DataContext(_configuration, isMaster, DbStoreKey, ConnectionFunc).DbConnection; #else connection = new DataContext(isMaster, DbStoreKey, ConnectionFunc).DbConnection; #endif if (connection == null) throw new Exception("数据库连接创建失败,请检查连接字符串是否正确..."); if (connection.State != ConnectionState.Open) connection.Open(); return connection; } /// /// 数据库连接方法 /// #if ASP_NET_CORE public virtual Func ConnectionFunc { get; set; } #else public virtual Func ConnectionFunc { get; set; } #endif /// /// 表名方法 /// public virtual Func TableNameFunc { get; set; } /// /// 创建表的脚本 /// public virtual Func CreateScriptFunc { get; set; } /// /// 执行的sql脚本 /// public virtual string ExecuteScript { get; set; } #endregion } } ================================================ FILE: src/core/Overt.Core.DataConsole/Overt.Core.DataConsole.csproj ================================================ Exe netcoreapp2.0 C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.dependencyinjection\2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll Always Always ================================================ FILE: src/core/Overt.Core.DataConsole/Program.cs ================================================ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; using Overt.User.Application; using Overt.User.Application.Constracts; using System.Collections.Generic; using System.Threading.Tasks; namespace Overt.Core.DataConsole { class Program { static IServiceProvider provider; static Program() { var builder = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddJsonFile("appsettings.json", true); var configuration = builder.Build(); var services = new ServiceCollection(); services.AddSingleton(configuration); services.AddApplicationDI(); provider = services.BuildServiceProvider(); } static void Main(string[] args) { ExecuteMethod(); ExecuteMethodAsync().GetAwaiter().GetResult(); ExecuteMethodLongId(); ExecuteMethodLongIdAsync().GetAwaiter().GetResult(); } #region Static Method private static void ExecuteMethodLongId() { var _userService = provider.GetService(); // 新增 var userId = _userService.Add(new User.Application.Models.UserPostModel() { UserName = "TEST1", RealName = "TEST1", Password = "123456", IsSex = false, JsonValue = "{}" }); // 批量新增 var batchAddResult = _userService.Add(new User.Application.Models.UserPostModel() { UserName = "TEST1" + DateTime.Now.ToString("fffff"), RealName = "TEST1" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }, new User.Application.Models.UserPostModel() { UserName = "TEST2" + DateTime.Now.ToString("fffff"), RealName = "TEST2" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }); } private static async Task ExecuteMethodLongIdAsync() { var _userService = provider.GetService(); // 新增 var userId = await _userService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST1", RealName = "TEST1", Password = "123456", IsSex = false, JsonValue = "{}" }); // 批量新增 var batchAddResult = await _userService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST1" + DateTime.Now.ToString("fffff"), RealName = "TEST1" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }, new User.Application.Models.UserPostModel() { UserName = "TEST2" + DateTime.Now.ToString("fffff"), RealName = "TEST2" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }); } private static async Task ExecuteMethodAsync() { var _userService = provider.GetService(); #region 单表 // 新增 var userId = await _userService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST1", RealName = "TEST1", Password = "123456", IsSex = false, JsonValue = "{}" }); // 批量新增 var batchAddResult = await _userService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST1" + DateTime.Now.ToString("fffff"), RealName = "TEST1" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }, new User.Application.Models.UserPostModel() { UserName = "TEST2" + DateTime.Now.ToString("fffff"), RealName = "TEST2" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }); // 修改 var setResult = await _userService.UpdateAsync(userId, true); // 单条查询 var getResult = await _userService.GetAsync(userId, true); // 多条查询 var listResult = await _userService.GetListAsync(new List() { userId }, true); // 分页查询 var pageResult = await _userService.GetPageAsync(new User.Application.Models.UserSearchModel() { Page = 1, Size = 10, UserIds = new List { userId }, IsMaster = true }); // 自定义SQL var otherResult = await _userService.OtherSqlAsync(); // 删除 var delResult = await _userService.DeleteAsync(userId); // ... 其他更多用法详见Readme,可有很多组合方式,并不局限于目前案例所示 #endregion #region 分表 var _subUserService = provider.GetService(); // 添加 var addResult1 = await _subUserService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST_Sub", RealName = "TEST_Sub", Password = "123456", IsSex = false, JsonValue = "{}" }); // 获取 var getResult1 = await _subUserService.GetAsync(addResult1); #endregion #region 分库 var _subDbUserService = provider.GetService(); // 添加 var addResult2 = await _subDbUserService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST_SubDb", RealName = "TEST_SubDb", Password = "123456", IsSex = false, JsonValue = "{}" }); // 获取 var getResult2 = await _subDbUserService.GetAsync(addResult2); #endregion #region 分库2 var _subDbUser2Service = provider.GetService(); // 添加 var addResult3 = await _subDbUser2Service.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST_SubDb2", RealName = "TEST_SubDb2", Password = "123456", IsSex = false, JsonValue = "{}" }); // 获取 var getResult3 = await _subDbUser2Service.GetAsync(addResult3); #endregion #region 事务 var transResult = await _userService.ExecuteInTransactionAsync(); #endregion } /// /// 同步方法 /// private static void ExecuteMethod() { var _userService = provider.GetService(); #region 单表 // 新增 var userId = _userService.Add(new User.Application.Models.UserPostModel() { UserName = "TEST1", RealName = "TEST1", Password = "123456", IsSex = false, JsonValue = "{}" }); // 批量新增 var batchAddResult = _userService.Add(new User.Application.Models.UserPostModel() { UserName = "TEST1" + DateTime.Now.ToString("fffff"), RealName = "TEST1" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }, new User.Application.Models.UserPostModel() { UserName = "TEST2" + DateTime.Now.ToString("fffff"), RealName = "TEST2" + DateTime.Now.ToString("fffff"), Password = "123456", IsSex = false, JsonValue = "{}" }); // 修改 var setResult = _userService.Update(userId, true); // 单条查询 var getResult = _userService.Get(userId, true); // 多条查询 var listResult = _userService.GetList(new List() { userId }, true); // 分页查询 var pageResult = _userService.GetPage(new User.Application.Models.UserSearchModel() { Page = 1, Size = 10, UserIds = new List { userId }, IsMaster = true }); // 自定义SQL var otherResult = _userService.OtherSql(); // 删除 var delResult = _userService.Delete(userId); // ... 其他更多用法详见Readme,可有很多组合方式,并不局限于目前案例所示 #endregion #region 分表 var _subUserService = provider.GetService(); // 添加 var addResult1 = _subUserService.Add(new User.Application.Models.UserPostModel() { UserName = "TEST_Sub", RealName = "TEST_Sub", Password = "123456", IsSex = false, JsonValue = "{}" }); // 获取 var getResult1 = _subUserService.Get(addResult1); #endregion #region 分库 var _subDbUserService = provider.GetService(); // 添加 var addResult2 = _subDbUserService.Add(new User.Application.Models.UserPostModel() { UserName = "TEST_SubDb", RealName = "TEST_SubDb", Password = "123456", IsSex = false, JsonValue = "{}" }); // 获取 var getResult2 = _subDbUserService.Get(addResult2); #endregion #region 分库2 var _subDbUser2Service = provider.GetService(); // 添加 var addResult3 = _subDbUser2Service.Add(new User.Application.Models.UserPostModel() { UserName = "TEST_SubDb2", RealName = "TEST_SubDb2", Password = "123456", IsSex = false, JsonValue = "{}" }); // 获取 var getResult3 = _subDbUser2Service.Get(addResult3); #endregion #region 事务 var transResult = _userService.ExecuteInTransaction(); #endregion } #endregion } } ================================================ FILE: src/core/Overt.Core.DataConsole/appsettings.json ================================================ { "ConnectionStrings": { "master": "Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123465;DbType=SqlServer", "secondary": "Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123465;DbType=SqlServer", "subdb.master": "Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123465;DbType=SqlServer", "subdb.secondary": "Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123465;DbType=SqlServer", // д "mysql.master": "Server=127.0.0.1;Database=TestDb;uid=root;password=123456;DbType=MySql", "sqlite.master": "Data Source=E:\\SQLiteDb\\testdb.db;DbType=SQLite;", "ss.master": "Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123456;DbType=SqlServer", "pg.master": "Host=127.0.0.1;Port=5432;Database=test;Username=sa;Password=123456;Persist Security Info=True;DbType=PostgreSQL" } } ================================================ FILE: src/core/Overt.Core.Test/ApplicationTest.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using Overt.User.Application.Constracts; using Microsoft.Extensions.DependencyInjection; using System.Threading.Tasks; using System.Collections.Generic; namespace Overt.Core.Test { [TestClass] public class ApplicationTest : BaseTest { public ApplicationTest() : base() { } [TestMethod] public void DoSomethingTest() { var _userService = provider.GetService(); //_userService.DoSomethingWithTrans(); _userService.GetList(); //Parallel.For(0, 100, (i) => //{ // if (i % 2 == 0) // { // System.Diagnostics.Debug.WriteLine("Add: " + i); // var _userService = provider.GetService(); // _userService.Add(); // } // else // { // System.Diagnostics.Debug.WriteLine("GetList: " + i); // var _userService = provider.GetService(); // _userService.GetList(); // } //}); //var tasks = new List(); //for (int i = 0; i < 2; i++) //{ // var task = Task.Run(() => // { // var _userService = provider.GetService(); // _userService.DoSomethingWithTrans(); // }); // tasks.Add(task); //} //Task.WaitAll(tasks.ToArray()); } [TestMethod] public void DoSomeTest() { var _userService = provider.GetService(); _userService.DoSomethingWithTrans(); } [TestMethod] public void GetByIdsTest() { var _userService = provider.GetService(); _userService.GetByIds(); } } } ================================================ FILE: src/core/Overt.Core.Test/BaseTest.cs ================================================ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Overt.User.Application; using System; namespace Overt.Core.Test { public class BaseTest { public IServiceProvider provider; public BaseTest() { var builder = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddJsonFile("appsettings.json", true); var configuration = builder.Build(); var services = new ServiceCollection(); services.AddSingleton(configuration); services.AddApplicationDI(); provider = services.BuildServiceProvider(); } } } ================================================ FILE: src/core/Overt.Core.Test/Overt.Core.Test.csproj ================================================ netcoreapp2.0 false C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.dependencyinjection\2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.dependencyinjection.abstractions\2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll Always ================================================ FILE: src/core/Overt.Core.Test/appsettings.json ================================================ { "ConnectionStrings": { "ss.master": "Data Source=127.0.0.1;Initial Catalog=TestDb;Persist Security Info=True;User ID=sa;Password=123456;DbType=SqlServer" } } ================================================ FILE: src/core/Overt.User.Application/AutoMapperProfiles.cs ================================================ using AutoMapper; using Overt.User.Application.Models; using Overt.User.Domain.Entities; namespace Overt.User.Application { public class AutoMapperProfiles : Profile { public AutoMapperProfiles() { #region Input CreateMap(); CreateMap(); CreateMap(); CreateMap(); CreateMap(); #endregion #region Output CreateMap(); CreateMap(); CreateMap(); CreateMap(); CreateMap(); #endregion } } } ================================================ FILE: src/core/Overt.User.Application/Constracts/ISubDbUser2Service.cs ================================================ using Overt.User.Application.Models; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface ISubDbUser2Service { int Add(UserPostModel model); UserModel Get(int userId, bool isMaster = false); Task AddAsync(UserPostModel model); Task GetAsync(int userId, bool isMaster = false); } } ================================================ FILE: src/core/Overt.User.Application/Constracts/ISubDbUserService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface ISubDbUserService { int Add(UserPostModel model); UserModel Get(int userId, bool isMaster = false); Task AddAsync(UserPostModel model); Task GetAsync(int userId, bool isMaster = false); } } ================================================ FILE: src/core/Overt.User.Application/Constracts/ISubUserService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface ISubUserService { int Add(UserPostModel model); UserModel Get(int userId, bool isMaster = false); Task AddAsync(UserPostModel model); Task GetAsync(int userId, bool isMaster = false); } } ================================================ FILE: src/core/Overt.User.Application/Constracts/IUserLongIdService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface IUserLongIdService { #region SyncMethod UserModel Get(long userId, bool isMaster = false); List GetList(List userIds, bool isMaster = false); long Add(UserPostModel model); bool Add(params UserPostModel[] models); #endregion #region AsyncMethod Task GetAsync(long userId, bool isMaster = false); Task> GetListAsync(List userIds, bool isMaster = false); Task AddAsync(UserPostModel model); Task AddAsync(params UserPostModel[] models); #endregion } } ================================================ FILE: src/core/Overt.User.Application/Constracts/IUserService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface IUserService { #region SyncMethod UserModel Get(int userId, bool isMaster = false); List GetList(List userIds, bool isMaster = false); (int, List) GetPage(UserSearchModel model); int Add(UserPostModel model); bool Add(params UserPostModel[] models); bool Update(int userId, bool isSex); bool Delete(int userId); List OtherSql(); /// /// 在事务中执行 /// /// bool ExecuteInTransaction(); #endregion #region AsyncMethod Task GetAsync(int userId, bool isMaster = false); Task> GetListAsync(List userIds, bool isMaster = false); Task<(int, List)> GetPageAsync(UserSearchModel model); Task AddAsync(UserPostModel model); Task AddAsync(params UserPostModel[] models); Task UpdateAsync(int userId, bool isSex); Task DeleteAsync(int userId); Task> OtherSqlAsync(); /// /// 在事务中执行 /// /// Task ExecuteInTransactionAsync(); #endregion } } ================================================ FILE: src/core/Overt.User.Application/Extensions/ModelValidationExtensions.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using System.Linq; namespace Overt.User.Application { public static class ModelValidationExtensions { public static bool IsValid(this object obj,out Exception exception) { exception = null; if(obj == null) { exception = new ArgumentNullException(); return false; } var context = new ValidationContext(obj); var result = Validate(context); if (result == null) return true; exception = new Exception(result.ErrorMessage); return false; } private static ValidationResult Validate(ValidationContext context) { var properties = context.ObjectType.GetProperties(); if (context.ObjectInstance is IValidatableObject) { IValidatableObject valid = (IValidatableObject)context.ObjectInstance; var validationResults = valid.Validate(context); if (validationResults != null && validationResults.Count() > 0) { return valid.Validate(context).FirstOrDefault(); } } foreach (var property in properties) { var validationAttributes = property.GetCustomAttributes(false).OfType(); foreach (var attribute in validationAttributes) { bool isValid = attribute.IsValid(property.GetValue(context.ObjectInstance)); if (!isValid) { return new ValidationResult(attribute.ErrorMessage, new[] { property.Name }); } } } return null; } } } ================================================ FILE: src/core/Overt.User.Application/Models/UserModel.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace Overt.User.Application.Models { public class UserModel { public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/core/Overt.User.Application/Models/UserPostModel.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Models { public class UserPostModel : IValidatableObject { public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } /// /// 验证方法 /// /// /// public virtual IEnumerable Validate(ValidationContext validationContext) { if (string.IsNullOrWhiteSpace(UserName)) yield return new ValidationResult("用户名必须提供", new[] { nameof(UserName) }); if (string.IsNullOrWhiteSpace(Password)) yield return new ValidationResult("密码必须提供", new[] { nameof(Password) }); } } } ================================================ FILE: src/core/Overt.User.Application/Models/UserSearchModel.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Models { public class UserSearchModel { public int Page { get; set; } public int Size { get; set; } public bool IsMaster { get; set; } public List UserIds { get; set; } public string SearchKey { get; set; } public Expression> GetExpression() { Expression> expression = oo => oo.UserId > 0; if (UserIds?.Count > 0) expression = expression.And(oo => UserIds.Contains(oo.UserId)); if (!string.IsNullOrEmpty(SearchKey)) expression = expression.And(oo => oo.UserName.Contains(SearchKey)); return expression; } public OrderByField[] GetOrder() { var list = new List() { OrderByField.Create(nameof(UserEntity.AddTime), FieldSortType.Asc) }; return list.ToArray(); } } } ================================================ FILE: src/core/Overt.User.Application/Overt.User.Application.csproj ================================================ netcoreapp2.0 C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.dependencyinjection.abstractions\2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll ================================================ FILE: src/core/Overt.User.Application/ServiceCollectionExtensions.cs ================================================ using AutoMapper; using Microsoft.Extensions.DependencyInjection; using Overt.User.Application.Constracts; using Overt.User.Application.Services; using Overt.User.Domain; namespace Overt.User.Application { public static class ServiceCollectionExtensions { public static void AddApplicationDI(this IServiceCollection services) { services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddDomainDI(); services.AddAutoMapper(); } } } ================================================ FILE: src/core/Overt.User.Application/Services/SubDbUser2Service.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Threading.Tasks; namespace Overt.User.Application.Services { public class SubDbUser2Service : ISubDbUser2Service { IMapper _mapper; ISubDbUser2Repository _repository; public SubDbUser2Service( IMapper mapper, ISubDbUser2Repository repository) { _mapper = mapper; _repository = repository; } public int Add(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; _repository.SubDbAddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = _repository.Add(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public UserModel Get(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); _repository.SubDbAddTime = DateTime.Now; var entity = _repository.Get(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; _repository.SubDbAddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _repository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); _repository.SubDbAddTime = DateTime.Now; var entity = await _repository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } } } ================================================ FILE: src/core/Overt.User.Application/Services/SubDbUserService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Threading.Tasks; namespace Overt.User.Application.Services { public class SubDbUserService : ISubDbUserService { IMapper _mapper; ISubDbUserRepository _repository; public SubDbUserService( IMapper mapper, ISubDbUserRepository repository) { _mapper = mapper; _repository = repository; } public int Add(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = _repository.Add(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public UserModel Get(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = _repository.Get(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _repository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = await _repository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } } } ================================================ FILE: src/core/Overt.User.Application/Services/SubUserService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; namespace Overt.User.Application.Services { public class SubUserService : ISubUserService { IMapper _mapper; ISubUserRepository _repository; public SubUserService( IMapper mapper, ISubUserRepository repository) { _mapper = mapper; _repository = repository; } public int Add(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; // 分表标识赋值 _repository.AddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = _repository.Add(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public UserModel Get(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); // 分表标识赋值 _repository.AddTime = DateTime.Now; var entity = _repository.Get(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; // 分表标识赋值 _repository.AddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _repository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); // 分表标识赋值 _repository.AddTime = DateTime.Now; var entity = await _repository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } } } ================================================ FILE: src/core/Overt.User.Application/Services/UserLongIdService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Overt.User.Application.Services { public class UserLongIdService : IUserLongIdService { IMapper _mapper; IUserLongIdRepository _userRepository; public UserLongIdService( IMapper mapper, IUserLongIdRepository userRepository) { _mapper = mapper; _userRepository = userRepository; } #region SyncMethod public long Add(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = _userRepository.Add(entity, true); if (!result) throw new Exception($"新增失败"); var entity2 = _mapper.Map(model); entity2.AddTime = DateTime.Now; var result2 = _userRepository.Add(entity2); if (!result2) throw new Exception($"新增失败"); return entity.UserId; } public bool Add(params UserPostModel[] models) { if ((models?.Count() ?? 0) <= 0) throw new Exception("必须提供"); var entities = _mapper.Map>(models); entities.ForEach(oo => { oo.AddTime = DateTime.Now; }); var result = _userRepository.Add(entities.ToArray()); if (!result) throw new Exception($"新增失败"); return result; } public bool Delete(long userId) { if (userId <= 0) throw new Exception($"UserId必须大于0"); return _userRepository.Delete(oo => oo.UserId == userId); } public UserModel Get(long userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = _userRepository.Get(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public List GetList(List userIds, bool isMaster = false) { if ((userIds?.Count ?? 0) <= 0) throw new Exception($"UserIds至少提供一个"); var entities = _userRepository.GetList(1, userIds.Count, oo => userIds.Contains(oo.UserId), isMaster: isMaster); return _mapper.Map>(entities); } #endregion #region AsyncMethod public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _userRepository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task AddAsync(params UserPostModel[] models) { if ((models?.Count() ?? 0) <= 0) throw new Exception("必须提供"); var entities = _mapper.Map>(models); entities.ForEach(oo => { oo.AddTime = DateTime.Now; }); var result = await _userRepository.AddAsync(entities.ToArray()); if (!result) throw new Exception($"新增失败"); return result; } public async Task DeleteAsync(long userId) { if (userId <= 0) throw new Exception($"UserId必须大于0"); return await _userRepository.DeleteAsync(oo => oo.UserId == userId); } public async Task GetAsync(long userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = await _userRepository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task> GetListAsync(List userIds, bool isMaster = false) { if ((userIds?.Count ?? 0) <= 0) throw new Exception($"UserIds至少提供一个"); var entities = await _userRepository.GetListAsync(1, userIds.Count, oo => userIds.Contains(oo.UserId), isMaster: isMaster); return _mapper.Map>(entities); } #endregion } } ================================================ FILE: src/core/Overt.User.Application/Services/UserService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; namespace Overt.User.Application.Services { public class UserService : IUserService { IMapper _mapper; IUserRepository _userRepository; ISubUserRepository _subUserRepository; public UserService( IMapper mapper, IUserRepository userRepository, ISubUserRepository subUserRepository) { _mapper = mapper; _userRepository = userRepository; _subUserRepository = subUserRepository; } #region SyncMethod public int Add(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = _userRepository.Add(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public bool Add(params UserPostModel[] models) { if ((models?.Count() ?? 0) <= 0) throw new Exception("必须提供"); var entities = _mapper.Map>(models); entities.ForEach(oo => { oo.AddTime = DateTime.Now; }); var result = _userRepository.Add(entities.ToArray()); if (!result) throw new Exception($"新增失败"); return result; } public bool Delete(int userId) { if (userId <= 0) throw new Exception($"UserId必须大于0"); return _userRepository.Delete(oo => oo.UserId == userId); } public UserModel Get(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = _userRepository.Get(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public List GetList(List userIds, bool isMaster = false) { if ((userIds?.Count ?? 0) <= 0) throw new Exception($"UserIds至少提供一个"); var entities = _userRepository.GetList(1, userIds.Count, oo => userIds.Contains(oo.UserId), isMaster: isMaster); return _mapper.Map>(entities); } public (int, List) GetPage(UserSearchModel model) { var expression = model.GetExpression(); var orders = model.GetOrder(); var count = _userRepository.Count(expression, model.IsMaster); var entities = _userRepository.GetList(model.Page, model.Size, expression, isMaster: model.IsMaster, orderByFields: orders); var models = _mapper.Map>(entities); return (count, models); } public List OtherSql() { var result = _userRepository.OtherSql(); return result; } public bool Update(int userId, bool isSex) { if (userId <= 0) throw new Exception($"UserId必须大于0"); // 第一种 var dic = new Dictionary() { { nameof(UserEntity.IsSex), isSex } }; var updateResult1 = _userRepository.Set(() => dic, oo => oo.UserId == userId); // 第二种 var updateResult2 = _userRepository.Set(() => new { IsSex = isSex }, oo => oo.UserId == userId); // 第三种 var entity = _userRepository.Get(oo => oo.UserId == userId, isMaster: true); if (entity?.UserId != userId) throw new Exception($"无可更新数据"); entity.IsSex = isSex; var updateResult3 = _userRepository.Set(entity); // 第三种 var updateResult4 = _userRepository.Incr(nameof(UserEntity.Age), 10, oo => oo.UserId == userId); return updateResult3; } public bool ExecuteInTransaction() { using (var scope = new TransactionScope()) { var result = _userRepository.Add(new UserEntity() { UserName = "11111111111", RealName = "11111111111", Password = "123456", IsSex = false, JsonValue = "{}", AddTime = DateTime.Now }); result &= _subUserRepository.Add(new SubUserEntity() { UserName = "222222222", RealName = "22222222", Password = "123456", IsSex = false, JsonValue = "{}", AddTime = DateTime.Now }); scope.Complete(); } //_userRepository.BeginTransaction( transaction => //{ // // 传递事务 // _subUserRepository.Transaction = transaction; // var result = _userRepository.Add(new UserEntity()); // result &= _subUserRepository.Add(new SubUserEntity()); // transaction.Commit(); // return result; //}); return true; } #endregion #region AsyncMethod public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _userRepository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task AddAsync(params UserPostModel[] models) { if ((models?.Count() ?? 0) <= 0) throw new Exception("必须提供"); var entities = _mapper.Map>(models); entities.ForEach(oo => { oo.AddTime = DateTime.Now; }); var result = await _userRepository.AddAsync(entities.ToArray()); if (!result) throw new Exception($"新增失败"); return result; } public async Task DeleteAsync(int userId) { if (userId <= 0) throw new Exception($"UserId必须大于0"); return await _userRepository.DeleteAsync(oo => oo.UserId == userId); } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = await _userRepository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task> GetListAsync(List userIds, bool isMaster = false) { if ((userIds?.Count ?? 0) <= 0) throw new Exception($"UserIds至少提供一个"); var entities = await _userRepository.GetListAsync(1, userIds.Count, oo => userIds.Contains(oo.UserId), isMaster: isMaster); return _mapper.Map>(entities); } public async Task<(int, List)> GetPageAsync(UserSearchModel model) { var expression = model.GetExpression(); var orders = model.GetOrder(); var count = await _userRepository.CountAsync(expression, model.IsMaster); var entities = await _userRepository.GetListAsync(model.Page, model.Size, expression, isMaster: model.IsMaster, orderByFields: orders); var models = _mapper.Map>(entities); return (count, models); } public async Task> OtherSqlAsync() { var result = await _userRepository.OtherSqlAsync(); return result; } public async Task UpdateAsync(int userId, bool isSex) { if (userId <= 0) throw new Exception($"UserId必须大于0"); // 第一种 var dic = new Dictionary() { { nameof(UserEntity.IsSex), isSex } }; var updateResult1 = await _userRepository.SetAsync(() => dic, oo => oo.UserId == userId); // 第二种 var updateResult2 = await _userRepository.SetAsync(() => new { IsSex = isSex }, oo => oo.UserId == userId); // 第三种 var entity = await _userRepository.GetAsync(oo => oo.UserId == userId, isMaster: true); if (entity?.UserId != userId) throw new Exception($"无可更新数据"); entity.IsSex = !isSex; entity.AddTime = new DateTime(2021, 1, 1); entity.UserName = "我是谁"; var updateResult3 = await _userRepository.SetAsync(entity); // 第三种 var updateResult4 = await _userRepository.IncrAsync(nameof(UserEntity.Age), 10, oo => oo.UserId == userId); return updateResult3; } public async Task ExecuteInTransactionAsync() { using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { var result = await _userRepository.AddAsync(new UserEntity() { UserName = "11111111111", RealName = "11111111111", Password = "123456", IsSex = false, JsonValue = "{}", AddTime = DateTime.Now }); result &= await _subUserRepository.AddAsync(new SubUserEntity() { UserName = "222222222", RealName = "22222222", Password = "123456", IsSex = false, JsonValue = "{}", AddTime = DateTime.Now }); scope.Complete(); } //await _userRepository.BeginTransactionAsync(async transaction => //{ // // 传递事务 // _subUserRepository.Transaction = transaction; // var result = await _userRepository.AddAsync(new UserEntity()); // result &= await _subUserRepository.AddAsync(new SubUserEntity()); // transaction.Commit(); // return result; //}); return true; } #endregion } } ================================================ FILE: src/core/Overt.User.Domain/Contracts/ISubDbUser2Repository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; namespace Overt.User.Domain.Contracts { public interface ISubDbUser2Repository : IBaseRepository { /// /// 分库标识 /// DateTime SubDbAddTime { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Contracts/ISubDbUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; namespace Overt.User.Domain.Contracts { public interface ISubDbUserRepository : IBaseRepository { } } ================================================ FILE: src/core/Overt.User.Domain/Contracts/ISubUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Domain.Contracts { public interface ISubUserRepository : IBaseRepository { /// /// 分表标识 第二种 /// 第一种为HASH模式,实体标记Submeter /// DateTime AddTime { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Contracts/IUserLongIdRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System.Collections.Generic; using System.Threading.Tasks; namespace Overt.User.Domain.Contracts { public interface IUserLongIdRepository : IBaseRepository { } } ================================================ FILE: src/core/Overt.User.Domain/Contracts/IUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System.Collections.Generic; using System.Threading.Tasks; namespace Overt.User.Domain.Contracts { public interface IUserRepository : IBaseRepository { /// /// 其他sql 本案例中 统计UserName去重个数 /// /// List OtherSql(); /// /// 其他sql 本案例中 统计UserName去重个数 /// /// Task> OtherSqlAsync(); } } ================================================ FILE: src/core/Overt.User.Domain/Entities/SubDbUser2Entity.cs ================================================ using Overt.Core.Data; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Overt.User.Domain.Entities { [Table("User")] public class SubDbUser2Entity { // 第一种分表标识,Md5(UserId)取{Bit}位 尽量不用,使用自定义模式 //[Submeter(Bit = 2)] [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// /// public int Age { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Entities/SubDbUserEntity.cs ================================================ using Overt.Core.Data; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Overt.User.Domain.Entities { [Table("User")] public class SubDbUserEntity { // 第一种分表标识,Md5(UserId)取{Bit}位 尽量不用,使用自定义模式 //[Submeter(Bit = 2)] [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// /// public int Age { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Entities/SubUserEntity.cs ================================================ using Overt.Core.Data; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Overt.User.Domain.Entities { [Table("User")] public class SubUserEntity { // 第一种分表标识,Md5(UserId)取{Bit}位 尽量不用,使用自定义模式 //[Submeter(Bit = 2)] [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// /// public int Age { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Entities/UserEntity.cs ================================================ using Overt.Core.Data; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace Overt.User.Domain.Entities { [Table("User")] public class UserEntity { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// /// public int Age { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Entities/UserLongIdEntity.cs ================================================ using Overt.Core.Data; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace Overt.User.Domain.Entities { [Table("UserLongId")] public class UserLongIdEntity { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// /// public int Age { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/core/Overt.User.Domain/Overt.User.Domain.csproj ================================================  netcoreapp2.0 ================================================ FILE: src/core/Overt.User.Domain/Repositories/SubDbUser2Repository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using System.Linq; using Microsoft.Extensions.Configuration; namespace Overt.User.Domain.Repositories { public class SubDbUser2Repository : BaseRepository, ISubDbUser2Repository { public SubDbUser2Repository(IConfiguration configuration) : base(configuration) { } // Service层进行赋值即可 public DateTime SubDbAddTime { get; set; } public override Func ConnectionFunc => (isMaster) => { var connectionString = string.Empty; var dbName = $"TestDb_{SubDbAddTime.ToString("yyyy")}"; if (isMaster) connectionString = $"Data Source=127.0.0.1;Initial Catalog={dbName};Persist Security Info=True;User ID=sa;Password=123465;DbType=SqlServer"; else connectionString = $"Data Source=127.0.0.1;Initial Catalog={dbName};Persist Security Info=True;User ID=sa;Password=123465;DbType=SqlServer"; return connectionString; }; } } ================================================ FILE: src/core/Overt.User.Domain/Repositories/SubDbUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using System.Linq; using Microsoft.Extensions.Configuration; namespace Overt.User.Domain.Repositories { public class SubDbUserRepository : BaseRepository, ISubDbUserRepository { public SubDbUserRepository(IConfiguration configuration) : base(configuration, "subdb") { } } } ================================================ FILE: src/core/Overt.User.Domain/Repositories/SubUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using System.Linq; using Microsoft.Extensions.Configuration; namespace Overt.User.Domain.Repositories { public class SubUserRepository : BaseRepository, ISubUserRepository { public SubUserRepository(IConfiguration configuration) : base(configuration) { } // Service层进行赋值即可 public DateTime AddTime { get; set; } public override Func TableNameFunc => () => { var tableName = $"{GetMainTableName()}_{AddTime.ToString("yyyyMMdd")}"; return tableName; }; public override Func CreateScriptFunc => (tableName) => { return $"CREATE TABLE [{tableName}] (" + " [UserId] int IDENTITY(1,1) NOT NULL," + " [UserName] varchar(200) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL," + " [Password] varchar(200) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL," + " [RealName] varchar(200) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL," + " [AddTime] datetime DEFAULT NULL NULL," + " [IsSex] bit DEFAULT NULL NULL," + " [Age] int DEFAULT 0 NOT NULL," + " [JsonValue] varchar(1000) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL" + ") "; }; } } ================================================ FILE: src/core/Overt.User.Domain/Repositories/UserLongIdRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using System.Linq; using Microsoft.Extensions.Configuration; namespace Overt.User.Domain.Repositories { public class UserLongIdRepository : BaseRepository, IUserLongIdRepository { public UserLongIdRepository(IConfiguration configuration) : base(configuration) // dbStoreKey 可用于不同数据库切换,连接字符串key前缀:xxx.master xxx.secondary { } } } ================================================ FILE: src/core/Overt.User.Domain/Repositories/UserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using System.Linq; using Microsoft.Extensions.Configuration; namespace Overt.User.Domain.Repositories { public class UserRepository : BaseRepository, IUserRepository { public UserRepository(IConfiguration configuration) : base(configuration) // dbStoreKey 可用于不同数据库切换,连接字符串key前缀:xxx.master xxx.secondary { } public List OtherSql() { // 表名最好使用这个方法获取,支持分表,分表案例详见其他案例 var tableName = GetTableName(); var sql = $"select distinct([UserName]) from [{tableName}]"; return Execute(connecdtion => { var task = connecdtion.Query(sql); return task.ToList(); }); } public async Task> OtherSqlAsync() { // 表名最好使用这个方法获取,支持分表,分表案例详见其他案例 var tableName = GetTableName(); var sql = $"select distinct([UserName]) from [{tableName}]"; return await Execute(async connecdtion => { var task = await connecdtion.QueryAsync(sql); return task.ToList(); }); } } } ================================================ FILE: src/core/Overt.User.Domain/ServiceCollectionExtensions.cs ================================================ using Microsoft.Extensions.DependencyInjection; using Overt.User.Domain.Contracts; using Overt.User.Domain.Repositories; using System; using System.Collections.Generic; using System.Text; namespace Overt.User.Domain { public static class ServiceCollectionExtensions { public static void AddDomainDI(this IServiceCollection services) { services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); } } } ================================================ FILE: src/net46/Overt.Core.DataConsole/App.config ================================================
================================================ FILE: src/net46/Overt.Core.DataConsole/AutofacContainer.cs ================================================ using Autofac; using AutoMapper; using Overt.User.Application; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Overt.Core.DataConsole { public class AutofacContainer { public static IContainer Container { get; private set; } public static IContainer Register() { var builder = new ContainerBuilder(); builder.AddApplicationDI(); builder.RegisterType().As(); builder.Register(c => new MapperConfiguration(cfg => { foreach (var profile in c.Resolve>()) { cfg.AddProfile(profile); } })).AsSelf().SingleInstance(); builder.Register(c => c.Resolve().CreateMapper(c.Resolve)).As().InstancePerLifetimeScope(); Container = builder.Build(Autofac.Builder.ContainerBuildOptions.None); return Container; } } } ================================================ FILE: src/net46/Overt.Core.DataConsole/Overt.Core.DataConsole.csproj ================================================  Debug AnyCPU {F92F116B-A438-447B-9475-4D62AF197742} Exe Overt.Core.DataConsole Overt.Core.DataConsole v4.6.2 512 true true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 ..\..\..\packages\Autofac.4.8.1\lib\net45\Autofac.dll ..\..\..\packages\AutoMapper.9.0.0\lib\net461\AutoMapper.dll ..\..\..\packages\EntityFramework.6.3.0\lib\net45\EntityFramework.dll ..\..\..\packages\EntityFramework.6.3.0\lib\net45\EntityFramework.SqlServer.dll ..\..\..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.113.3\lib\net46\System.Data.SQLite.dll True ..\..\..\packages\System.Data.SQLite.EF6.1.0.113.0\lib\net46\System.Data.SQLite.EF6.dll True ..\..\..\packages\System.Data.SQLite.Linq.1.0.113.0\lib\net46\System.Data.SQLite.Linq.dll True ..\..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll {8b1b556f-1d2c-49d2-8cb9-f9d2c7080786} Overt.Core.Data {5adb2a33-42e4-484b-997e-3f8b942539e4} Overt.User.Application {F34F6B13-B939-47A3-827E-E7015FD1B434} Overt.User.Domain 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 ================================================ FILE: src/net46/Overt.Core.DataConsole/Program.cs ================================================ using Overt.User.Application.Constracts; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Autofac; namespace Overt.Core.DataConsole { class Program { static void Main(string[] args) { // autofac AutofacContainer.Register(); #region 单表 var _userService = AutofacContainer.Container.Resolve(); // 新增 var userId = _userService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST1", RealName = "TEST1", Password = "123456", IsSex = false, JsonValue = "{}" }).Result; // 修改 var setResult = _userService.UpdateAsync(userId, true).Result; // 单条查询 var getResult = _userService.GetAsync(userId).Result; // 多条查询 var listResult = _userService.GetListAsync(new List() { userId }).Result; // 分页查询 var pageResult = _userService.GetPageAsync(new User.Application.Models.UserSearchModel() { Page = 1, Size = 10, UserIds = new List { userId } }).Result; // 自定义SQL var otherResult = _userService.OtherSqlAsync().Result; // 删除 var delResult = _userService.DeleteAsync(userId).Result; // ... 其他更多用法详见Readme,可有很多组合方式,并不局限于目前案例所示 #endregion #region 分表 var _subUserService = AutofacContainer.Container.Resolve(); // 添加 var addResult1 = _subUserService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST_Sub", RealName = "TEST_Sub", Password = "123456", IsSex = false, JsonValue = "{}" }).Result; // 获取 var getResult1 = _subUserService.GetAsync(addResult1).Result; #endregion #region 分库 var _subDbUserService = AutofacContainer.Container.Resolve(); // 添加 var addResult2 = _subDbUserService.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST_SubDb", RealName = "TEST_SubDb", Password = "123456", IsSex = false, JsonValue = "{}" }).Result; // 获取 var getResult2 = _subDbUserService.GetAsync(addResult2).Result; #endregion #region 分库2 var _subDbUser2Service = AutofacContainer.Container.Resolve(); // 添加 var addResult3 = _subDbUser2Service.AddAsync(new User.Application.Models.UserPostModel() { UserName = "TEST_SubDb", RealName = "TEST_SubDb", Password = "123456", IsSex = false, JsonValue = "{}" }).Result; // 获取 var getResult3 = _subDbUser2Service.GetAsync(addResult3).Result; #endregion #region 事务 var transResult = _userService.ExecuteInTransactionAsync().Result; #endregion } } } ================================================ FILE: src/net46/Overt.Core.DataConsole/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("Overt.Core.DataConsole")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("Overt.Core.DataConsole")] [assembly: AssemblyCopyright("Copyright © Microsoft 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 会使此程序集中的类型 //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 //请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("f92f116b-a438-447b-9475-4d62af197742")] // 程序集的版本信息由下列四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: src/net46/Overt.Core.DataConsole/packages.config ================================================  ================================================ FILE: src/net46/Overt.User.Application/AutoMapperProfiles.cs ================================================ using AutoMapper; using Overt.User.Application.Models; using Overt.User.Domain.Entities; namespace Overt.User.Application { public class AutoMapperProfiles : Profile { public AutoMapperProfiles() { #region Input CreateMap(); CreateMap(); CreateMap(); CreateMap(); #endregion #region Output CreateMap(); CreateMap(); CreateMap(); CreateMap(); #endregion } } } ================================================ FILE: src/net46/Overt.User.Application/AutofacExtensions.cs ================================================ using Autofac; using Overt.User.Application.Constracts; using Overt.User.Application.Services; using Overt.User.Domain; namespace Overt.User.Application { public static class AutofacExtensions { public static void AddApplicationDI(this ContainerBuilder builder) { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.AddDomainDI(); } } } ================================================ FILE: src/net46/Overt.User.Application/Constracts/ISubDbUser2Service.cs ================================================ using Overt.User.Application.Models; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface ISubDbUser2Service { int Add(UserPostModel model); UserModel Get(int userId, bool isMaster = false); Task AddAsync(UserPostModel model); Task GetAsync(int userId, bool isMaster = false); } } ================================================ FILE: src/net46/Overt.User.Application/Constracts/ISubDbUserService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface ISubDbUserService { Task AddAsync(UserPostModel model); Task GetAsync(int userId, bool isMaster = false); } } ================================================ FILE: src/net46/Overt.User.Application/Constracts/ISubUserService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface ISubUserService { Task AddAsync(UserPostModel model); Task GetAsync(int userId, bool isMaster = false); } } ================================================ FILE: src/net46/Overt.User.Application/Constracts/IUserService.cs ================================================ using Overt.User.Application.Models; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Constracts { public interface IUserService { Task GetAsync(int userId, bool isMaster = false); Task> GetListAsync(List userIds, bool isMaster = false); Task<(int, List)> GetPageAsync(UserSearchModel model); Task AddAsync(UserPostModel model); Task UpdateAsync(int userId, bool isSex); Task DeleteAsync(int userId); Task> OtherSqlAsync(); Task ExecuteInTransactionAsync(); } } ================================================ FILE: src/net46/Overt.User.Application/Extensions/ModelValidationExtensions.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using System.Linq; namespace Overt.User.Application { public static class ModelValidationExtensions { public static bool IsValid(this object obj,out Exception exception) { exception = null; if(obj == null) { exception = new ArgumentNullException(); return false; } var context = new ValidationContext(obj); var result = Validate(context); if (result == null) return true; exception = new Exception(result.ErrorMessage); return false; } private static ValidationResult Validate(ValidationContext context) { var properties = context.ObjectType.GetProperties(); if (context.ObjectInstance is IValidatableObject) { IValidatableObject valid = (IValidatableObject)context.ObjectInstance; var validationResults = valid.Validate(context); if (validationResults != null && validationResults.Count() > 0) { return valid.Validate(context).FirstOrDefault(); } } foreach (var property in properties) { var validationAttributes = property.GetCustomAttributes(false).OfType(); foreach (var attribute in validationAttributes) { bool isValid = attribute.IsValid(property.GetValue(context.ObjectInstance)); if (!isValid) { return new ValidationResult(attribute.ErrorMessage, new[] { property.Name }); } } } return null; } } } ================================================ FILE: src/net46/Overt.User.Application/Models/UserModel.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace Overt.User.Application.Models { public class UserModel { public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/net46/Overt.User.Application/Models/UserPostModel.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Models { public class UserPostModel : IValidatableObject { public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } /// /// 验证方法 /// /// /// public virtual IEnumerable Validate(ValidationContext validationContext) { if (string.IsNullOrWhiteSpace(UserName)) yield return new ValidationResult("用户名必须提供", new[] { nameof(UserName) }); if (string.IsNullOrWhiteSpace(Password)) yield return new ValidationResult("密码必须提供", new[] { nameof(Password) }); } } } ================================================ FILE: src/net46/Overt.User.Application/Models/UserSearchModel.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace Overt.User.Application.Models { public class UserSearchModel { public int Page { get; set; } public int Size { get; set; } public bool IsMaster { get; set; } public List UserIds { get; set; } public string SearchKey { get; set; } public Expression> GetExpression() { Expression> expression = oo => oo.UserId > 0; if (UserIds?.Count > 0) expression = expression.And(oo => UserIds.Contains(oo.UserId)); if (!string.IsNullOrEmpty(SearchKey)) expression = expression.And(oo => oo.UserName.Contains(SearchKey)); return expression; } public OrderByField[] GetOrder() { var list = new List() { OrderByField.Create(nameof(UserEntity.AddTime), FieldSortType.Asc) }; return list.ToArray(); } } } ================================================ FILE: src/net46/Overt.User.Application/Overt.User.Application.csproj ================================================  Debug AnyCPU {5ADB2A33-42E4-484B-997E-3F8B942539E4} Library Properties Overt.User.Application Overt.User.Application v4.6.2 512 true true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 ..\..\..\packages\Autofac.4.8.1\lib\net45\Autofac.dll ..\..\..\packages\AutoMapper.9.0.0\lib\net461\AutoMapper.dll ..\..\..\packages\MySql.Data.6.10.7\lib\net452\MySql.Data.dll ..\..\..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll {8b1b556f-1d2c-49d2-8cb9-f9d2c7080786} Overt.Core.Data {f34f6b13-b939-47a3-827e-e7015fd1b434} Overt.User.Domain ================================================ FILE: src/net46/Overt.User.Application/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("Overt.User.Application")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("Overt.User.Application")] [assembly: AssemblyCopyright("Copyright © Microsoft 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 会使此程序集中的类型 //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 //请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("5adb2a33-42e4-484b-997e-3f8b942539e4")] // 程序集的版本信息由下列四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: src/net46/Overt.User.Application/Services/SubDbUser2Service.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Threading.Tasks; namespace Overt.User.Application.Services { public class SubDbUser2Service : ISubDbUser2Service { IMapper _mapper; ISubDbUser2Repository _repository; public SubDbUser2Service( IMapper mapper, ISubDbUser2Repository repository) { _mapper = mapper; _repository = repository; } public int Add(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; _repository.SubDbAddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = _repository.Add(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public UserModel Get(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); _repository.SubDbAddTime = DateTime.Now; var entity = _repository.Get(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; _repository.SubDbAddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _repository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); _repository.SubDbAddTime = DateTime.Now; var entity = await _repository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } } } ================================================ FILE: src/net46/Overt.User.Application/Services/SubDbUserService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Threading.Tasks; namespace Overt.User.Application.Services { public class SubDbUserService : ISubDbUserService { IMapper _mapper; ISubDbUserRepository _repository; public SubDbUserService( IMapper mapper, ISubDbUserRepository repository) { _mapper = mapper; _repository = repository; } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _repository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = await _repository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } } } ================================================ FILE: src/net46/Overt.User.Application/Services/SubUserService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; namespace Overt.User.Application.Services { public class SubUserService : ISubUserService { IMapper _mapper; ISubUserRepository _repository; public SubUserService( IMapper mapper, ISubUserRepository repository) { _mapper = mapper; _repository = repository; } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; // 分表标识赋值 _repository.AddTime = DateTime.Now; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _repository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); // 分表标识赋值 _repository.AddTime = DateTime.Now; var entity = await _repository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } } } ================================================ FILE: src/net46/Overt.User.Application/Services/UserService.cs ================================================ using AutoMapper; using Overt.User.Application.Constracts; using Overt.User.Application.Models; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Transactions; namespace Overt.User.Application.Services { public class UserService : IUserService { IMapper _mapper; IUserRepository _userRepository; ISubUserRepository _subUserRepository; public UserService( IMapper mapper, IUserRepository userRepository, ISubUserRepository subUserRepository) { _mapper = mapper; _userRepository = userRepository; _subUserRepository = subUserRepository; } public async Task AddAsync(UserPostModel model) { if (!model.IsValid(out Exception ex)) throw ex; var entity = _mapper.Map(model); entity.AddTime = DateTime.Now; var result = await _userRepository.AddAsync(entity, true); if (!result) throw new Exception($"新增失败"); return entity.UserId; } public async Task DeleteAsync(int userId) { if (userId <= 0) throw new Exception($"UserId必须大于0"); return await _userRepository.DeleteAsync(oo => oo.UserId == userId); } public async Task GetAsync(int userId, bool isMaster = false) { if (userId <= 0) throw new Exception($"UserId必须大于0"); var entity = await _userRepository.GetAsync(oo => oo.UserId == userId, isMaster: isMaster); return _mapper.Map(entity); } public async Task> GetListAsync(List userIds, bool isMaster = false) { if ((userIds?.Count ?? 0) <= 0) throw new Exception($"UserIds至少提供一个"); var entities = await _userRepository.GetListAsync(1, userIds.Count, oo => userIds.Contains(oo.UserId), isMaster: isMaster); return _mapper.Map>(entities); } public async Task<(int, List)> GetPageAsync(UserSearchModel model) { var expression = model.GetExpression(); var orders = model.GetOrder(); var count = await _userRepository.CountAsync(expression, model.IsMaster); var entities = await _userRepository.GetListAsync(model.Page, model.Size, expression, isMaster: model.IsMaster, orderByFields: orders); var models = _mapper.Map>(entities); return (count, models); } public async Task> OtherSqlAsync() { var result = await _userRepository.OtherSqlAsync(); return result; } public async Task UpdateAsync(int userId, bool isSex) { if (userId <= 0) throw new Exception($"UserId必须大于0"); // 第一种 var dic = new Dictionary() { { nameof(UserEntity.IsSex), isSex } }; var updateResult1 = await _userRepository.SetAsync(() => dic, oo => oo.UserId == userId); // 第二种 var updateResult2 = await _userRepository.SetAsync(() => new { IsSex = isSex }, oo => oo.UserId == userId); // 第三种 var entity = await _userRepository.GetAsync(oo => oo.UserId == userId, isMaster: true); if (entity?.UserId != userId) throw new Exception($"无可更新数据"); entity.IsSex = isSex; var updateResult3 = await _userRepository.SetAsync(entity); return updateResult3; } public async Task ExecuteInTransactionAsync() { // 分布式事务 using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { var result = false; try { result = await _userRepository.AddAsync(new UserEntity() { UserName = "222222222", RealName = "22222222", Password = "123456", IsSex = false, JsonValue = "{}", AddTime = DateTime.Now }); result &= await _subUserRepository.AddAsync(new SubUserEntity() { UserName = "222222222", RealName = "22222222", Password = "123456", IsSex = false, JsonValue = "{}", AddTime = DateTime.Now }); scope.Complete(); return result; } catch(Exception ex) { // logger } return false; } } } } ================================================ FILE: src/net46/Overt.User.Application/packages.config ================================================  ================================================ FILE: src/net46/Overt.User.Domain/AutofacExtensions.cs ================================================ using Autofac; using Overt.User.Domain.Contracts; using Overt.User.Domain.Repositories; namespace Overt.User.Domain { public static class AutofacExtensions { public static void AddDomainDI(this ContainerBuilder builder) { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); } } } ================================================ FILE: src/net46/Overt.User.Domain/Contracts/ISubDbUser2Repository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; namespace Overt.User.Domain.Contracts { public interface ISubDbUser2Repository : IBaseRepository { /// /// 分库标识 /// DateTime SubDbAddTime { get; set; } } } ================================================ FILE: src/net46/Overt.User.Domain/Contracts/ISubDbUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; namespace Overt.User.Domain.Contracts { public interface ISubDbUserRepository : IBaseRepository { } } ================================================ FILE: src/net46/Overt.User.Domain/Contracts/ISubUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Domain.Contracts { public interface ISubUserRepository : IBaseRepository { /// /// 分表标识 第二种 /// 第一种为HASH模式,实体标记Submeter /// DateTime AddTime { get; set; } } } ================================================ FILE: src/net46/Overt.User.Domain/Contracts/IUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Entities; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace Overt.User.Domain.Contracts { public interface IUserRepository : IBaseRepository { /// /// 其他sql 本案例中 统计UserName去重个数 /// /// Task> OtherSqlAsync(); } } ================================================ FILE: src/net46/Overt.User.Domain/Entities/SubDbUser2Entity.cs ================================================ using Overt.Core.Data; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Overt.User.Domain.Entities { [Table("User")] public class SubDbUser2Entity { // 第一种分表标识,Md5(UserId)取{Bit}位 尽量不用,使用自定义模式 //[Submeter(Bit = 2)] [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// /// public int Age { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/net46/Overt.User.Domain/Entities/SubDbUserEntity.cs ================================================ using Overt.Core.Data; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Overt.User.Domain.Entities { [Table("User")] public class SubDbUserEntity { // 第一种分表标识,Md5(UserId)取{Bit}位 尽量不用,使用自定义模式 //[Submeter(Bit = 2)] [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/net46/Overt.User.Domain/Entities/SubUserEntity.cs ================================================ using Overt.Core.Data; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Overt.User.Domain.Entities { [Table("User")] public class SubUserEntity { // 第一种分表标识,Md5(UserId)取{Bit}位 尽量不用,使用自定义模式 //[Submeter(Bit = 2)] [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/net46/Overt.User.Domain/Entities/UserEntity.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace Overt.User.Domain.Entities { [Table("User")] public class UserEntity { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } public string RealName { get; set; } public string Password { get; set; } public DateTime AddTime { get; set; } public bool IsSex { get; set; } /// /// Json类型 /// public string JsonValue { get; set; } } } ================================================ FILE: src/net46/Overt.User.Domain/Overt.User.Domain.csproj ================================================  Debug AnyCPU {F34F6B13-B939-47A3-827E-E7015FD1B434} Library Properties Overt.User.Domain Overt.User.Domain v4.6.2 512 true true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 ..\..\..\packages\Autofac.4.8.1\lib\net45\Autofac.dll ..\..\..\packages\Dapper.1.50.2\lib\net451\Dapper.dll {8b1b556f-1d2c-49d2-8cb9-f9d2c7080786} Overt.Core.Data ================================================ FILE: src/net46/Overt.User.Domain/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("Overt.User.Domain")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("Overt.User.Domain")] [assembly: AssemblyCopyright("Copyright © Microsoft 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 会使此程序集中的类型 //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 //请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("f34f6b13-b939-47a3-827e-e7015fd1b434")] // 程序集的版本信息由下列四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: src/net46/Overt.User.Domain/Repositories/SubDbUser2Repository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; using System.Configuration; namespace Overt.User.Domain.Repositories { public class SubDbUser2Repository : BaseRepository, ISubDbUser2Repository { public SubDbUser2Repository() : base() { } // Service层进行赋值即可 public DateTime SubDbAddTime { get; set; } public override Func ConnectionFunc => (isMaster) => { var connectionString = string.Empty; var dbName = $"TestDb_{SubDbAddTime.ToString("yyyy")}"; if (isMaster) connectionString = $"Data Source=127.0.0.1;Initial Catalog={dbName};Persist Security Info=True;User ID=sa;Password=123465"; else connectionString = $"Data Source=127.0.0.1;Initial Catalog={dbName};Persist Security Info=True;User ID=sa;Password=123465"; return new ConnectionStringSettings(dbName, connectionString, "System.Data.SqlClient"); }; } } ================================================ FILE: src/net46/Overt.User.Domain/Repositories/SubDbUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; namespace Overt.User.Domain.Repositories { public class SubDbUserRepository : BaseRepository, ISubDbUserRepository { public SubDbUserRepository() : base("subdb") { } } } ================================================ FILE: src/net46/Overt.User.Domain/Repositories/SubUserRepository.cs ================================================ using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System; namespace Overt.User.Domain.Repositories { public class SubUserRepository : BaseRepository, ISubUserRepository { public SubUserRepository() : base() { } // Service层进行赋值即可 public DateTime AddTime { get; set; } public override Func TableNameFunc => () => { var tableName = $"{GetMainTableName()}_{DateTime.Now.ToString("yyyyMMdd")}"; return tableName; }; public override Func CreateScriptFunc => (tableName) => { return $"CREATE TABLE [{tableName}] (" + " [UserId] int IDENTITY(1,1) NOT NULL," + " [UserName] varchar(200) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL," + " [Password] varchar(200) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL," + " [RealName] varchar(200) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL," + " [AddTime] datetime DEFAULT NULL NULL," + " [IsSex] bit DEFAULT NULL NULL," + " [JsonValue] varchar(1000) COLLATE Chinese_PRC_CI_AS DEFAULT NULL NULL" + ") "; }; } } ================================================ FILE: src/net46/Overt.User.Domain/Repositories/UserRepository.cs ================================================ using Dapper; using Overt.Core.Data; using Overt.User.Domain.Contracts; using Overt.User.Domain.Entities; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Overt.User.Domain.Repositories { public class UserRepository : BaseRepository, IUserRepository { public UserRepository() : base() // dbStoreKey 可用于不同数据库切换,连接字符串key前缀:xxx.master xxx.secondary { } public async Task> OtherSqlAsync() { // 表名最好使用这个方法获取,支持分表,分表案例详见其他案例 var tableName = GetTableName(); var sql = $"select distinct([UserName]) from [{tableName}]"; return await Execute(async connecdtion => { var task = await connecdtion.QueryAsync(sql); return task.ToList(); }); } } } ================================================ FILE: src/net46/Overt.User.Domain/packages.config ================================================