├── .gitignore ├── README.md └── src ├── References └── MySql.Data.dll ├── SqlBatis.Extensions.Dapper ├── DapperDbContext.cs ├── DapperDbMultipleResult.cs ├── SqlBatis - Backup.Extensions.Dapper.csproj ├── SqlBatis.Extensions.Dapper.csproj └── SqlBatis.Extensions.Dapper.xml ├── SqlBatis.XUnit ├── SqlBatis.XUnit.csproj ├── Templates │ ├── MySqlDbContext.cs │ ├── MySqlDbContext.tt │ ├── MySqlEntities.cs │ ├── MySqlEntities.tt │ ├── SqlServerDbContext.tt │ └── SqlServerEntities.tt ├── UnitTests │ ├── BaseTest.cs │ ├── DeleteTest.cs │ ├── InsertTest.cs │ ├── MyDbContext.cs │ ├── QueryTest.cs │ ├── SqlFun.cs │ └── UpdateTest.cs └── sqlite.db ├── SqlBatis.sln └── SqlBatis ├── Attributes ├── ColumnAttribute.cs ├── ComplexTypeAttribute.cs ├── ConcurrencyCheckAttribute.cs ├── DefaultAttribute.cs ├── FunctionAttribute.cs ├── IdentityAttribute.cs ├── NotMappedAttribute.cs ├── PrimaryKeyAttribute.cs └── TableAttribute.cs ├── DbContexts ├── DbContext.cs ├── DbContextBehavior.cs ├── DbContextBuilder.cs ├── DbContextExtensions.cs ├── DbContextState.cs ├── DbContextType.cs ├── DbConvertMethods.cs ├── DbGridReader.cs └── SqlBuilder.cs ├── Exceptions └── DbUpdateConcurrencyException.cs ├── Expressions ├── BooleanExpressionResovle.cs ├── DbExpressions.cs ├── ExpressionResovle.cs ├── FunctionExpressionResovle.cs ├── GroupExpressionResovle.cs ├── IgnoreExpressionResovle.cs ├── OrderExpressionResovle.cs └── SelectExpressionResovle.cs ├── Providers └── DbMetaInfoProvider.cs ├── Queryables ├── DbQueryable.cs ├── DbQueryable`.cs ├── DbQueryable``.cs ├── DbQueryable```.cs ├── DbQueryable````.cs ├── IDbQueryable`.cs ├── IDbQueryable``.cs ├── IDbQueryable```.cs ├── IDbQueryable````.cs └── Operator.cs ├── SqlBatis.csproj ├── SqlBatis.xml └── SqlBatisSettings.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2 | 3 | # User-specific files 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # User-specific files (MonoDevelop/Xamarin Studio) 10 | *.userprefs 11 | 12 | # Build results 13 | [Dd]ebug/ 14 | [Dd]ebugPublic/ 15 | [Rr]elease/ 16 | [Rr]eleases/ 17 | [Xx]64/ 18 | [Xx]86/ 19 | [Bb]uild/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | *.publishproj 144 | *.pubxml 145 | PublishProfiles/ 146 | PublishScripts/ 147 | 148 | # NuGet Packages 149 | *.nupkg 150 | # The packages folder can be ignored because of Package Restore 151 | **/packages/* 152 | # except build/, which is used as an MSBuild target. 153 | !**/packages/build/ 154 | # Uncomment if necessary however generally it will be regenerated when needed 155 | #!**/packages/repositories.config 156 | # NuGet v3's project.json files produces more ignoreable files 157 | *.nuget.props 158 | *.nuget.targets 159 | 160 | # Microsoft Azure Build Output 161 | csx/ 162 | *.build.csdef 163 | 164 | # Microsoft Azure Emulator 165 | ecf/ 166 | rcf/ 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | Package.StoreAssociation.xml 172 | _pkginfo.txt 173 | 174 | # Visual Studio cache files 175 | # files ending in .cache can be ignored 176 | *.[Cc]ache 177 | # but keep track of directories ending in .cache 178 | !*.[Cc]ache/ 179 | 180 | # Others 181 | ClientBin/ 182 | [Ss]tyle[Cc]op.* 183 | ~$* 184 | *~ 185 | *.dbmdl 186 | *.dbproj.schemaview 187 | *.pfx 188 | *.publishsettings 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | paket-files/ 241 | 242 | # FAKE - F# Make 243 | .fake/ 244 | 245 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SqlBatis 2 | 3 | ## 免责声明 4 | 5 | 本人对所有因使用本产品而导致的损失概不负责,使用前请先测试和阅读源代码并自觉遵守开源协议,可用于学习交流二次开发 6 | 7 | ## 打印日志 8 | 9 | ``` C# 10 | //通过重写DbContext,我们可以定制化一些需求 11 | public class LoggerDbContext : DbContext 12 | { 13 | public MyDbContext(DbContextBuilder builder) 14 | : base(builder) 15 | { 16 | 17 | } 18 | //通过覆盖CreateDbCommand,来实现日志打印,DbContext中有许多方法设计成可以被覆盖的, 19 | protected override IDbCommand CreateDbCommand(string sql, object parameter, int? commandTimeout = null, CommandType? commandType = null) 20 | { 21 | Trace.WriteLine("================Command==================="); 22 | Trace.WriteLine(sql); 23 | Trace.WriteLine("==========================================="); 24 | return base.CreateDbCommand(sql, parameter, commandTimeout, commandType); 25 | } 26 | } 27 | //创建数据上下文 28 | var context = new LoggerDbContext(new DbContext() 29 | { 30 | Connection = new MySqlConnector.MySqlConnection("server=127.0.0.1;user id=root;password=1024;database=test;"), 31 | DbContextType = DbContextType.Mysql 32 | }); 33 | ``` 34 | 35 | ## sql查询 36 | 37 | ``` C# 38 | //返回匿名类型的集合 39 | List list = context.Query("select * from student").ToList(); 40 | //映射到指定类型,忽略大小写和下划线 41 | List list = context.Query("select id,stu_name from student").ToList(); 42 | //多结果集 43 | using(var multi = context.QueryMultiple("select id,stu_name from student;select count(1) from student")) 44 | { 45 | var list = multi.Read(); 46 | var count = multi.ReadFirst(); 47 | } 48 | public class Student 49 | { 50 | public int Id {get;set;} 51 | public string StuName {get; set;} 52 | } 53 | 54 | ``` 55 | 56 | ## Sqlbuilder 57 | 58 | ``` C# 59 | 60 | var b1 = new SqlBuilder(); 61 | b1.Where("id>@Id") 62 | .Where("score>@Score") 63 | .Join("student_name as b on a.id=b.sid"); 64 | var p = new { Age = 1, Score = 20 }; 65 | //对两个模板进行构建 66 | var tmp1 = b1.Build("select * from student as a /**join**/ /**where**/ "); 67 | var tmp2 = b1.Build("select count(1) from student as a /**join**/ /**where**/ "); 68 | using(var multi = context.QueryMultiple($"{tmp1.RawSql};{tmp2.RawSql}",new {Id=1,Score=5})) 69 | { 70 | var list = multi.Read(); 71 | var count = multi.ReadFirst(); 72 | } 73 | 74 | ``` 75 | ## Linq查询 76 | 77 | ``` C# 78 | var entity = context.From().Where(a=>a.id==1).Single(); 79 | var entitys = context.From().Where(a=>a.id>1).Select(); 80 | var entiityNames = context.From().Select(s=>new 81 | { 82 | s.Name, 83 | s.Age 84 | }); 85 | //默认忽略:这会忽略id字段 86 | var row = context.From().Insert(new {Name="zs",Age=1}); 87 | //默认忽略:这会忽略Age字段 88 | var row = context.From().Update(new {Name="zs",Id=1}); 89 | //显示忽略:这会忽略所有为null的字段 90 | var row = context.From().Ignore().Update(new Student{Name="zs",Id=1}); 91 | 92 | public class Student 93 | { 94 | [PrimaryKey] 95 | [Indenity] 96 | public int Id {get;set;} 97 | public string Name {get;set;} 98 | public int Age {get;set;} 99 | } 100 | ``` 101 | 102 | ## 自定义类型映射 103 | 104 | 105 | ``` C# 106 | /// 107 | /// 提供json转换能力 108 | /// 109 | public class MyDDbContextBehavior : DbContextBehavior 110 | { 111 | readonly static MethodInfo _methodInfo = typeof(MyDDbContextBehavior).GetMethod("StringToJson"); 112 | 113 | static readonly JsonSerializerOptions _jsonSerializerOptions; 114 | 115 | static MyDDbContextBehavior() 116 | { 117 | _jsonSerializerOptions = new JsonSerializerOptions 118 | { 119 | Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), 120 | PropertyNameCaseInsensitive = true, 121 | PropertyNamingPolicy=JsonNamingPolicy.CamelCase 122 | }; 123 | _jsonSerializerOptions.Converters.Add(new JsonDateTimeConverter("yyyy-MM-dd HH:mm:ss")); 124 | _jsonSerializerOptions.Converters.Add(new JsonDateTimeNullableConverter("yyyy-MM-dd HH:mm:ss")); 125 | } 126 | 127 | public static T StringToJson(IDataRecord record, int i) 128 | { 129 | if (record.IsDBNull(i)) 130 | { 131 | return default; 132 | } 133 | var json = record.GetString(i); 134 | return JsonSerializer.Deserialize(json, _jsonSerializerOptions); 135 | } 136 | 137 | public override KeyValuePair CreateDbCommandParameter(string name, object value) 138 | { 139 | if (value != null && typeof(ValueObject).IsInstanceOfType(value)) 140 | { 141 | var json = JsonSerializer.Serialize(value, _jsonSerializerOptions); 142 | return base.CreateDbCommandParameter(name, json); 143 | } 144 | return base.CreateDbCommandParameter(name, value); 145 | } 146 | 147 | protected override MethodInfo FindConvertMethod(Type entityType, Type memberType, DataReaderField recordField) 148 | { 149 | if (typeof(ValueObject).IsAssignableFrom(memberType)) 150 | { 151 | return _methodInfo.MakeGenericMethod(memberType); 152 | } 153 | return base.FindConvertMethod(entityType, memberType, recordField); 154 | } 155 | } 156 | var connectionString = @"server=127.0.0.1;user id=root;password=1024;database=sqlbatis;"; 157 | var connection = new MySqlConnection(connectionString); 158 | //3.设置默认的数据库上下文行为 159 | new MyDbContext(new DbContextBuilder() 160 | { 161 | Connection = new MySqlConnector.MySqlConnection(configuration.GetConnectionString("Mysql")), 162 | DbContextType = DbContextType.Mysql, 163 | DbContextBehavior = new MyDDbContextBehavior() 164 | }) 165 | ``` 166 | ## 定义简单的函数 167 | 168 | ```C# 169 | [SqlBatis.Attributes.Function] 170 | public static class SqlFun 171 | { 172 | public static T2 IF(T1 column, T2 v1, T2 v2) 173 | { 174 | return default; 175 | } 176 | 177 | public static bool ISNULL(T1 t1) 178 | { 179 | return default; 180 | } 181 | } 182 | 183 | var list = _context.From() 184 | .Ignore(a => a.StuGender) 185 | .Select(s => new 186 | { 187 | FF = SqlFun.IF(SqlFun.ISNULL(s.SchId), 1, 2) 188 | }); 189 | ``` 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/References/MySql.Data.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soul-soft/SqlBatis/f9ff8f9c3b614fe7b1383d3723dab94048753903/src/References/MySql.Data.dll -------------------------------------------------------------------------------- /src/SqlBatis.Extensions.Dapper/DapperDbContext.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Threading.Tasks; 5 | 6 | namespace SqlBatis 7 | { 8 | public class DaprDbContext : DbContext 9 | { 10 | static DaprDbContext() 11 | { 12 | Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 13 | } 14 | public static bool MatchNamesWithUnderscores 15 | { 16 | get 17 | { 18 | return Dapper.DefaultTypeMap.MatchNamesWithUnderscores; 19 | } 20 | set 21 | { 22 | Dapper.DefaultTypeMap.MatchNamesWithUnderscores = value; 23 | } 24 | } 25 | public DaprDbContext 26 | (DbContextBuilder builder) : base(builder) 27 | { 28 | 29 | } 30 | public override int Execute(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 31 | { 32 | return Connection.Execute(sql, parameter, Transaction, commandTimeout, commandType); 33 | } 34 | public override Task ExecuteAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 35 | { 36 | return Connection.ExecuteAsync(sql, parameter, Transaction, commandTimeout, commandType); 37 | } 38 | public override object ExecuteScalar(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 39 | { 40 | return Connection.ExecuteScalar(sql, parameter, Transaction, commandTimeout, commandType); 41 | } 42 | public override T ExecuteScalar(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 43 | { 44 | return Connection.ExecuteScalar(sql, parameter, Transaction, commandTimeout, commandType); 45 | } 46 | public override Task ExecuteScalarAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 47 | { 48 | return Connection.ExecuteScalarAsync(sql, parameter, Transaction, commandTimeout, commandType); 49 | } 50 | public override IEnumerable Query(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 51 | { 52 | return Connection.Query(sql, parameter, Transaction, false, commandTimeout, commandType); 53 | } 54 | public override IEnumerable Query(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 55 | { 56 | return Connection.Query(sql, parameter, Transaction, false, commandTimeout, commandType); 57 | } 58 | public override async Task> QueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 59 | { 60 | var list = await Connection.QueryAsync(sql, parameter, Transaction, commandTimeout, commandType); 61 | return list.AsList(); 62 | } 63 | public override Task> QueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 64 | { 65 | return Connection.QueryAsync(sql, parameter, Transaction, commandTimeout, commandType); 66 | } 67 | public override IDbGridReader QueryMultiple(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) 68 | { 69 | var reader = Connection.QueryMultiple(sql, parameter, Transaction, commandTimeout, commandType); 70 | return new DapperDbMultipleResult(reader); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/SqlBatis.Extensions.Dapper/DapperDbMultipleResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Dapper; 6 | 7 | namespace SqlBatis 8 | { 9 | public class DapperDbMultipleResult : IDbGridReader 10 | { 11 | readonly SqlMapper.GridReader _reader; 12 | 13 | public DapperDbMultipleResult(SqlMapper.GridReader reader) 14 | { 15 | _reader = reader; 16 | } 17 | public void Dispose() 18 | { 19 | _reader?.Dispose(); 20 | GC.SuppressFinalize(this); 21 | } 22 | 23 | public object ReadFirst() 24 | { 25 | return _reader.ReadFirst(); 26 | } 27 | 28 | public T ReadFirst() 29 | { 30 | return _reader.ReadFirst(); 31 | } 32 | 33 | public Task ReadFirstAsync() 34 | { 35 | return _reader.ReadFirstAsync(); 36 | } 37 | 38 | public Task ReadFirstAsync() 39 | { 40 | return _reader.ReadFirstAsync(); 41 | } 42 | 43 | public List Read() 44 | { 45 | return _reader.Read(false).AsList(); 46 | } 47 | 48 | public List Read() 49 | { 50 | return _reader.Read(false).AsList(); 51 | } 52 | 53 | public async Task> ReadAsync() 54 | { 55 | var list = await _reader.ReadAsync(false); 56 | return list.AsList(); 57 | } 58 | 59 | public async Task> ReadAsync() 60 | { 61 | var list = await _reader.ReadAsync(false); 62 | return list.AsList(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/SqlBatis.Extensions.Dapper/SqlBatis - Backup.Extensions.Dapper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(NoWarn);1591 5 | netstandard2.0;net5 6 | https://github.com/1448376744/SqlBatis 7 | https://github.com/1448376744/SqlBatis 8 | github 9 | orm;sql;micro-orm;mybatis 10 | A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite etc.. 11 | Apache-2.0 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/SqlBatis.Extensions.Dapper/SqlBatis.Extensions.Dapper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(NoWarn);1591 5 | netstandard2.0;net5 6 | https://github.com/1448376744/SqlBatis 7 | https://github.com/1448376744/SqlBatis 8 | github 9 | orm;sql;micro-orm;mybatis 10 | A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite etc.. 11 | Apache-2.0 12 | true 13 | 2.0.0.3 14 | 2.0.0.3 15 | 2.0.0.3 16 | 17 | 18 | 19 | D:\SqlBatis\src\SqlBatis.Extensions.Dapper\SqlBatis.Extensions.Dapper.xml 20 | 21 | 22 | 23 | D:\SqlBatis\src\SqlBatis.Extensions.Dapper\SqlBatis.Extensions.Dapper.xml 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/SqlBatis.Extensions.Dapper/SqlBatis.Extensions.Dapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SqlBatis.Extensions.Dapper 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/SqlBatis.XUnit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | all 28 | 29 | 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | all 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Always 43 | 44 | 45 | MySqlEntities.cs 46 | TextTemplatingFileGenerator 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | True 57 | True 58 | MySqlEntities.tt 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/Templates/MySqlDbContext.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis; 2 | using System.Data; 3 | using SqlBatis.Queryables; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace SqlBatis.XUnit 7 | { 8 | /// 9 | /// 数据库上下文 10 | /// 11 | public class MyDbContext : DbContext 12 | { 13 | private readonly ILogger _logger; 14 | 15 | public MyDbContext(DbContextBuilder builder,ILogger logger) 16 | :base(builder) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | protected override IDbCommand CreateDbCommand(string sql, object parameter, int? commandTimeout = null, CommandType? commandType = null) 22 | { 23 | _logger.LogInformation(sql); 24 | return base.CreateDbCommand(sql, parameter, commandTimeout, commandType); 25 | } 26 | 27 | /// 28 | /// 29 | /// 30 | public IDbQueryable Student 31 | { 32 | get 33 | { 34 | return new DbQueryable(this); 35 | 36 | } 37 | } 38 | 39 | } 40 | } 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/Templates/MySqlDbContext.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ assembly name="System.Data" #> 4 | <#@ assembly name="$(SolutionDir)\References\MySql.Data.dll" #> 5 | <#@ import namespace="System.Linq" #> 6 | <#@ import namespace="System.Text" #> 7 | <#@ import namespace="System.Collections.Generic" #> 8 | <#@ import namespace="System.Data" #> 9 | <#@ import namespace="MySql.Data.MySqlClient" #> 10 | <#@ output extension=".cs" #> 11 | <# 12 | //数据库连接字符串 13 | var connectionString = "server=127.0.0.1;user id=root;password=1024;database=test;"; 14 | #> 15 | <# 16 | var connection = new MySqlConnection(connectionString); 17 | var tablesql = string.Format("SELECT TABLE_NAME as TableName,TABLE_TYPE as TableType,TABLE_COMMENT as TableComment from INFORmation_schema.TABLES WHERE TABLE_SCHEMA='{0}'", connection.Database); 18 | var columnsql = string.Format("SELECT TABLE_NAME as TableName,COLUMN_NAME as ColumnName,COLUMN_COMMENT as ColumnComment,COLUMN_DEFAULT as ColumnDefault,IS_NULLABLE as IsNullable,DATA_TYPE as DataType,COLUMN_TYPE as ColumnType,COLUMN_KEY as ColumnKey,EXTRA as Extra FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='{0}' ORDER BY ORDINAL_POSITION", connection.Database); 19 | var tables = Utils.Query(connection,tablesql); 20 | var columns = Utils.Query(connection,columnsql); 21 | connection.Close(); 22 | #> 23 | using SqlBatis; 24 | using System.Data; 25 | using SqlBatis.Queryables; 26 | using Microsoft.Extensions.Logging; 27 | 28 | namespace SqlBatis.XUnit 29 | { 30 | /// 31 | /// 数据库上下文 32 | /// 33 | public class MyDbContext : DbContext 34 | { 35 | private readonly ILogger _logger; 36 | 37 | public MyDbContext(DbContextBuilder builder,ILogger logger) 38 | :base(builder) 39 | { 40 | _logger = logger; 41 | } 42 | 43 | protected override IDbCommand CreateDbCommand(string sql, object parameter, int? commandTimeout = null, CommandType? commandType = null) 44 | { 45 | _logger.LogInformation(sql); 46 | return base.CreateDbCommand(sql, parameter, commandTimeout, commandType); 47 | } 48 | <#foreach(var table in tables){#> 49 | 50 | /// 51 | /// <#=table.TableComment#> 52 | /// 53 | public IDbQueryable<<#=Utils.Pascal(table.TableName)#>Dto> <#=Utils.Pascal(table.TableName)#> 54 | { 55 | get 56 | { 57 | return new DbQueryable<<#=Utils.Pascal(table.TableName)#>Dto>(this); 58 | 59 | } 60 | } 61 | <#}#> 62 | 63 | } 64 | } 65 | 66 | 67 | 68 | <#+ 69 | public class Table 70 | { 71 | public string TableName { get; set; } 72 | public string TableType { get; set; } 73 | public string TableComment { get; set; } 74 | } 75 | public class Column 76 | { 77 | public string TableName { get; set; } 78 | public string ColumnName { get; set; } 79 | public string ColumnComment { get; set; } 80 | public string ColumnDefault { get; set; } 81 | public string IsNullable { get; set; } 82 | public string ColumnType { get; set; } 83 | public string DataType { get; set; } 84 | public string ColumnKey { get; set; } 85 | public string Extra { get; set; } 86 | } 87 | public static class Utils 88 | { 89 | //字段类型映射 90 | public static string GetCSharpType(string columnType) 91 | { 92 | var type = "object"; 93 | switch (columnType) 94 | { 95 | case "varchar": type = "string"; break; 96 | case "text": type = "string"; break; 97 | case "char": type = "string"; break; 98 | case "bit": type = "bool?"; break; 99 | case "tinyint": type = "int?"; break; 100 | case "smallint": type = "int?"; break; 101 | case "int": type = "int?"; break; 102 | case "integer": type = "int?"; break; 103 | case "bigint": type = "int?"; break; 104 | case "mediumint": type = "int?"; break; 105 | case "real": type = "float?"; break; 106 | case "float": type = "float?"; break; 107 | case "double": type = "double?"; break; 108 | case "decimal": type = "decimal?"; break; 109 | case "date": type = "DateTime?"; break; 110 | case "datetime": type = "DateTime?"; break; 111 | case "json": type = "System.Text.Json.JsonElement?"; break; 112 | } 113 | return type; 114 | } 115 | //Pacsl命名转换 116 | public static string Pascal(string name) 117 | { 118 | var list = new List(); 119 | foreach (var item in name.Split('_')) 120 | { 121 | list.Add(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(item.ToLower())); 122 | } 123 | return string.Join("",list); 124 | } 125 | //Camel命名转换 126 | public static string Camel(string name) 127 | { 128 | name = Pascal(name); 129 | return char.ToLower(name[0]) + name.Substring(1); 130 | } 131 | //数据库查询 132 | public static List Query(IDbConnection connection, string sql,object param=null) 133 | { 134 | if (connection.State!=ConnectionState.Open) 135 | { 136 | connection.Open(); 137 | } 138 | var cmd = connection.CreateCommand(); 139 | cmd.CommandText = sql; 140 | var list = new List(); 141 | using (var reader = cmd.ExecuteReader()) 142 | { 143 | while (reader.Read()) 144 | { 145 | var obj = Activator.CreateInstance(typeof(T)); 146 | foreach (var item in obj.GetType().GetProperties()) 147 | { 148 | var i = reader.GetOrdinal(item.Name); 149 | if (!reader.IsDBNull(i)) 150 | { 151 | item.SetValue(obj,reader.GetValue(i)); 152 | } 153 | } 154 | list.Add((T)obj); 155 | } 156 | } 157 | return list; 158 | } 159 | } 160 | #> -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/Templates/MySqlEntities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SqlBatis.Attributes; 3 | 4 | namespace SqlBatis.XUnit 5 | { 6 | 7 | /// 8 | /// 9 | /// 10 | [Table("address")] 11 | public partial class AddressDto 12 | { 13 | 14 | /// 15 | /// 16 | /// 17 | [Column("id")] 18 | [PrimaryKey] 19 | public int? Id { get; set; } 20 | 21 | /// 22 | /// 23 | /// 24 | [Column("addr_name")] 25 | public string AddrName { get; set; } 26 | } 27 | 28 | /// 29 | /// 30 | /// 31 | [Table("school")] 32 | public partial class SchoolDto 33 | { 34 | 35 | /// 36 | /// 37 | /// 38 | [Column("id")] 39 | [PrimaryKey] 40 | [Identity] 41 | public int? Id { get; set; } 42 | 43 | /// 44 | /// 45 | /// 46 | [Column("sch_name")] 47 | public string SchName { get; set; } 48 | } 49 | 50 | /// 51 | /// 52 | /// 53 | [Table("student")] 54 | public partial class StudentDto 55 | { 56 | 57 | /// 58 | /// 59 | /// 60 | [Column("id")] 61 | [PrimaryKey] 62 | [Identity] 63 | public int? Id { get; set; } 64 | 65 | /// 66 | /// 67 | /// 68 | [Column("sch_id")] 69 | public int? SchId { get; set; } 70 | 71 | /// 72 | /// 73 | /// 74 | [Column("addr_id")] 75 | public int? AddrId { get; set; } 76 | 77 | /// 78 | /// 79 | /// 80 | [Column("stu_name")] 81 | public string StuName { get; set; } 82 | 83 | /// 84 | /// 85 | /// 86 | [Column("stu_gender")] 87 | public bool? StuGender { get; set; } 88 | 89 | /// 90 | /// 91 | /// 92 | [Column("score")] 93 | public double? Score { get; set; } 94 | 95 | /// 96 | /// 97 | /// 98 | [Column("version")] 99 | [ConcurrencyCheck] 100 | public string Version { get; set; } 101 | 102 | /// 103 | /// 104 | /// 105 | [Column("create_time")] 106 | [Default] 107 | public DateTime? CreateTime { get; set; } 108 | } 109 | } 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/Templates/MySqlEntities.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ assembly name="System.Data" #> 4 | <#@ assembly name="System.Data.Common" #> 5 | <#@ assembly name="$(SolutionDir)\References\MySql.Data.dll" #> 6 | <#@ import namespace="System.Linq" #> 7 | <#@ import namespace="System.Text" #> 8 | <#@ import namespace="System.Collections.Generic" #> 9 | <#@ import namespace="System.Data" #> 10 | <#@ import namespace="System.Data.Common" #> 11 | <#@ import namespace="MySql.Data.MySqlClient" #> 12 | <#@ output extension=".cs" #> 13 | <# 14 | //数据库连接字符串 15 | var connectionString = "server=127.0.0.1;user id=root;password=1024;database=test;"; 16 | #> 17 | <# 18 | var connection = new MySqlConnection(connectionString); 19 | var tablesql = string.Format("SELECT TABLE_NAME as TableName,TABLE_TYPE as TableType,TABLE_COMMENT as TableComment from INFORmation_schema.TABLES WHERE TABLE_SCHEMA='{0}'", connection.Database); 20 | var columnsql = string.Format("SELECT TABLE_NAME as TableName,COLUMN_NAME as ColumnName,COLUMN_COMMENT as ColumnComment,COLUMN_DEFAULT as ColumnDefault,IS_NULLABLE as IsNullable,DATA_TYPE as DataType,COLUMN_TYPE as ColumnType,COLUMN_KEY as ColumnKey,EXTRA as Extra FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='{0}' ORDER BY ORDINAL_POSITION", connection.Database); 21 | var tables = Utils.Query
(connection,tablesql); 22 | var columns = Utils.Query(connection,columnsql); 23 | connection.Close(); 24 | #> 25 | using System; 26 | using SqlBatis.Attributes; 27 | 28 | namespace SqlBatis.XUnit 29 | { 30 | <#foreach(var table in tables){#> 31 | 32 | /// 33 | /// <#=table.TableComment#> 34 | /// 35 | [Table("<#=table.TableName#>")] 36 | public partial class <#=Utils.Pascal(table.TableName)#>Dto 37 | { 38 | <#foreach(var column in columns.FindAll(f => f.TableName == table.TableName)){#> 39 | /// 40 | /// <#=column.ColumnComment#> 41 | /// 42 | [Column("<#= column.ColumnName #>")] 43 | <# if(column.ColumnKey == "PRI") #>[PrimaryKey] 44 | <# if(column.Extra == "auto_increment") #>[Identity] 45 | <# if(column.ColumnDefault != null) #>[Default] 46 | <# if(column.ColumnName == "version") #>[ConcurrencyCheck] 47 | <#= "public "+Utils.GetCSharpType(column.DataType) #> <#= Utils.Pascal(column.ColumnName) #> { get; set; } 48 | <#}#>} 49 | <#}#> 50 | } 51 | 52 | 53 | 54 | <#+ 55 | public class Table 56 | { 57 | public string TableName { get; set; } 58 | public string TableType { get; set; } 59 | public string TableComment { get; set; } 60 | } 61 | public class Column 62 | { 63 | public string TableName { get; set; } 64 | public string ColumnName { get; set; } 65 | public string ColumnComment { get; set; } 66 | public string ColumnDefault { get; set; } 67 | public string IsNullable { get; set; } 68 | public string ColumnType { get; set; } 69 | public string DataType { get; set; } 70 | public string ColumnKey { get; set; } 71 | public string Extra { get; set; } 72 | } 73 | public static class Utils 74 | { 75 | //字段类型映射 76 | public static string GetCSharpType(string columnType) 77 | { 78 | var type = "object"; 79 | switch (columnType) 80 | { 81 | case "varchar": type = "string"; break; 82 | case "text": type = "string"; break; 83 | case "char": type = "string"; break; 84 | case "bit": type = "bool?"; break; 85 | case "tinyint": type = "int?"; break; 86 | case "smallint": type = "int?"; break; 87 | case "int": type = "int?"; break; 88 | case "integer": type = "int?"; break; 89 | case "bigint": type = "int?"; break; 90 | case "mediumint": type = "int?"; break; 91 | case "real": type = "float?"; break; 92 | case "float": type = "float?"; break; 93 | case "double": type = "double?"; break; 94 | case "decimal": type = "decimal?"; break; 95 | case "date": type = "DateTime?"; break; 96 | case "datetime": type = "DateTime?"; break; 97 | case "json": type = "System.Text.Json.JsonElement?"; break; 98 | } 99 | return type; 100 | } 101 | //Pacsl命名转换 102 | public static string Pascal(string name) 103 | { 104 | var list = new List(); 105 | foreach (var item in name.Split('_')) 106 | { 107 | list.Add(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(item.ToLower())); 108 | } 109 | return string.Join("",list); 110 | } 111 | //Camel命名转换 112 | public static string Camel(string name) 113 | { 114 | name = Pascal(name); 115 | return char.ToLower(name[0]) + name.Substring(1); 116 | } 117 | //数据库查询 118 | public static List Query(IDbConnection connection, string sql,object param=null) 119 | { 120 | if (connection.State!=ConnectionState.Open) 121 | { 122 | connection.Open(); 123 | } 124 | var cmd = connection.CreateCommand(); 125 | cmd.CommandText = sql; 126 | var list = new List(); 127 | using (var reader = cmd.ExecuteReader()) 128 | { 129 | while (reader.Read()) 130 | { 131 | var obj = Activator.CreateInstance(typeof(T)); 132 | foreach (var item in obj.GetType().GetProperties()) 133 | { 134 | var i = reader.GetOrdinal(item.Name); 135 | if (!reader.IsDBNull(i)) 136 | { 137 | item.SetValue(obj,reader.GetValue(i)); 138 | } 139 | } 140 | list.Add((T)obj); 141 | } 142 | } 143 | return list; 144 | } 145 | } 146 | #> -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/Templates/SqlServerDbContext.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #><#@ assembly name="System.Core" #><#@ assembly name="System.Data" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Text" #><#@ import namespace="System.Collections.Generic" #><#@ import namespace="System.Data" #><#@ output extension=".cs" #> 2 | <# 3 | //数据库连接字符串 4 | var connectionString = @"Data Source=127.0.0.1;Initial Catalog=test;User ID=sa;Password=111"; 5 | var connection = new System.Data.SqlClient.SqlConnection(connectionString); 6 | var tables = Utils.GetTables(connection); 7 | var columns = Utils.GetColumns(connection); 8 | #> 9 | using SqlBatis; 10 | using System.Data; 11 | using SqlBatis.Queryables; 12 | 13 | namespace SqlBatis.XUnit 14 | { 15 | /// 16 | /// 数据库上下文 17 | /// 18 | public class MyDbContext : DbContext 19 | { 20 | public MyDbContext(DbContextBuilder builder) 21 | :base(builder) 22 | { 23 | } 24 | 25 | <#foreach(var table in tables){#> 26 | 27 | /// 28 | /// <#=table.Description ?? ""#> 29 | /// 30 | public IDbQueryable<<#=Utils.Pascal(table.TableName)#>Dto> <#=Utils.Pascal(table.TableName)#> 31 | { 32 | get 33 | { 34 | return new DbQueryable<<#=Utils.Pascal(table.TableName)#>Dto>(this); 35 | } 36 | } 37 | <#}#> 38 | } 39 | } 40 | <#+ 41 | public class Table 42 | { 43 | public int? TableId { get; set; } 44 | public string TableTypeDesc { get; set; } 45 | public string TableName { get; set; } 46 | public string SchemaName { get; set; } 47 | public string Description { get; set; } 48 | } 49 | public class Column 50 | { 51 | public int? TableId { get; set; } 52 | public string ColumnName { get; set; } 53 | public string SystemTypeName { get; set; } 54 | public short MaxLength { get; set; } 55 | public bool IsNullable { get; set; } 56 | public bool IsIdentity { get; set; } 57 | public bool IsComputed { get; set; } 58 | public bool IsDefault { get; set; } 59 | public string Description { get; set; } 60 | } 61 | public static class Utils 62 | { 63 | //字段类型映射 64 | public static string GetCSharpType(string columnType) 65 | { 66 | var type = "object"; 67 | switch (columnType) 68 | { 69 | case "varchar": type = "string"; break; 70 | case "text": type = "string"; break; 71 | case "char": type = "string"; break; 72 | case "tinyint": type = "int?"; break; 73 | case "smallint": type = "int?"; break; 74 | case "int": type = "int?"; break; 75 | case "integer": type = "int?"; break; 76 | case "bigint": type = "int?"; break; 77 | case "mediumint": type = "int?"; break; 78 | case "real": type = "float?"; break; 79 | case "float": type = "float?"; break; 80 | case "double": type = "double?"; break; 81 | case "decimal": type = "decimal?"; break; 82 | case "date": type = "DateTime?"; break; 83 | case "datetime": type = "DateTime?"; break; 84 | } 85 | return type; 86 | } 87 | //Pacsl命名转换 88 | public static string Pascal(string name) 89 | { 90 | var list = new List(); 91 | foreach (var item in name.Split('_')) 92 | { 93 | list.Add(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(item.ToLower())); 94 | } 95 | return string.Join("",list); 96 | } 97 | //Camel命名转换 98 | public static string Camel(string name) 99 | { 100 | name = Pascal(name); 101 | return char.ToLower(name[0]) + name.Substring(1); 102 | } 103 | //数据库查询 104 | public static List Query(IDbConnection connection, string sql, object param=null) 105 | { 106 | if (connection.State!=ConnectionState.Open) 107 | { 108 | connection.Open(); 109 | } 110 | var cmd = connection.CreateCommand(); 111 | cmd.CommandText = sql; 112 | param?.GetType().GetProperties().ToList().ForEach(item=> 113 | { 114 | var p = cmd.CreateParameter(); 115 | p.ParameterName = "@"+ item.Name; 116 | p.Value = item.GetValue(param); 117 | cmd.Parameters.Add(p); 118 | }); 119 | var list = new List(); 120 | using (var reader = cmd.ExecuteReader()) 121 | { 122 | while (reader.Read()) 123 | { 124 | var entity = Activator.CreateInstance(); 125 | foreach (var item in entity.GetType().GetProperties()) 126 | { 127 | var i = reader.GetOrdinal(item.Name); 128 | if (!reader.IsDBNull(i)) 129 | { 130 | item.SetValue(entity, reader.GetValue(i)); 131 | } 132 | } 133 | list.Add(entity); 134 | } 135 | } 136 | return list; 137 | } 138 | //获取所有表 139 | public static List
GetTables(IDbConnection connection) 140 | { 141 | //视图SQL 142 | var tablesql = @"SELECT 143 | [Tables].[object_id] AS [TableId], 144 | [Tables].[type_desc] AS [TableTypeDesc], 145 | [Tables].[name] AS [TableName], 146 | [Schemas].[name] AS [SchemaName], 147 | [Properties].[value] AS [Description] 148 | FROM 149 | sys.objects AS [Tables] 150 | INNER JOIN sys.schemas AS [Schemas] ON [Tables].schema_id = [Schemas].schema_id 151 | LEFT JOIN sys.extended_properties AS [Properties] ON [Properties].major_id=[Tables].object_id AND [Properties].minor_id=0 WHERE (Tables.type='V' OR Tables.type='U')"; 152 | return Utils.Query
(connection, tablesql).ToList(); 153 | } 154 | //获取所有字段 155 | public static List GetColumns(IDbConnection connection) 156 | { 157 | //字段SQL 158 | var columnsql = @"SELECT 159 | [TableId] = [Columns].object_id, 160 | [ColumnName] = [Columns].name, 161 | [SystemTypeName] = [Types].name, 162 | [MaxLength] = [Columns].max_length, 163 | [IsNullable] = [Columns].is_nullable, 164 | [IsIdentity] = [Columns].is_identity, 165 | [IsComputed] = [Columns].is_computed, 166 | [IsDefault] = CASE [Columns].default_object_id WHEN 0 THEN CAST(0 AS bit) ELSE CAST(1 AS bit) END, 167 | [Description] = [Properties].value 168 | FROM 169 | sys.objects AS [Tables] 170 | INNER JOIN sys.columns AS [Columns] ON [Tables].object_id = [Columns].object_id AND ([Tables].type='V' OR [Tables].type='U') 171 | INNER JOIN sys.types AS [Types] ON [Columns].system_type_id = [Types].system_type_id 172 | AND is_user_defined = 0 173 | AND [Types].name <> 'sysname' 174 | LEFT OUTER JOIN sys.extended_properties AS [Properties] ON [Properties].major_id = [Tables].object_id 175 | AND [Properties].minor_id = [Columns].column_id 176 | AND [Properties].name = 'MS_Description'"; 177 | return Utils.Query(connection, columnsql).ToList(); 178 | } 179 | } 180 | #> -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/Templates/SqlServerEntities.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" #><#@ assembly name="System.Core" #><#@ assembly name="System.Data" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Text" #><#@ import namespace="System.Collections.Generic" #><#@ import namespace="System.Data" #><#@ output extension=".cs" #> 2 | using System; 3 | using SqlBatis.Attributes; 4 | 5 | namespace SqlBatis.XUnit 6 | { 7 | <# 8 | //数据库连接字符串 9 | var connectionString = @"Data Source=127.0.0.1;Initial Catalog=test;User ID=sa;Password=111"; 10 | var connection = new System.Data.SqlClient.SqlConnection(connectionString); 11 | var tables = Utils.GetTables(connection); 12 | var columns = Utils.GetColumns(connection); 13 | #> 14 | <#foreach(var table in tables){#> 15 | /// 16 | /// <#=table.Description ?? ""#> 17 | /// 18 | [Table("<#=table.TableName#>")] 19 | public class <#=Utils.Pascal(table.TableName)#>Dto 20 | { 21 | <#foreach(var column in columns.FindAll(f=>f.TableId == table.TableId)){#> 22 | /// 23 | /// <#=column.Description ?? ""#> 24 | /// 25 | [Column("[<#=column.ColumnName#>]")] 26 | <#if(column.IsIdentity)#>[PrimaryKey] 27 | <#if(column.IsIdentity)#>[Identity] 28 | <#="public "+Utils.GetCSharpType(column.SystemTypeName)#> <#=Utils.Pascal(column.ColumnName)#> { get; set; } 29 | <#}#> 30 | } 31 | <#}#> 32 | } 33 | 34 | <#+ 35 | public class Table 36 | { 37 | public int? TableId { get; set; } 38 | public string TableTypeDesc { get; set; } 39 | public string TableName { get; set; } 40 | public string SchemaName { get; set; } 41 | public string Description { get; set; } 42 | } 43 | public class Column 44 | { 45 | public int? TableId { get; set; } 46 | public string ColumnName { get; set; } 47 | public string SystemTypeName { get; set; } 48 | public short MaxLength { get; set; } 49 | public bool IsNullable { get; set; } 50 | public bool IsIdentity { get; set; } 51 | public bool IsComputed { get; set; } 52 | public bool IsDefault { get; set; } 53 | public string Description { get; set; } 54 | } 55 | public static class Utils 56 | { 57 | //字段类型映射 58 | public static string GetCSharpType(string columnType) 59 | { 60 | var type = "object"; 61 | switch (columnType) 62 | { 63 | case "char": type = "string"; break; 64 | case "nchar": type = "string"; break; 65 | case "varchar": type = "string"; break; 66 | case "nvarchar": type = "string"; break; 67 | case "text": type = "string"; break; 68 | case "ntext": type = "string"; break; 69 | case "bit": type = "bool?"; break; 70 | case "tinyint": type = "short?"; break; 71 | case "smallint": type = "short?"; break; 72 | case "int": type = "int?"; break; 73 | case "integer": type = "int?"; break; 74 | case "bigint": type = "long?"; break; 75 | case "mediumint": type = "int?"; break; 76 | case "real": type = "float?"; break; 77 | case "float": type = "float?"; break; 78 | case "double": type = "double?"; break; 79 | case "decimal": type = "decimal?"; break; 80 | case "date": type = "DateTime?"; break; 81 | case "datetime": type = "DateTime?"; break; 82 | } 83 | return type; 84 | } 85 | //Pacsl命名转换 86 | public static string Pascal(string name) 87 | { 88 | var list = new List(); 89 | foreach (var item in name.Split('_')) 90 | { 91 | list.Add(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(item.ToLower())); 92 | } 93 | return string.Join("",list); 94 | } 95 | //Camel命名转换 96 | public static string Camel(string name) 97 | { 98 | name = Pascal(name); 99 | return char.ToLower(name[0]) + name.Substring(1); 100 | } 101 | //数据库查询 102 | public static List Query(IDbConnection connection, string sql, object param=null) 103 | { 104 | if (connection.State!=ConnectionState.Open) 105 | { 106 | connection.Open(); 107 | } 108 | var cmd = connection.CreateCommand(); 109 | cmd.CommandText = sql; 110 | param?.GetType().GetProperties().ToList().ForEach(item=> 111 | { 112 | var p = cmd.CreateParameter(); 113 | p.ParameterName = "@"+ item.Name; 114 | p.Value = item.GetValue(param); 115 | cmd.Parameters.Add(p); 116 | }); 117 | var list = new List(); 118 | using (var reader = cmd.ExecuteReader()) 119 | { 120 | while (reader.Read()) 121 | { 122 | var entity = Activator.CreateInstance(); 123 | foreach (var item in entity.GetType().GetProperties()) 124 | { 125 | var i = reader.GetOrdinal(item.Name); 126 | if (!reader.IsDBNull(i)) 127 | { 128 | item.SetValue(entity, reader.GetValue(i)); 129 | } 130 | } 131 | list.Add(entity); 132 | } 133 | } 134 | return list; 135 | } 136 | //获取所有表 137 | public static List
GetTables(IDbConnection connection) 138 | { 139 | //视图SQL 140 | var tablesql = @"SELECT 141 | [Tables].[object_id] AS [TableId], 142 | [Tables].[type_desc] AS [TableTypeDesc], 143 | [Tables].[name] AS [TableName], 144 | [Schemas].[name] AS [SchemaName], 145 | [Properties].[value] AS [Description] 146 | FROM 147 | sys.objects AS [Tables] 148 | 149 | INNER JOIN sys.schemas AS [Schemas] ON [Tables].schema_id = [Schemas].schema_id 150 | LEFT JOIN sys.extended_properties AS [Properties] ON [Properties].major_id=[Tables].object_id AND [Properties].minor_id=0 WHERE (Tables.type='V' OR Tables.type='U')" ; 151 | return Utils.Query
(connection, tablesql).ToList(); 152 | } 153 | //获取所有字段 154 | public static List GetColumns(IDbConnection connection) 155 | { 156 | //字段SQL 157 | var columnsql = @"SELECT 158 | [TableId] = [Columns].object_id, 159 | [ColumnName] = [Columns].name, 160 | [SystemTypeName] = [Types].name, 161 | [MaxLength] = [Columns].max_length, 162 | [IsNullable] = [Columns].is_nullable, 163 | [IsIdentity] = [Columns].is_identity, 164 | [IsComputed] = [Columns].is_computed, 165 | [IsDefault] = CASE [Columns].default_object_id WHEN 0 THEN CAST(0 AS bit) ELSE CAST(1 AS bit) END, 166 | [Description] = [Properties].value 167 | FROM 168 | sys.objects AS [Tables] 169 | INNER JOIN sys.columns AS [Columns] ON [Tables].object_id = [Columns].object_id AND ([Tables].type='V' OR [Tables].type='U') 170 | INNER JOIN sys.types AS [Types] ON [Columns].system_type_id = [Types].system_type_id 171 | AND is_user_defined = 0 172 | AND [Types].name <> 'sysname' 173 | LEFT OUTER JOIN sys.extended_properties AS [Properties] ON [Properties].major_id = [Tables].object_id 174 | AND [Properties].minor_id = [Columns].column_id 175 | AND [Properties].name = 'MS_Description'"; 176 | return Utils.Query(connection, columnsql).ToList(); 177 | } 178 | } 179 | #> -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis.XUnit 6 | { 7 | public class BaseTest 8 | { 9 | protected IDbContext _context = null; 10 | 11 | public BaseTest() 12 | { 13 | _context = new MyDbContext(new DbContextBuilder 14 | { 15 | //Connection = new MySqlConnector.MySqlConnection("server=127.0.0.1;user id=root;password=1024;database=test;"), 16 | //DbContextType = DbContextType.Mysql 17 | Connection = new System.Data.SQLite.SQLiteConnection(@"Data Source=D:\SqlBatis\src\SqlBatis.XUnit\sqlite.db;"), 18 | DbContextType = DbContextType.Sqlite 19 | }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/DeleteTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soul-soft/SqlBatis/f9ff8f9c3b614fe7b1383d3723dab94048753903/src/SqlBatis.XUnit/UnitTests/DeleteTest.cs -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/InsertTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soul-soft/SqlBatis/f9ff8f9c3b614fe7b1383d3723dab94048753903/src/SqlBatis.XUnit/UnitTests/InsertTest.cs -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/MyDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualBasic; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Diagnostics; 6 | using System.Text; 7 | 8 | namespace SqlBatis.XUnit 9 | { 10 | public class MyDbContext : DbContext 11 | { 12 | public MyDbContext(DbContextBuilder builder) 13 | : base(builder) 14 | { 15 | 16 | } 17 | 18 | protected override IDbCommand CreateDbCommand(string sql, object parameter, int? commandTimeout = null, CommandType? commandType = null) 19 | { 20 | Trace.WriteLine("================Command==================="); 21 | Trace.WriteLine(sql); 22 | Trace.WriteLine("==========================================="); 23 | return base.CreateDbCommand(sql, parameter, commandTimeout, commandType); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/QueryTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soul-soft/SqlBatis/f9ff8f9c3b614fe7b1383d3723dab94048753903/src/SqlBatis.XUnit/UnitTests/QueryTest.cs -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/SqlFun.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis.XUnit 6 | { 7 | [SqlBatis.Attributes.Function] 8 | public static class SqlFun 9 | { 10 | public static T2 IF(T1 column, T2 v1, T2 v2) 11 | { 12 | return default; 13 | } 14 | 15 | public static bool ISNULL(T1 t1) 16 | { 17 | return default; 18 | } 19 | 20 | public static T1 COUNT(T1 t1) 21 | { 22 | return default; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/UnitTests/UpdateTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soul-soft/SqlBatis/f9ff8f9c3b614fe7b1383d3723dab94048753903/src/SqlBatis.XUnit/UnitTests/UpdateTest.cs -------------------------------------------------------------------------------- /src/SqlBatis.XUnit/sqlite.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soul-soft/SqlBatis/f9ff8f9c3b614fe7b1383d3723dab94048753903/src/SqlBatis.XUnit/sqlite.db -------------------------------------------------------------------------------- /src/SqlBatis.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlBatis", "SqlBatis\SqlBatis.csproj", "{769AE0A9-2981-4236-B17A-C3CFE03AE7E5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlBatis.Extensions.Dapper", "SqlBatis.Extensions.Dapper\SqlBatis.Extensions.Dapper.csproj", "{BD11BB2D-CCFA-4239-9842-C0150D93A748}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlBatis.XUnit", "SqlBatis.XUnit\SqlBatis.XUnit.csproj", "{36CD734F-19AF-4A93-AA89-27FCC4B0E6AC}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {769AE0A9-2981-4236-B17A-C3CFE03AE7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {769AE0A9-2981-4236-B17A-C3CFE03AE7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {769AE0A9-2981-4236-B17A-C3CFE03AE7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {769AE0A9-2981-4236-B17A-C3CFE03AE7E5}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {BD11BB2D-CCFA-4239-9842-C0150D93A748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {BD11BB2D-CCFA-4239-9842-C0150D93A748}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {BD11BB2D-CCFA-4239-9842-C0150D93A748}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {BD11BB2D-CCFA-4239-9842-C0150D93A748}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {36CD734F-19AF-4A93-AA89-27FCC4B0E6AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {36CD734F-19AF-4A93-AA89-27FCC4B0E6AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {36CD734F-19AF-4A93-AA89-27FCC4B0E6AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {36CD734F-19AF-4A93-AA89-27FCC4B0E6AC}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {F5B03AAC-1FA8-4962-A6BB-EDA00F230542} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/ColumnAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 字段映射 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class ColumnAttribute : Attribute 10 | { 11 | internal string Name { get; set; } 12 | internal Type Type { get; set; } 13 | /// 14 | /// 属性字段映射 15 | /// 16 | /// 字段名 17 | /// 字段类型 18 | public ColumnAttribute(string name = null,Type type=null) 19 | { 20 | Name = name; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/ComplexTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis.Attributes 6 | { 7 | /// 8 | /// 计算列,如果字段一个是计算列则新增和修改的时候不会处理 9 | /// 10 | public class ComplexTypeAttribute : Attribute 11 | { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/ConcurrencyCheckAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis.Attributes 6 | { 7 | /// 8 | /// 并发检查列,如果字段属性是number类型则用时间戳,否则使用GUID 9 | /// 10 | public class ConcurrencyCheckAttribute : Attribute 11 | { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/DefaultAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 默认值约束 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class DefaultAttribute : Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/FunctionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 数据库函数标识 7 | /// 8 | [AttributeUsage(AttributeTargets.Class)] 9 | public class FunctionAttribute : Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/IdentityAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 自增列标识 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class IdentityAttribute : Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/NotMappedAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 忽略映射 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class NotMappedAttribute: Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/PrimaryKeyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 主键约束 7 | /// 8 | [AttributeUsage(AttributeTargets.Property)] 9 | public class PrimaryKeyAttribute : Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SqlBatis/Attributes/TableAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlBatis.Attributes 4 | { 5 | /// 6 | /// 表名映射 7 | /// 8 | [AttributeUsage(AttributeTargets.Class)] 9 | public class TableAttribute : Attribute 10 | { 11 | internal string Name { get; set; } 12 | public TableAttribute(string name = null) 13 | { 14 | Name = name; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/DbContextBuilder.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Expressions; 2 | using System.Data; 3 | 4 | namespace SqlBatis 5 | { 6 | public class DbContextBuilder 7 | { 8 | /// 9 | /// 设置要托管的数据库连接 10 | /// 11 | public IDbConnection Connection { get; set; } 12 | /// 13 | /// 设置数据库类型 14 | /// 15 | public DbContextType DbContextType { get; set; } 16 | /// 17 | /// 上下文行为 18 | /// 19 | public IDbContextBehavior DbContextBehavior { get; set; } = new DbContextBehavior(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/DbContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Queryables; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SqlBatis 9 | { 10 | /// 11 | /// 扩展IDbContext 12 | /// 13 | public static class DbContextExtensions 14 | { 15 | public static IDbQueryable From(this IDbContext context) 16 | { 17 | return new DbQueryable(context); 18 | } 19 | public static IDbQueryable Queryable(this IDbContext context) 20 | { 21 | return new DbQueryable(context); 22 | } 23 | public static IDbQueryable From(this IDbContext context) 24 | { 25 | return new DbQueryable(context); 26 | } 27 | public static IDbQueryable Queryable(this IDbContext context) 28 | { 29 | return new DbQueryable(context); 30 | } 31 | public static IDbQueryable From(this IDbContext context) 32 | { 33 | return new DbQueryable(context); 34 | } 35 | public static IDbQueryable Queryable(this IDbContext context) 36 | { 37 | return new DbQueryable(context); 38 | } 39 | public static IDbQueryable From(this IDbContext context) 40 | { 41 | return new DbQueryable(context); 42 | } 43 | public static IDbQueryable Queryable(this IDbContext context) 44 | { 45 | return new DbQueryable(context); 46 | } 47 | public static int Insert(this IDbContext context, T entity) 48 | { 49 | return new DbQueryable(context).Insert(entity); 50 | } 51 | public static Task InsertAsync(this IDbContext context, T entity) 52 | { 53 | return new DbQueryable(context).InsertAsync(entity); 54 | } 55 | public static int InsertBatch(this IDbContext context, IEnumerable entities, int? commandTimeout = null) 56 | { 57 | return new DbQueryable(context).InsertBatch(entities, commandTimeout); 58 | } 59 | public static Task InsertBatchAsync(this IDbContext context, IEnumerable entities, int? commandTimeout = null) 60 | { 61 | return new DbQueryable(context).InsertBatchAsync(entities, commandTimeout); 62 | } 63 | public static int InsertReturnId(this IDbContext context, T entity) 64 | { 65 | return new DbQueryable(context).InsertReturnId(entity); 66 | } 67 | public static Task InsertReturnIdAsync(this IDbContext context, T entity) 68 | { 69 | return new DbQueryable(context).InsertReturnIdAsync(entity); 70 | } 71 | public static int Update(this IDbContext context, T entity) 72 | { 73 | return new DbQueryable(context).Update(entity); 74 | } 75 | public static Task UpdateAsync(this IDbContext context, T entity) 76 | { 77 | return new DbQueryable(context).UpdateAsync(entity); 78 | } 79 | public static int Delete(this IDbContext context, T entity) 80 | { 81 | return new DbQueryable(context).Delete(entity); 82 | } 83 | public static Task DeleteAsync(this IDbContext context, T entity) 84 | { 85 | return new DbQueryable(context).DeleteAsync(entity); 86 | } 87 | public static int DeleteBatch(this IDbContext context, IEnumerable entities) 88 | { 89 | return new DbQueryable(context).DeleteBatch(entities); 90 | } 91 | public static Task DeleteBatchAsync(this IDbContext context, IEnumerable entities) 92 | { 93 | return new DbQueryable(context).DeleteBatchAsync(entities); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/DbContextState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis 6 | { 7 | public enum DbContextState 8 | { 9 | Closed = 0, 10 | Open = 1, 11 | Commit = 2, 12 | Rollback = 3, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/DbContextType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis 6 | { 7 | public enum DbContextType 8 | { 9 | Mysql, 10 | SqlServer2008, 11 | SqlServer2012, 12 | Postgresql, 13 | Oracle, 14 | Sqlite, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/DbConvertMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SqlBatis 10 | { 11 | internal class DbConvertMethods 12 | { 13 | #region Method Field 14 | internal static MethodInfo ToObjectMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToObject)); 15 | internal static MethodInfo ToByteMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToByte)); 16 | internal static MethodInfo ToIn16Method = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToInt16)); 17 | internal static MethodInfo ToIn32Method = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToInt32)); 18 | internal static MethodInfo ToIn64Method = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToInt64)); 19 | internal static MethodInfo ToFloatMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToFloat)); 20 | internal static MethodInfo ToDoubleMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToDouble)); 21 | internal static MethodInfo ToDecimalMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToDecimal)); 22 | internal static MethodInfo ToBooleanMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToBoolean)); 23 | internal static MethodInfo ToCharMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToChar)); 24 | internal static MethodInfo ToStringMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToString)); 25 | internal static MethodInfo ToTrimStringMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToTrimString)); 26 | internal static MethodInfo ToDateTimeMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToDateTime)); 27 | internal static MethodInfo ToEnumMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToEnum)); 28 | internal static MethodInfo ToGuidMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToGuid)); 29 | #endregion 30 | 31 | #region NullableMethod Field 32 | internal static MethodInfo ToByteNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToByteNullable)); 33 | internal static MethodInfo ToIn16NullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToInt16Nullable)); 34 | internal static MethodInfo ToIn32NullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToInt32Nullable)); 35 | internal static MethodInfo ToIn64NullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToInt64Nullable)); 36 | internal static MethodInfo ToFloatNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToFloatNullable)); 37 | internal static MethodInfo ToDoubleNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToDoubleNullable)); 38 | internal static MethodInfo ToBooleanNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToBooleanNullable)); 39 | internal static MethodInfo ToDecimalNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToDecimalNullable)); 40 | internal static MethodInfo ToCharNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToCharNullable)); 41 | internal static MethodInfo ToDateTimeNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToDateTimeNullable)); 42 | internal static MethodInfo ToEnumNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToEnumNullable)); 43 | internal static MethodInfo ToGuidNullableMethod = typeof(DbConvertMethods).GetMethod(nameof(DbConvertMethods.ConvertToGuidNullable)); 44 | #endregion 45 | 46 | #region Define Convert 47 | public static object ConvertToObject(IDataRecord dr, int i) 48 | { 49 | try 50 | { 51 | if (dr.IsDBNull(i)) 52 | { 53 | return default; 54 | } 55 | return dr.GetValue(i); 56 | } 57 | catch 58 | { 59 | throw ThrowException(dr, i); 60 | } 61 | } 62 | 63 | public static byte ConvertToByte(IDataRecord dr, int i) 64 | { 65 | try 66 | { 67 | if (dr.IsDBNull(i)) 68 | { 69 | return default; 70 | } 71 | var result = dr.GetByte(i); 72 | return result; 73 | } 74 | catch 75 | { 76 | throw ThrowException(dr, i); 77 | } 78 | } 79 | 80 | public static short ConvertToInt16(IDataRecord dr, int i) 81 | { 82 | try 83 | { 84 | if (dr.IsDBNull(i)) 85 | { 86 | return default; 87 | } 88 | return dr.GetInt16(i); 89 | } 90 | catch 91 | { 92 | throw ThrowException(dr, i); 93 | } 94 | } 95 | 96 | public static int ConvertToInt32(IDataRecord dr, int i) 97 | { 98 | try 99 | { 100 | if (dr.IsDBNull(i)) 101 | { 102 | return default; 103 | } 104 | return dr.GetInt32(i); 105 | } 106 | catch 107 | { 108 | throw ThrowException(dr, i); 109 | } 110 | } 111 | 112 | public static long ConvertToInt64(IDataRecord dr, int i) 113 | { 114 | try 115 | { 116 | if (dr.IsDBNull(i)) 117 | { 118 | return default; 119 | } 120 | return dr.GetInt64(i); 121 | } 122 | catch 123 | { 124 | throw ThrowException(dr, i); 125 | } 126 | } 127 | 128 | public static float ConvertToFloat(IDataRecord dr, int i) 129 | { 130 | try 131 | { 132 | if (dr.IsDBNull(i)) 133 | { 134 | return default; 135 | } 136 | return dr.GetFloat(i); 137 | } 138 | catch 139 | { 140 | throw ThrowException(dr, i); 141 | } 142 | } 143 | 144 | public static double ConvertToDouble(IDataRecord dr, int i) 145 | { 146 | try 147 | { 148 | if (dr.IsDBNull(i)) 149 | { 150 | return default; 151 | } 152 | return dr.GetDouble(i); 153 | } 154 | catch 155 | { 156 | throw ThrowException(dr, i); 157 | } 158 | } 159 | 160 | public static bool ConvertToBoolean(IDataRecord dr, int i) 161 | { 162 | try 163 | { 164 | if (dr.IsDBNull(i)) 165 | { 166 | return default; 167 | } 168 | return dr.GetBoolean(i); 169 | } 170 | catch 171 | { 172 | throw ThrowException(dr, i); 173 | } 174 | } 175 | 176 | public static decimal ConvertToDecimal(IDataRecord dr, int i) 177 | { 178 | try 179 | { 180 | if (dr.IsDBNull(i)) 181 | { 182 | return default; 183 | } 184 | return dr.GetDecimal(i); 185 | } 186 | catch 187 | { 188 | throw ThrowException(dr, i); 189 | } 190 | } 191 | 192 | public static char ConvertToChar(IDataRecord dr, int i) 193 | { 194 | try 195 | { 196 | if (dr.IsDBNull(i)) 197 | { 198 | return default; 199 | } 200 | var result = dr.GetChar(i); 201 | return result; 202 | } 203 | catch 204 | { 205 | throw ThrowException(dr, i); 206 | } 207 | } 208 | 209 | public static string ConvertToString(IDataRecord dr, int i) 210 | { 211 | try 212 | { 213 | if (dr.IsDBNull(i)) 214 | { 215 | return default; 216 | } 217 | return dr.GetString(i); 218 | } 219 | catch 220 | { 221 | throw ThrowException(dr, i); 222 | } 223 | } 224 | 225 | public static string ConvertToTrimString(IDataRecord dr, int i) 226 | { 227 | try 228 | { 229 | if (dr.IsDBNull(i)) 230 | { 231 | return default; 232 | } 233 | return dr.GetString(i).Trim(); 234 | } 235 | catch 236 | { 237 | throw ThrowException(dr, i); 238 | } 239 | } 240 | 241 | public static DateTime ConvertToDateTime(IDataRecord dr, int i) 242 | { 243 | try 244 | { 245 | if (dr.IsDBNull(i)) 246 | { 247 | return default; 248 | } 249 | return dr.GetDateTime(i); 250 | } 251 | catch 252 | { 253 | throw ThrowException(dr, i); 254 | } 255 | } 256 | 257 | public static T ConvertToEnum(IDataRecord dr, int i) where T : struct 258 | { 259 | try 260 | { 261 | if (dr.IsDBNull(i)) 262 | { 263 | return default; 264 | } 265 | var value = dr.GetValue(i); 266 | if (Enum.TryParse(value.ToString(), out T result)) return result; 267 | return default; 268 | } 269 | catch 270 | { 271 | throw ThrowException(dr, i); 272 | } 273 | } 274 | 275 | public static Guid ConvertToGuid(IDataRecord dr, int i) 276 | { 277 | try 278 | { 279 | if (dr.IsDBNull(i)) 280 | { 281 | return default; 282 | } 283 | var result = dr.GetGuid(i); 284 | return result; 285 | } 286 | catch 287 | { 288 | throw ThrowException(dr, i); 289 | } 290 | } 291 | 292 | #endregion 293 | 294 | #region Define Nullable Convert 295 | public static byte? ConvertToByteNullable(IDataRecord dr, int i) 296 | { 297 | if (dr.IsDBNull(i)) 298 | { 299 | return default; 300 | } 301 | return ConvertToByte(dr, i); 302 | } 303 | public static short? ConvertToInt16Nullable(IDataRecord dr, int i) 304 | { 305 | if (dr.IsDBNull(i)) 306 | { 307 | return default; 308 | } 309 | return ConvertToInt16(dr, i); 310 | } 311 | public static int? ConvertToInt32Nullable(IDataRecord dr, int i) 312 | { 313 | if (dr.IsDBNull(i)) 314 | { 315 | return default; 316 | } 317 | return ConvertToInt32(dr, i); 318 | } 319 | public static long? ConvertToInt64Nullable(IDataRecord dr, int i) 320 | { 321 | if (dr.IsDBNull(i)) 322 | { 323 | return default; 324 | } 325 | return ConvertToInt64(dr, i); 326 | } 327 | public static float? ConvertToFloatNullable(IDataRecord dr, int i) 328 | { 329 | if (dr.IsDBNull(i)) 330 | { 331 | return default; 332 | } 333 | return ConvertToFloat(dr, i); 334 | } 335 | public static double? ConvertToDoubleNullable(IDataRecord dr, int i) 336 | { 337 | if (dr.IsDBNull(i)) 338 | { 339 | return default; 340 | } 341 | return ConvertToDouble(dr, i); 342 | } 343 | public static bool? ConvertToBooleanNullable(IDataRecord dr, int i) 344 | { 345 | if (dr.IsDBNull(i)) 346 | { 347 | return default; 348 | } 349 | return ConvertToBoolean(dr, i); 350 | } 351 | public static decimal? ConvertToDecimalNullable(IDataRecord dr, int i) 352 | { 353 | if (dr.IsDBNull(i)) 354 | { 355 | return default; 356 | } 357 | return ConvertToDecimal(dr, i); 358 | } 359 | public static char? ConvertToCharNullable(IDataRecord dr, int i) 360 | { 361 | if (dr.IsDBNull(i)) 362 | { 363 | return default; 364 | } 365 | return ConvertToChar(dr, i); 366 | } 367 | public static DateTime? ConvertToDateTimeNullable(IDataRecord dr, int i) 368 | { 369 | if (dr.IsDBNull(i)) 370 | { 371 | return default; 372 | } 373 | return ConvertToDateTime(dr, i); 374 | } 375 | public static T? ConvertToEnumNullable(IDataRecord dr, int i) where T : struct 376 | { 377 | if (dr.IsDBNull(i)) 378 | { 379 | return default; 380 | } 381 | return ConvertToEnum(dr, i); 382 | } 383 | public static Guid? ConvertToGuidNullable(IDataRecord dr, int i) 384 | { 385 | if (dr.IsDBNull(i)) 386 | { 387 | return default; 388 | } 389 | return ConvertToGuid(dr, i); 390 | } 391 | #endregion 392 | 393 | #region Throw Exception 394 | private static Exception ThrowException(IDataRecord dr, int i) 395 | { 396 | var column = dr.GetName(i); 397 | var fieldType = dr.GetFieldType(i); 398 | return new InvalidCastException($"Unable to cast object of type '{fieldType}' to type '{typeof(T)}' at the column '{column}'."); 399 | } 400 | #endregion 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/DbGridReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace SqlBatis 9 | { 10 | /// 11 | /// DataReader多个结果集 12 | /// 13 | public interface IDbGridReader : IDisposable 14 | { 15 | /// 16 | /// 返回当前dynamic类型结果集 17 | /// 18 | /// 19 | List Read(); 20 | /// 21 | /// 异步返回当前dynamic类型结果集 22 | /// 23 | /// 24 | Task> ReadAsync(); 25 | /// 26 | /// 返回当前T结果集 27 | /// 28 | /// 结果集类型 29 | /// 30 | List Read(); 31 | /// 32 | /// 异步返回当前T类型结果集 33 | /// 34 | /// 35 | /// 36 | Task> ReadAsync(); 37 | /// 38 | /// 返回当前dynamic类型结果 39 | /// 40 | /// 41 | object ReadFirst(); 42 | /// 43 | /// 异步返回当前dynamic类型结果 44 | /// 45 | /// 46 | Task ReadFirstAsync(); 47 | /// 48 | /// 返回当前T类型结果 49 | /// 50 | /// 结果集类型 51 | /// 52 | T ReadFirst(); 53 | /// 54 | /// 异步返回当前T类型结果 55 | /// 56 | /// 结果集类型 57 | /// 58 | Task ReadFirstAsync(); 59 | } 60 | 61 | internal class DbGridReader : IDbGridReader 62 | { 63 | private bool _disposed = false; 64 | private readonly IDataReader _reader = null; 65 | private readonly IDbCommand _command = null; 66 | readonly IDbContextBehavior _behavior; 67 | ~DbGridReader() 68 | { 69 | Dispose(); 70 | } 71 | internal DbGridReader(IDbCommand command, IDbContextBehavior behavior) 72 | { 73 | _command = command; 74 | _behavior = behavior; 75 | _reader = command.ExecuteReader(); 76 | } 77 | 78 | public void Dispose() 79 | { 80 | if (_disposed) 81 | { 82 | return; 83 | } 84 | _disposed = true; 85 | try { _reader?.Close(); } catch { } 86 | try { _reader?.Dispose(); } catch { } 87 | try { _command?.Dispose(); } catch { } 88 | GC.SuppressFinalize(this); 89 | } 90 | 91 | public T ReadFirst() 92 | { 93 | return Read().FirstOrDefault(); 94 | } 95 | 96 | public async Task ReadFirstAsync() 97 | { 98 | return (await ReadAsync()).FirstOrDefault(); 99 | } 100 | 101 | public object ReadFirst() 102 | { 103 | return Read().FirstOrDefault(); 104 | } 105 | 106 | public async Task ReadFirstAsync() 107 | { 108 | return (await ReadAsync()).FirstOrDefault(); 109 | } 110 | 111 | public async Task> ReadAsync() 112 | { 113 | var handler = _behavior.GetDataReaderToDynamicHandler(); 114 | var list = new List(); 115 | while (await (_reader as DbDataReader).ReadAsync()) 116 | { 117 | list.Add(handler(_reader)); 118 | } 119 | NextResult(); 120 | return list; 121 | } 122 | 123 | public List Read() 124 | { 125 | var handler = _behavior.GetDataReaderToDynamicHandler(); 126 | var list = new List(); 127 | while (_reader.Read()) 128 | { 129 | list.Add(handler(_reader)); 130 | } 131 | NextResult(); 132 | return list; 133 | } 134 | 135 | public List Read() 136 | { 137 | var handler = _behavior.GetDataReaderToEntityHandler(_reader); 138 | var list = new List(); 139 | while (_reader.Read()) 140 | { 141 | list.Add(handler(_reader)); 142 | } 143 | NextResult(); 144 | return list; 145 | } 146 | 147 | public async Task> ReadAsync() 148 | { 149 | var handler = _behavior.GetDataReaderToEntityHandler(_reader); 150 | var list = new List(); 151 | while (await (_reader as DbDataReader).ReadAsync()) 152 | { 153 | list.Add(handler(_reader)); 154 | } 155 | NextResult(); 156 | return list; 157 | } 158 | 159 | public void NextResult() 160 | { 161 | if (!_reader.NextResult()) 162 | { 163 | Dispose(); 164 | } 165 | } 166 | } 167 | } 168 | 169 | -------------------------------------------------------------------------------- /src/SqlBatis/DbContexts/SqlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace SqlBatis 8 | { 9 | /// 10 | /// 构建动态sql 11 | /// 12 | public class SqlBuilder 13 | { 14 | private readonly Dictionary _data = new Dictionary(); 15 | 16 | private int _seq; 17 | 18 | private class Clause 19 | { 20 | public string Sql { get; set; } 21 | public bool IsInclusive { get; set; } 22 | } 23 | 24 | private class Clauses : List 25 | { 26 | private readonly string _joiner, _prefix, _postfix; 27 | 28 | public Clauses(string joiner, string prefix = "", string postfix = "") 29 | { 30 | _joiner = joiner; 31 | _prefix = prefix; 32 | _postfix = postfix; 33 | } 34 | 35 | public string ResolveClauses() 36 | { 37 | return this.Any(a => a.IsInclusive) 38 | ? _prefix + 39 | string.Join(_joiner, 40 | this.Where(a => !a.IsInclusive) 41 | .Select(c => c.Sql) 42 | .Union(new[] 43 | { 44 | " ( " + 45 | string.Join(" OR ", this.Where(a => a.IsInclusive).Select(c => c.Sql).ToArray()) + 46 | " ) " 47 | }).ToArray()) + _postfix 48 | : _prefix + string.Join(_joiner, this.Select(c => c.Sql).ToArray()) + _postfix; 49 | } 50 | } 51 | 52 | public class Template 53 | { 54 | private readonly string _sql; 55 | private readonly SqlBuilder _builder; 56 | private int _dataSeq = -1; // Unresolved 57 | 58 | public Template(SqlBuilder builder, string sql) 59 | { 60 | _sql = sql; 61 | _builder = builder; 62 | } 63 | 64 | private static readonly Regex _regex = new Regex(@"\/\*\*.+?\*\*\/", RegexOptions.Compiled | RegexOptions.Multiline); 65 | 66 | private void ResolveSql() 67 | { 68 | if (_dataSeq != _builder._seq) 69 | { 70 | 71 | rawSql = _sql; 72 | 73 | foreach (var pair in _builder._data) 74 | { 75 | rawSql = rawSql.Replace("/**" + pair.Key + "**/", pair.Value.ResolveClauses()); 76 | } 77 | 78 | // replace all that is left with empty 79 | rawSql = _regex.Replace(rawSql, ""); 80 | 81 | _dataSeq = _builder._seq; 82 | } 83 | } 84 | 85 | private string rawSql; 86 | 87 | public string RawSql 88 | { 89 | get { ResolveSql(); return rawSql; } 90 | } 91 | } 92 | 93 | public Template Build(string template) => 94 | new Template(this, template); 95 | 96 | protected SqlBuilder AddClause(string name, string sql, string joiner, string prefix = "", string postfix = "", bool isInclusive = false) 97 | { 98 | if (!_data.TryGetValue(name, out Clauses clauses)) 99 | { 100 | clauses = new Clauses(joiner, prefix, postfix); 101 | _data[name] = clauses; 102 | } 103 | clauses.Add(new Clause { Sql = sql, IsInclusive = isInclusive }); 104 | _seq++; 105 | return this; 106 | } 107 | 108 | public SqlBuilder Intersect(string sql) => 109 | AddClause("intersect", sql, "\nINTERSECT\n ", "\n ", "\n", false); 110 | 111 | public SqlBuilder InnerJoin(string sql) => 112 | AddClause("innerjoin", sql, "\nINNER JOIN ", "\nINNER JOIN ", "\n", false); 113 | 114 | public SqlBuilder LeftJoin(string sql) => 115 | AddClause("leftjoin", sql, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n", false); 116 | 117 | public SqlBuilder RightJoin(string sql) => 118 | AddClause("rightjoin", sql, "\nRIGHT JOIN ", "\nRIGHT JOIN ", "\n", false); 119 | 120 | public SqlBuilder Where(string sql, bool condition = true) 121 | { 122 | if (condition) 123 | { 124 | AddClause("where", sql, " AND ", "WHERE ", "\n", false); 125 | } 126 | return this; 127 | } 128 | 129 | public SqlBuilder OrWhere(string sql, bool condition = true) 130 | { 131 | if (condition) 132 | { 133 | AddClause("where", sql, " OR ", "WHERE ", "\n", true); 134 | } 135 | return this; 136 | } 137 | 138 | public SqlBuilder OrderBy(string sql) => 139 | AddClause("orderby", sql, " , ", "ORDER BY ", "\n", false); 140 | 141 | public SqlBuilder Select(string sql) => 142 | AddClause("select", sql, " , ", "", "\n", false); 143 | 144 | public SqlBuilder Join(string sql) => 145 | AddClause("join", sql, "\nJOIN ", "\nJOIN ", "\n", false); 146 | 147 | public SqlBuilder GroupBy(string sql) => 148 | AddClause("groupby", sql, " , ", "\nGROUP BY ", "\n", false); 149 | 150 | public SqlBuilder Having(string sql, bool condition = true) 151 | { 152 | if (condition) 153 | { 154 | AddClause("having", sql, "\nAND ", "HAVING ", "\n", false); 155 | } 156 | return this; 157 | } 158 | 159 | public SqlBuilder Set(string sql, bool condition = true) 160 | { 161 | if (condition) 162 | { 163 | AddClause("set", sql, " , ", "SET ", "\n", false); 164 | } 165 | return this; 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/SqlBatis/Exceptions/DbUpdateConcurrencyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SqlBatis 8 | { 9 | /// 10 | /// 修改数据时并发冲突 11 | /// 12 | public class DbUpdateConcurrencyException : Exception 13 | { 14 | public DbUpdateConcurrencyException(string message) 15 | : base(message) 16 | { 17 | 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/BooleanExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Text; 7 | using SqlBatis.Attributes; 8 | 9 | namespace SqlBatis.Expressions 10 | { 11 | /// 12 | /// 条件表达式解析 13 | /// 14 | public class BooleanExpressionResovle : ExpressionResovle 15 | { 16 | private readonly string _prefix = "@"; 17 | 18 | private readonly Expression _expression; 19 | 20 | private bool _isNotExpression = false; 21 | 22 | private readonly bool _single; 23 | 24 | private readonly StringBuilder _textBuilder = new StringBuilder(); 25 | 26 | private readonly Dictionary _parameters; 27 | 28 | public BooleanExpressionResovle(bool single, Expression expression, Dictionary parameters) 29 | : base(single) 30 | { 31 | _expression = expression; 32 | _parameters = parameters; 33 | _single = single; 34 | } 35 | 36 | protected override Expression VisitMember(MemberExpression node) 37 | { 38 | if (IsParameterExpression(node)) 39 | { 40 | SetParameterName(node); 41 | } 42 | else 43 | { 44 | SetParameterValue(node); 45 | } 46 | return node; 47 | } 48 | 49 | protected override Expression VisitMethodCall(MethodCallExpression node) 50 | { 51 | if (node.Method.DeclaringType == typeof(Operator)) 52 | { 53 | if (node.Arguments.Count == 2) 54 | { 55 | _textBuilder.Append('('); 56 | Visit(node.Arguments[0]); 57 | _textBuilder.Append($" {Operator.ResovleExpressionType(node.Method.Name)} "); 58 | var value = VisitExpressionValue(node.Arguments[1]); 59 | if (node.Method.Name == nameof(Operator.StartsWith) || node.Method.Name == nameof(Operator.NotStartsWith)) 60 | { 61 | SetParameterValue(Expression.Constant($"{value}%", typeof(string))); 62 | } 63 | else if (node.Method.Name == nameof(Operator.EndsWith) || node.Method.Name == nameof(Operator.NotEndsWith)) 64 | { 65 | SetParameterValue(Expression.Constant($"%{value}", typeof(string))); 66 | } 67 | else if (node.Method.Name == nameof(Operator.Contains) || node.Method.Name == nameof(Operator.NotContains)) 68 | { 69 | SetParameterValue(Expression.Constant($"%{value}%", typeof(string))); 70 | } 71 | else 72 | { 73 | Visit(Expression.Constant(value)); 74 | } 75 | _textBuilder.Append(')'); 76 | } 77 | } 78 | else if (IsLikeExpression(node)) 79 | { 80 | _textBuilder.Append('('); 81 | object value = null; 82 | if (IsParameterExpression(node.Object)) 83 | { 84 | Visit(node.Object); 85 | value = VisitExpressionValue(node.Arguments[0]); 86 | } 87 | else 88 | { 89 | Visit(node.Object); 90 | value = VisitExpressionValue(node.Object); 91 | } 92 | if (_isNotExpression) 93 | { 94 | _isNotExpression = false; 95 | _textBuilder.Append(" NOT LIKE "); 96 | } 97 | else 98 | { 99 | _textBuilder.Append(" LIKE "); 100 | } 101 | if (node.Method.Name == nameof(string.Contains)) 102 | { 103 | SetParameterValue(Expression.Constant($"%{value}%")); 104 | } 105 | else if (node.Method.Name == nameof(string.StartsWith)) 106 | { 107 | SetParameterValue(Expression.Constant($"{value}%")); 108 | } 109 | else 110 | { 111 | SetParameterValue(Expression.Constant($"%{value}")); 112 | } 113 | _textBuilder.Append(')'); 114 | } 115 | else if (IsInExpression(node)) 116 | { 117 | _textBuilder.Append('('); 118 | Expression arguments1 = null; 119 | Expression arguments2 = null; 120 | if (node.Arguments.Count == 1) 121 | { 122 | arguments1 = node.Object; 123 | arguments2 = node.Arguments[0]; 124 | } 125 | else 126 | { 127 | arguments1 = node.Arguments[0]; 128 | arguments2 = node.Arguments[1]; 129 | } 130 | Visit(arguments2); 131 | if (_isNotExpression) 132 | { 133 | _isNotExpression = false; 134 | _textBuilder.Append(" NOT IN "); 135 | } 136 | else 137 | { 138 | _textBuilder.Append(" IN "); 139 | } 140 | Visit(arguments1); 141 | _textBuilder.Append(')'); 142 | } 143 | else if (node.Method.DeclaringType.GetCustomAttribute(typeof(FunctionAttribute), true) != null) 144 | { 145 | var function = new FunctionExpressionResovle(_single, node).Resovle(); 146 | _textBuilder.Append(function); 147 | } 148 | else 149 | { 150 | SetParameterValue(node); 151 | } 152 | return node; 153 | } 154 | 155 | protected override Expression VisitBinary(BinaryExpression node) 156 | { 157 | _textBuilder.Append('('); 158 | Visit(node.Left); 159 | if (node.Right is ConstantExpression right && right.Value == null && (node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual)) 160 | { 161 | _textBuilder.AppendFormat(" {0}", node.NodeType == ExpressionType.Equal ? "IS NULL" : "IS NOT NULL"); 162 | } 163 | else 164 | { 165 | _textBuilder.Append($" {Operator.ResovleExpressionType(node.NodeType)} "); 166 | Visit(node.Right); 167 | } 168 | _textBuilder.Append(')'); 169 | return node; 170 | } 171 | 172 | protected override Expression VisitNew(NewExpression node) 173 | { 174 | SetParameterValue(node); 175 | return node; 176 | } 177 | 178 | protected override Expression VisitUnary(UnaryExpression node) 179 | { 180 | if (node.NodeType == ExpressionType.Not) 181 | { 182 | if (node.Operand is MethodCallExpression methodCallExpression 183 | && (IsInExpression(methodCallExpression) || IsLikeExpression(methodCallExpression))) 184 | { 185 | _isNotExpression = true; 186 | } 187 | else 188 | { 189 | if (node.Type == typeof(int) || node.Type == typeof(int?)) 190 | { 191 | _textBuilder.AppendFormat("{0} ", Operator.ResovleExpressionType("~")); 192 | } 193 | else 194 | { 195 | _textBuilder.AppendFormat("{0} ", Operator.ResovleExpressionType("NOT")); 196 | } 197 | } 198 | Visit(node.Operand); 199 | } 200 | else 201 | { 202 | Visit(node.Operand); 203 | } 204 | return node; 205 | } 206 | 207 | protected override Expression VisitConstant(ConstantExpression node) 208 | { 209 | SetParameterValue(node); 210 | return node; 211 | } 212 | 213 | private void SetParameterName(MemberExpression expression) 214 | { 215 | var name = GetDbColumnNameAsAlias(expression); 216 | _textBuilder.Append(name); 217 | } 218 | 219 | private void SetParameterValue(Expression expression) 220 | { 221 | var value = VisitExpressionValue(expression); 222 | var parameterName = $"P_{_parameters.Count}"; 223 | _parameters.Add(parameterName, value); 224 | _textBuilder.Append($"{_prefix}{parameterName}"); 225 | } 226 | 227 | private static bool IsLikeExpression(MethodCallExpression node) 228 | { 229 | return 230 | node.Arguments.Count == 1 && node.Method.DeclaringType == typeof(string) 231 | && 232 | ( 233 | nameof(string.Contains).Equals(node.Method.Name) 234 | || nameof(string.StartsWith).Equals(node.Method.Name) 235 | || nameof(string.EndsWith).Equals(node.Method.Name) 236 | ); 237 | } 238 | 239 | private static bool IsParameterExpression(Expression expression) 240 | { 241 | return expression is MemberExpression memberExpression && 242 | memberExpression.Expression?.NodeType == ExpressionType.Parameter; 243 | } 244 | 245 | private static bool IsInExpression(MethodCallExpression node) 246 | { 247 | if (typeof(IEnumerable).IsAssignableFrom(node.Method.DeclaringType)) 248 | { 249 | return node.Method.Name == nameof(Enumerable.Contains) && node.Arguments.Count == 1; 250 | } 251 | else 252 | { 253 | return node.Method.Name == nameof(Enumerable.Contains) && node.Arguments.Count == 2; 254 | } 255 | } 256 | 257 | public override string Resovle() 258 | { 259 | Visit(_expression); 260 | return _textBuilder.ToString(); 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/DbExpressions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SqlBatis.Expressions 10 | { 11 | internal enum DbExpressionType 12 | { 13 | Where, 14 | Order, 15 | Group, 16 | Having, 17 | Set, 18 | Ignore 19 | } 20 | internal class DbWhereExpression : DbExpression 21 | { 22 | public DbWhereExpression(Expression expression) 23 | : base(expression, DbExpressionType.Where) 24 | { 25 | 26 | } 27 | } 28 | internal class DbOrderExpression : DbExpression 29 | { 30 | public bool Asc = false; 31 | public DbOrderExpression(Expression expression,bool asc) 32 | : base(expression, DbExpressionType.Order) 33 | { 34 | Asc = asc; 35 | } 36 | } 37 | internal class DbGroupExpression : DbExpression 38 | { 39 | public DbGroupExpression(Expression expression) 40 | : base(expression, DbExpressionType.Group) 41 | { 42 | } 43 | } 44 | internal class DbHavingExpression : DbExpression 45 | { 46 | public DbHavingExpression(Expression expression) 47 | : base(expression, DbExpressionType.Having) 48 | { 49 | } 50 | } 51 | internal class DbSetExpression : DbExpression 52 | { 53 | public readonly Expression Column; 54 | public DbSetExpression(Expression column,Expression expression) 55 | : base(expression, DbExpressionType.Set) 56 | { 57 | Column = column; 58 | } 59 | } 60 | internal class DbIgnoreExpression : DbExpression 61 | { 62 | public DbIgnoreExpression( Expression expression) 63 | : base(expression, DbExpressionType.Ignore) 64 | { 65 | } 66 | } 67 | internal class DbExpression 68 | { 69 | public readonly Expression Expression; 70 | public readonly DbExpressionType ExpressionType; 71 | public DbExpression(Expression expression, DbExpressionType expressionType) 72 | { 73 | Expression = expression; 74 | ExpressionType = expressionType; 75 | } 76 | } 77 | internal class DbExpressionCollection : List 78 | { 79 | public IEnumerable GetWhereExpressions() 80 | { 81 | return this.Where(a => a.ExpressionType == DbExpressionType.Where).Select(s => s as DbWhereExpression); 82 | } 83 | public IEnumerable GetOrderExpressions() 84 | { 85 | return this.Where(a => a.ExpressionType == DbExpressionType.Order).Select(s => s as DbOrderExpression); 86 | } 87 | public IEnumerable GetGroupExpressions() 88 | { 89 | return this.Where(a => a.ExpressionType == DbExpressionType.Group).Select(s => s as DbGroupExpression); 90 | } 91 | public IEnumerable GetHavingExpressions() 92 | { 93 | return this.Where(a => a.ExpressionType == DbExpressionType.Having).Select(s => s as DbHavingExpression); 94 | } 95 | public IEnumerable GetSetExpressions() 96 | { 97 | return this.Where(a => a.ExpressionType == DbExpressionType.Set).Select(s => s as DbSetExpression); 98 | } 99 | public IEnumerable GetIgnoreExpressions() 100 | { 101 | return this.Where(a => a.ExpressionType == DbExpressionType.Ignore).Select(s => s as DbIgnoreExpression); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/ExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace SqlBatis.Expressions 9 | { 10 | /// 11 | /// 数据表达式解析基类 12 | /// 13 | public abstract class ExpressionResovle : ExpressionVisitor 14 | { 15 | /// 16 | /// 是否单表操作 17 | /// 18 | private readonly bool _singleTable; 19 | 20 | /// 21 | /// 表别名 22 | /// 23 | private readonly Dictionary _tableAliasNames 24 | = new Dictionary(); 25 | 26 | /// 27 | /// 数据库表达式解析基类 28 | /// 29 | /// 是否单表操作 30 | protected ExpressionResovle(bool singleTable) 31 | { 32 | _singleTable = singleTable; 33 | } 34 | 35 | /// 36 | /// 解析常量表达式,并返回值 37 | /// 38 | /// 39 | /// 40 | public static object VisitExpressionValue(Expression expression) 41 | { 42 | object value = null; 43 | if (expression is ConstantExpression constant) 44 | value = constant.Value; 45 | else if (expression is MemberExpression) 46 | { 47 | var mxs = new Stack(); 48 | var temp = expression; 49 | while (temp is MemberExpression memberExpression) 50 | { 51 | mxs.Push(memberExpression); 52 | temp = memberExpression.Expression; 53 | } 54 | foreach (var item in mxs) 55 | { 56 | if (item.Expression is ConstantExpression cex) 57 | value = cex.Value; 58 | if (item.Member is PropertyInfo pif) 59 | value = pif.GetValue(value); 60 | else if (item.Member is FieldInfo fif) 61 | value = fif.GetValue(value); 62 | } 63 | } 64 | else 65 | { 66 | value = Expression.Lambda(expression).Compile().DynamicInvoke(); 67 | } 68 | if (!SqlBatisSettings.AllowConstantExpressionResultIsNull && value == null) 69 | { 70 | throw new NullReferenceException($"The result of '{expression}' is not allowed to be null"); 71 | } 72 | return value; 73 | } 74 | 75 | /// 76 | /// 获取数据库字段名转换成别名 77 | /// 78 | /// 79 | /// 80 | protected string GetDbColumnNameAsAlias(MemberExpression expression) 81 | { 82 | var tableType = expression.Member.DeclaringType; 83 | var fieldName = expression.Member.Name; 84 | var columns = SqlBatisSettings.DbMetaInfoProvider.GetColumns(tableType); 85 | var column = columns 86 | .Where(a => a.CsharpName == fieldName) 87 | .FirstOrDefault().ColumnName; 88 | if (_singleTable) 89 | { 90 | return column; 91 | } 92 | var aliasName = (expression.Expression as ParameterExpression).Name; 93 | var tableName = SqlBatisSettings.DbMetaInfoProvider.GetTable(tableType).TableName; 94 | if (!_tableAliasNames.ContainsKey(aliasName)) 95 | { 96 | _tableAliasNames.Add(aliasName, tableName); 97 | } 98 | return $"{aliasName}.{column}"; 99 | } 100 | 101 | public IReadOnlyDictionary GetTableAlias() 102 | { 103 | return _tableAliasNames; 104 | } 105 | /// 106 | /// 解析出一个字符串 107 | /// 108 | /// 109 | public abstract string Resovle(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/FunctionExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Text; 4 | 5 | namespace SqlBatis.Expressions 6 | { 7 | /// 8 | /// 数据库函数解析 9 | /// 10 | public class FunctionExpressionResovle : ExpressionResovle 11 | { 12 | private readonly StringBuilder _textBuilder = new StringBuilder(); 13 | 14 | private readonly Expression _expression; 15 | 16 | public FunctionExpressionResovle(bool single, Expression expression) 17 | : base(single) 18 | { 19 | _expression = expression; 20 | } 21 | 22 | protected override Expression VisitMethodCall(MethodCallExpression node) 23 | { 24 | _textBuilder.Append(node.Method.Name.ToUpper()); 25 | _textBuilder.Append('('); 26 | for (var i = 0; i < node.Arguments.Count; i++) 27 | { 28 | var item = node.Arguments[i]; 29 | Visit(item); 30 | _textBuilder.Append(','); 31 | } 32 | if (_textBuilder[_textBuilder.Length - 1] == ',') 33 | { 34 | _textBuilder.Remove(_textBuilder.Length - 1, 1); 35 | } 36 | _textBuilder.Append(')'); 37 | return node; 38 | } 39 | 40 | protected override Expression VisitConstant(ConstantExpression node) 41 | { 42 | var value = VisitExpressionValue(node); 43 | if (value == null) 44 | { 45 | value = "NULL"; 46 | } 47 | else if (value is string) 48 | { 49 | value = $"'{value}'"; 50 | } 51 | else if (value is bool) 52 | { 53 | value = Convert.ToBoolean(value) ? 1 : 0; 54 | } 55 | _textBuilder.Append($"{value}"); 56 | return node; 57 | } 58 | 59 | protected override Expression VisitMember(MemberExpression node) 60 | { 61 | var name = GetDbColumnNameAsAlias(node); 62 | _textBuilder.Append($"{name}"); 63 | return node; 64 | } 65 | 66 | protected override Expression VisitNewArray(NewArrayExpression node) 67 | { 68 | foreach (var item in node.Expressions) 69 | { 70 | Visit(item); 71 | } 72 | return node; 73 | } 74 | 75 | protected override Expression VisitUnary(UnaryExpression node) 76 | { 77 | if (node.Operand != null) 78 | { 79 | Visit(node.Operand); 80 | } 81 | return node; 82 | } 83 | 84 | public override string Resovle() 85 | { 86 | Visit(_expression); 87 | return _textBuilder.ToString(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/GroupExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | namespace SqlBatis.Expressions 5 | { 6 | /// 7 | /// 分组表达式解析解析 8 | /// 9 | public class GroupExpressionResovle : ExpressionResovle 10 | { 11 | private readonly bool _single; 12 | private readonly Expression _expression; 13 | private readonly List _list = new List(); 14 | 15 | public GroupExpressionResovle(bool isSingleTable, Expression expression) 16 | : base(isSingleTable) 17 | { 18 | _single = isSingleTable; 19 | _expression = expression; 20 | } 21 | 22 | protected override Expression VisitNew(NewExpression node) 23 | { 24 | foreach (var item in node.Arguments) 25 | { 26 | Visit(item); 27 | } 28 | return node; 29 | } 30 | 31 | protected override Expression VisitMember(MemberExpression node) 32 | { 33 | var name = GetDbColumnNameAsAlias(node); 34 | _list.Add(name); 35 | return node; 36 | } 37 | 38 | protected override Expression VisitMethodCall(MethodCallExpression node) 39 | { 40 | var result = new FunctionExpressionResovle(_single,node).Resovle(); 41 | _list.Add(result); 42 | return node; 43 | } 44 | 45 | public override string Resovle() 46 | { 47 | Visit(_expression); 48 | return string.Join(",",_list); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/IgnoreExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace SqlBatis.Expressions 6 | { 7 | public class IgnoreExpressionResovle : ExpressionResovle 8 | { 9 | private readonly List _list = new List(); 10 | 11 | private readonly Expression _expression; 12 | 13 | public IgnoreExpressionResovle(Expression expression) 14 | : base(true) 15 | { 16 | _expression = expression; 17 | } 18 | 19 | protected override Expression VisitNew(NewExpression node) 20 | { 21 | foreach (var item in node.Arguments) 22 | { 23 | Visit(item); 24 | } 25 | return node; 26 | } 27 | 28 | protected override Expression VisitMember(MemberExpression node) 29 | { 30 | var name = GetDbColumnNameAsAlias(node); 31 | _list.Add(name); 32 | return node; 33 | } 34 | 35 | public List Resovles() 36 | { 37 | Visit(_expression); 38 | return _list; 39 | } 40 | 41 | public override string Resovle() 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/OrderExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | namespace SqlBatis.Expressions 5 | { 6 | /// 7 | /// 排序表达式解析 8 | /// 9 | public class OrderExpressionResovle : ExpressionResovle 10 | { 11 | private readonly List _list = new List(); 12 | 13 | private readonly string _asc = string.Empty; 14 | 15 | private readonly bool _single; 16 | 17 | private readonly Expression _expression; 18 | 19 | public OrderExpressionResovle(bool single, Expression expression, bool asc) 20 | : base(single) 21 | { 22 | _expression = expression; 23 | _single = single; 24 | if (!asc) 25 | { 26 | _asc = " DESC"; 27 | } 28 | } 29 | 30 | protected override Expression VisitNew(NewExpression node) 31 | { 32 | foreach (var item in node.Arguments) 33 | { 34 | Visit(item); 35 | } 36 | return node; 37 | } 38 | 39 | protected override Expression VisitMethodCall(MethodCallExpression node) 40 | { 41 | var result = new FunctionExpressionResovle(_single, node).Resovle(); 42 | _list.Add($"{result}{_asc}"); 43 | return node; 44 | } 45 | 46 | protected override Expression VisitMember(MemberExpression node) 47 | { 48 | var name = GetDbColumnNameAsAlias(node); 49 | _list.Add($"{name}{_asc}"); 50 | return node; 51 | } 52 | 53 | public override string Resovle() 54 | { 55 | Visit(_expression); 56 | return string.Join(",", _list); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/SqlBatis/Expressions/SelectExpressionResovle.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | namespace SqlBatis.Expressions 5 | { 6 | public class SelectExpressionResovle : ExpressionResovle 7 | { 8 | private readonly bool _single; 9 | 10 | private readonly List _list = new List(); 11 | 12 | private readonly Expression _expression; 13 | 14 | public SelectExpressionResovle(bool single, Expression expression) 15 | : base(single) 16 | { 17 | _single = single; 18 | _expression = expression; 19 | } 20 | 21 | protected override Expression VisitMemberInit(MemberInitExpression node) 22 | { 23 | for (int i = 0; i < node.Bindings.Count; i++) 24 | { 25 | var item = node.Bindings[i] as MemberAssignment; 26 | 27 | if (item.Expression is MemberExpression mExp) 28 | { 29 | var name = GetDbColumnNameAsAlias(mExp); 30 | _list.Add($"{name} AS {item.Member.Name}"); 31 | } 32 | else if (item.Expression is MethodCallExpression) 33 | { 34 | var expression = new FunctionExpressionResovle(_single, item.Expression).Resovle(); 35 | _list.Add($"{expression} AS {item.Member.Name}"); 36 | } 37 | } 38 | return node; 39 | } 40 | 41 | protected override Expression VisitNew(NewExpression node) 42 | { 43 | for (int i = 0; i < node.Arguments.Count; i++) 44 | { 45 | var item = node.Arguments[i]; 46 | var column = node.Members[i].Name; 47 | if (item is MemberExpression member) 48 | { 49 | var name = GetDbColumnNameAsAlias(member); 50 | if (name != column) 51 | { 52 | _list.Add($"{name} AS {column}"); 53 | } 54 | else 55 | { 56 | _list.Add(name); 57 | } 58 | } 59 | else if (item is MethodCallExpression) 60 | { 61 | var expression = new FunctionExpressionResovle(_single, item).Resovle(); 62 | _list.Add($"{expression} AS {column}"); 63 | } 64 | } 65 | return node; 66 | } 67 | 68 | protected override Expression VisitMember(MemberExpression node) 69 | { 70 | var name = GetDbColumnNameAsAlias(node); 71 | _list.Add(name); 72 | return node; 73 | } 74 | 75 | protected override Expression VisitMethodCall(MethodCallExpression node) 76 | { 77 | var result = new FunctionExpressionResovle(_single, node).Resovle(); 78 | _list.Add($"{result} AS expr"); 79 | return node; 80 | } 81 | public override string Resovle() 82 | { 83 | if (_expression is ConstantExpression constantExpression) 84 | { 85 | return constantExpression.Value.ToString(); 86 | } 87 | Visit(_expression); 88 | return string.Join(",", _list); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/SqlBatis/Providers/DbMetaInfoProvider.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Attributes; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace SqlBatis 8 | { 9 | /// 10 | /// 数据库元信息提供程序 11 | /// 12 | public interface IDbMetaInfoProvider 13 | { 14 | /// 15 | /// 获取表的元信息 16 | /// 17 | /// 18 | /// 19 | DbTableMetaInfo GetTable(Type type); 20 | /// 21 | /// 获取字段的元信息 22 | /// 23 | /// 24 | /// 25 | IReadOnlyList GetColumns(Type type); 26 | } 27 | 28 | /// 29 | /// 注解方案数据库元信息 30 | /// 31 | public class AnnotationDbMetaInfoProvider: IDbMetaInfoProvider 32 | { 33 | private static readonly ConcurrentDictionary _tables 34 | = new ConcurrentDictionary(); 35 | 36 | private static readonly ConcurrentDictionary> _columns 37 | = new ConcurrentDictionary>(); 38 | 39 | public DbTableMetaInfo GetTable(Type type) 40 | { 41 | return _tables.GetOrAdd(type, t => 42 | { 43 | var name = t.Name; 44 | if (t.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault() != null) 45 | { 46 | var attribute = t.GetCustomAttributes(typeof(TableAttribute), true) 47 | .FirstOrDefault() as TableAttribute; 48 | name = attribute.Name; 49 | } 50 | var table = new DbTableMetaInfo() 51 | { 52 | TableName = name, 53 | CsharpName = t.Name 54 | }; 55 | return table; 56 | }); 57 | } 58 | 59 | public IReadOnlyList GetColumns(Type type) 60 | { 61 | return _columns.GetOrAdd(type, t => 62 | { 63 | var list = new List(); 64 | var properties = type.GetProperties(); 65 | foreach (var item in properties) 66 | { 67 | var columnName = item.Name; 68 | var isPrimaryKey = false; 69 | var isDefault = false; 70 | var isIdentity = false; 71 | var isNotMapped = false; 72 | var isConcurrencyCheck = false; 73 | var isComplexType = false; 74 | if (item.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault() != null) 75 | { 76 | var attribute = item.GetCustomAttributes(typeof(ColumnAttribute), true) 77 | .FirstOrDefault() as ColumnAttribute; 78 | columnName = attribute.Name; 79 | } 80 | if (item.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).FirstOrDefault() != null) 81 | { 82 | isPrimaryKey = true; 83 | } 84 | if (item.GetCustomAttributes(typeof(IdentityAttribute), true).FirstOrDefault() != null) 85 | { 86 | isIdentity = true; 87 | } 88 | if (item.GetCustomAttributes(typeof(DefaultAttribute), true).FirstOrDefault() != null) 89 | { 90 | isDefault = true; 91 | } 92 | if (item.GetCustomAttributes(typeof(ConcurrencyCheckAttribute), true).FirstOrDefault() != null) 93 | { 94 | isConcurrencyCheck = true; 95 | } 96 | if (item.GetCustomAttributes(typeof(NotMappedAttribute), true).FirstOrDefault() != null) 97 | { 98 | isNotMapped = true; 99 | } 100 | if (item.GetCustomAttributes(typeof(ComplexTypeAttribute), true).FirstOrDefault() != null) 101 | { 102 | isComplexType = true; 103 | } 104 | list.Add(new DbColumnMetaInfo() 105 | { 106 | CsharpType = item.PropertyType, 107 | IsDefault = isDefault, 108 | ColumnName = columnName, 109 | CsharpName = item.Name, 110 | IsPrimaryKey = isPrimaryKey, 111 | IsIdentity = isIdentity, 112 | IsNotMapped = isNotMapped, 113 | IsConcurrencyCheck = isConcurrencyCheck, 114 | IsComplexType = isComplexType 115 | }); 116 | } 117 | return list.AsReadOnly(); 118 | }); 119 | } 120 | } 121 | 122 | /// 123 | /// 表信息 124 | /// 125 | public class DbTableMetaInfo 126 | { 127 | /// 128 | /// 数据库表名称 129 | /// 130 | public string TableName { get; set; } 131 | /// 132 | /// Csharp表名称 133 | /// 134 | public string CsharpName { get; set; } 135 | } 136 | 137 | /// 138 | /// 字段信息 139 | /// 140 | public class DbColumnMetaInfo 141 | { 142 | /// 143 | /// 是否并发检查列 144 | /// 145 | public bool IsConcurrencyCheck { get; set; } 146 | /// 147 | /// 是否默认值约束 148 | /// 149 | public bool IsDefault { get; set; } 150 | /// 151 | /// 是否是数据库字段 152 | /// 153 | public bool IsNotMapped { get; set; } 154 | /// 155 | /// 数据库字段名 156 | /// 157 | public string ColumnName { get; set; } 158 | /// 159 | /// Csharp字段名 160 | /// 161 | public string CsharpName { get; set; } 162 | /// 163 | /// Csharp类型 164 | /// 165 | public Type CsharpType { get; set; } 166 | /// 167 | /// 是否主键约束 168 | /// 169 | public bool IsPrimaryKey { get; set; } 170 | /// 171 | /// 是否是自增列 172 | /// 173 | public bool IsIdentity { get; set; } 174 | /// 175 | /// 是否为计算列 176 | /// 177 | public bool IsComplexType { get; set; } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/DbQueryable.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Expressions; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Text; 5 | 6 | namespace SqlBatis.Queryables 7 | { 8 | public abstract class DbQueryable 9 | { 10 | #region fields 11 | protected readonly Dictionary _parameters = new Dictionary(); 12 | protected readonly bool _isSingleTable = true; 13 | private string _viewName = string.Empty; 14 | private string _lockname = string.Empty; 15 | private readonly PageData _page = new PageData(); 16 | protected readonly IDbContext _context; 17 | internal readonly DbExpressionCollection _expressions = new DbExpressionCollection(); 18 | public DbQueryable(IDbContext context, bool isSingleTable) 19 | { 20 | _context = context; 21 | _isSingleTable = isSingleTable; 22 | } 23 | #endregion 24 | 25 | #region resovles 26 | protected string BuildWhereExpression() 27 | { 28 | var builder = new StringBuilder(); 29 | var expressions = _expressions.GetWhereExpressions(); 30 | var first = true; 31 | foreach (var expression in expressions) 32 | { 33 | var result = new BooleanExpressionResovle(_isSingleTable, expression.Expression, _parameters).Resovle(); 34 | if (first) 35 | { 36 | first = false; 37 | builder.Append($" WHERE {result}"); 38 | } 39 | else 40 | { 41 | builder.Append($" AND {result}"); 42 | } 43 | } 44 | return builder.ToString(); 45 | } 46 | protected string BuildGroupExpression() 47 | { 48 | var buffer = new StringBuilder(); 49 | var expressions = _expressions.GetGroupExpressions(); 50 | foreach (var item in expressions) 51 | { 52 | var result = new GroupExpressionResovle(_isSingleTable, item.Expression).Resovle(); 53 | buffer.Append($"{result},"); 54 | } 55 | var sql = string.Empty; 56 | if (buffer.Length > 0) 57 | { 58 | buffer.Remove(buffer.Length - 1, 1); 59 | sql = $" GROUP BY {buffer}"; 60 | } 61 | return sql; 62 | } 63 | protected string BuildHavingExpression() 64 | { 65 | var buffer = new StringBuilder(); 66 | var expressions = _expressions.GetHavingExpressions(); 67 | var first = true; 68 | foreach (var item in expressions) 69 | { 70 | var result = new BooleanExpressionResovle(_isSingleTable, item.Expression, _parameters).Resovle(); 71 | if (first) 72 | { 73 | first = false; 74 | buffer.Append($" HAVING {result}"); 75 | } 76 | else 77 | { 78 | buffer.Append($" AND {result}"); 79 | } 80 | } 81 | return buffer.ToString(); 82 | } 83 | protected string BuildOrderExpression() 84 | { 85 | var buffer = new StringBuilder(); 86 | var expressions = _expressions.GetOrderExpressions(); 87 | var first = true; 88 | foreach (var item in expressions) 89 | { 90 | if (first) 91 | { 92 | first = false; 93 | buffer.Append($" ORDER BY "); 94 | } 95 | var result = new OrderExpressionResovle(_isSingleTable, item.Expression, item.Asc).Resovle(); 96 | buffer.Append(result); 97 | buffer.Append(','); 98 | } 99 | return buffer.ToString().Trim(','); 100 | } 101 | protected List BuildIgnoreExpression() 102 | { 103 | var result = new List(); 104 | var expressions = _expressions.GetIgnoreExpressions(); 105 | foreach (var item in expressions) 106 | { 107 | var list = new IgnoreExpressionResovle(item.Expression).Resovles(); 108 | result.AddRange(list); 109 | } 110 | return result; 111 | } 112 | protected string BuildCountCommand(Expression expression = null) 113 | { 114 | var table = _viewName.ToString(); 115 | var column = "COUNT(1)"; 116 | var where = BuildWhereExpression(); 117 | var group = BuildGroupExpression(); 118 | if (group.Length > 0) 119 | { 120 | column = group.Remove(0, 10); 121 | } 122 | else if (expression != null) 123 | { 124 | column = new SelectExpressionResovle(_isSingleTable, expression).Resovle(); 125 | } 126 | var sql = $"SELECT {column} FROM {table}{where}{group}"; 127 | if (group.Length > 0) 128 | { 129 | sql = $"SELECT COUNT(1) FROM ({sql}) as t"; 130 | return sql; 131 | } 132 | return sql; 133 | } 134 | protected string BuildSelectCommand(Expression expression) 135 | { 136 | var table = _viewName.ToString(); 137 | var column = new SelectExpressionResovle(_isSingleTable, expression).Resovle(); 138 | var where = BuildWhereExpression(); 139 | var group = BuildGroupExpression(); 140 | var having = BuildHavingExpression(); 141 | var orderBy = BuildOrderExpression(); 142 | string sql; 143 | if (_context.DbContextType == DbContextType.SqlServer2008 || _context.DbContextType == DbContextType.SqlServer2012) 144 | { 145 | if (_lockname != string.Empty) 146 | { 147 | _lockname = $" WITH({_lockname})"; 148 | } 149 | //第一页 150 | if (_page.Index == 0) 151 | { 152 | sql = $"SELECT TOP {_page.Count} {column} FROM {table}{_lockname}{where}{group}{having}{orderBy}"; 153 | } 154 | else if (_page.Index > 0)//大于一页 155 | { 156 | if (orderBy == string.Empty)//如果未指定排序 157 | { 158 | orderBy = " ORDER BY (SELECT 1)"; 159 | } 160 | if (_context.DbContextType == DbContextType.SqlServer2008) 161 | { 162 | var rownumber = $"ROW_NUMBER() OVER ({orderBy}) AS RowNumber"; 163 | var offset = $"WHERE RowNumber > {_page.Index}"; 164 | sql = $"SELECT TOP {_page.Count} * FROM (SELECT {column},{rownumber} FROM {_lockname}{table}{where}{group}{having}) AS t {offset}"; 165 | } 166 | else 167 | { 168 | var offset = $" OFFSET {_page.Index} ROWS FETCH NEXT {_page.Count} ROWS ONLY"; 169 | sql = $"SELECT {column} FROM {_lockname}{table}{where}{group}{having}{orderBy}{offset}"; 170 | } 171 | } 172 | else//不分页 173 | { 174 | sql = $"SELECT {column} FROM {_lockname}{table}{where}{group}{having}{orderBy}"; 175 | } 176 | } 177 | else 178 | { 179 | var offset = _page.Index > 0 || _page.Count > 0 ? $" LIMIT {_page.Index},{_page.Count}" : string.Empty; 180 | sql = $"SELECT {column} FROM {table}{where}{group}{having}{orderBy}{offset}{_lockname}"; 181 | } 182 | return sql; 183 | } 184 | protected string BuildExistsCommand() 185 | { 186 | var table = _viewName.ToString(); 187 | var where = BuildWhereExpression(); 188 | var group = BuildGroupExpression(); 189 | var having = BuildHavingExpression(); 190 | if (_context.DbContextType != DbContextType.Mysql) 191 | { 192 | return $"SELECT 1 WHERE EXISTS(SELECT 1 FROM {table}{where}{group}{having})"; 193 | } 194 | else 195 | { 196 | return $"SELECT EXISTS(SELECT 1 FROM {table}{where}{group}{having}) as flag"; 197 | } 198 | } 199 | #endregion 200 | 201 | #region protected 202 | protected void SetPage(int index, int count) 203 | { 204 | _page.Index = index; 205 | _page.Count = count; 206 | } 207 | protected void AppendViewName(string viewName) 208 | { 209 | if (_viewName.Length > 0) 210 | { 211 | _viewName += $" {viewName}"; 212 | } 213 | else 214 | { 215 | _viewName = viewName; 216 | } 217 | } 218 | protected void SetLockName(string lockname) 219 | { 220 | _lockname = lockname; 221 | } 222 | protected string GetViewName() 223 | { 224 | return _viewName; 225 | } 226 | protected void AddWhereExpression(Expression expression) 227 | { 228 | _expressions.Add(new DbWhereExpression(expression)); 229 | } 230 | protected void AddOrderExpression(Expression expression, bool asc) 231 | { 232 | _expressions.Add(new DbOrderExpression(expression, asc)); 233 | } 234 | protected void AddGroupExpression(Expression expression) 235 | { 236 | _expressions.Add(new DbGroupExpression(expression)); 237 | } 238 | protected void AddHavingExpression(Expression expression) 239 | { 240 | _expressions.Add(new DbHavingExpression(expression)); 241 | } 242 | protected void AddIgnoreExpression(Expression expression) 243 | { 244 | _expressions.Add(new DbIgnoreExpression(expression)); 245 | } 246 | #endregion 247 | 248 | #region class 249 | class PageData 250 | { 251 | public int Index { get; set; } = -1; 252 | public int Count { get; set; } 253 | } 254 | #endregion 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/DbQueryable``.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Expressions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace SqlBatis.Queryables 9 | { 10 | public class DbQueryable : DbQueryable, IDbQueryable 11 | { 12 | #region fields 13 | public DbQueryable(IDbContext context) 14 | : base(context, false) 15 | { 16 | 17 | } 18 | #endregion 19 | 20 | public int Count(int? commandTimeout = null) 21 | { 22 | var sql = BuildCountCommand(); 23 | return _context.ExecuteScalar(sql, _parameters, commandTimeout); 24 | } 25 | 26 | public int Count(Expression> expression) 27 | { 28 | var sql = BuildCountCommand(expression); 29 | return _context.ExecuteScalar(sql, _parameters); 30 | } 31 | 32 | public Task CountAsync(int? commandTimeout = null) 33 | { 34 | var sql = BuildCountCommand(); 35 | return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); 36 | } 37 | 38 | public Task CountAsync(Expression> expression) 39 | { 40 | var sql = BuildCountCommand(expression); 41 | return _context.ExecuteScalarAsync(sql, _parameters); 42 | } 43 | 44 | public IDbQueryable GroupBy(Expression> expression) 45 | { 46 | AddGroupExpression(expression); 47 | return this; 48 | } 49 | 50 | public IDbQueryable Having(Expression> expression, bool condition = true) 51 | { 52 | if (condition) 53 | { 54 | AddHavingExpression(expression); 55 | } 56 | return this; 57 | } 58 | 59 | public IDbQueryable OrderBy(Expression> expression, bool condition = true) 60 | { 61 | if (condition) 62 | { 63 | AddOrderExpression(expression, true); 64 | } 65 | return this; 66 | } 67 | 68 | public IDbQueryable OrderByDescending(Expression> expression, bool condition = true) 69 | { 70 | if (condition) 71 | { 72 | AddOrderExpression(expression, false); 73 | } 74 | return this; 75 | } 76 | 77 | public IDbQueryable Page(int index, int count, bool condition = true) 78 | { 79 | if (condition) 80 | { 81 | Skip((index - 1) * count, count); 82 | } 83 | return this; 84 | } 85 | 86 | public IEnumerable Select(Expression> expression, int? commandTimeout = null) 87 | { 88 | var sql = BuildSelectCommand(expression); 89 | return _context.Query(sql, _parameters, commandTimeout); 90 | } 91 | 92 | public Task> SelectAsync(Expression> expression, int? commandTimeout = null) 93 | { 94 | var sql = BuildSelectCommand(expression); 95 | return _context.QueryAsync(sql, _parameters, commandTimeout); 96 | } 97 | 98 | public (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null) 99 | { 100 | var sql1 = BuildSelectCommand(expression); 101 | var sql2 = BuildCountCommand(); 102 | using (var multi = _context.QueryMultiple($"{sql1};{sql2}", _parameters, commandTimeout)) 103 | { 104 | var list = multi.Read(); 105 | var count = multi.ReadFirst(); 106 | return (list, count); 107 | } 108 | } 109 | 110 | public async Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null) 111 | { 112 | var sql1 = BuildSelectCommand(expression); 113 | var sql2 = BuildCountCommand(); 114 | using (var multi = _context.QueryMultiple($"{sql1};{sql2}", _parameters, commandTimeout)) 115 | { 116 | var list = await multi.ReadAsync(); 117 | var count = await multi.ReadFirstAsync(); 118 | return (list, count); 119 | } 120 | } 121 | 122 | public TResult Single(Expression> expression, int? commandTimeout = null) 123 | { 124 | Take(1); 125 | return Select(expression, commandTimeout).FirstOrDefault(); 126 | } 127 | 128 | public async Task SingleAsync(Expression> expression, int? commandTimeout = null) 129 | { 130 | Take(1); 131 | return (await SelectAsync(expression, commandTimeout)).FirstOrDefault(); 132 | } 133 | 134 | public IDbQueryable Skip(int index, int count, bool condition = true) 135 | { 136 | if (condition) 137 | { 138 | SetPage(index, count); 139 | } 140 | return this; 141 | } 142 | 143 | public IDbQueryable Take(int count, bool condition = true) 144 | { 145 | if (condition) 146 | { 147 | Skip(0, count); 148 | } 149 | return this; 150 | } 151 | 152 | public IDbQueryable Where(Expression> expression, bool condition = true) 153 | { 154 | if (condition) 155 | { 156 | AddWhereExpression(expression); 157 | } 158 | return this; 159 | } 160 | 161 | public IDbQueryable With(string lockname) 162 | { 163 | SetLockName($" {lockname}"); 164 | return this; 165 | } 166 | 167 | private IDbQueryable Join(Expression> expression, string joinType) 168 | { 169 | var resovle = new BooleanExpressionResovle(_isSingleTable, expression, _parameters); 170 | var onExpression = resovle.Resovle(); 171 | var alias = resovle.GetTableAlias(); 172 | joinType = string.Format(" {0} JOIN ", joinType); 173 | var joinExpress = string.Join(joinType, alias.Select(s => $"{s.Value} AS {s.Key}")); 174 | AppendViewName(string.Format("{0} ON {1}", joinExpress, onExpression)); 175 | return this; 176 | } 177 | 178 | public IDbQueryable Join(Expression> expression) 179 | { 180 | Join(expression, "INNER"); 181 | return this; 182 | } 183 | 184 | public IDbQueryable LeftJoin(Expression> expression) 185 | { 186 | Join(expression, "LEFT"); 187 | return this; 188 | } 189 | 190 | public IDbQueryable RightJoin(Expression> expression) 191 | { 192 | Join(expression, "RIGHT"); 193 | return this; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/DbQueryable```.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Expressions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace SqlBatis.Queryables 9 | { 10 | public class DbQueryable : DbQueryable, IDbQueryable 11 | { 12 | #region fields 13 | private readonly List _tables = new List(); 14 | public DbQueryable(IDbContext context) 15 | : base(context, false) 16 | { 17 | 18 | } 19 | #endregion 20 | 21 | public int Count(int? commandTimeout = null) 22 | { 23 | var sql = BuildCountCommand(); 24 | return _context.ExecuteScalar(sql, _parameters, commandTimeout); 25 | } 26 | 27 | public int Count(Expression> expression) 28 | { 29 | var sql = BuildCountCommand(expression); 30 | return _context.ExecuteScalar(sql, _parameters); 31 | } 32 | 33 | public Task CountAsync(int? commandTimeout = null) 34 | { 35 | var sql = BuildCountCommand(); 36 | return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); 37 | } 38 | 39 | public Task CountAsync(Expression> expression) 40 | { 41 | var sql = BuildCountCommand(expression); 42 | return _context.ExecuteScalarAsync(sql, _parameters); 43 | } 44 | 45 | 46 | public IDbQueryable GroupBy(Expression> expression) 47 | { 48 | AddGroupExpression(expression); 49 | return this; 50 | } 51 | 52 | public IDbQueryable Having(Expression> expression, bool condition = true) 53 | { 54 | if (condition) 55 | { 56 | AddHavingExpression(expression); 57 | } 58 | return this; 59 | } 60 | 61 | public IDbQueryable OrderBy(Expression> expression, bool condition = true) 62 | { 63 | if (condition) 64 | { 65 | AddOrderExpression(expression, true); 66 | } 67 | return this; 68 | } 69 | 70 | public IDbQueryable OrderByDescending(Expression> expression, bool condition = true) 71 | { 72 | if (condition) 73 | { 74 | AddOrderExpression(expression, false); 75 | } 76 | return this; 77 | } 78 | 79 | public IDbQueryable Page(int index, int count, bool condition = true) 80 | { 81 | if (condition) 82 | { 83 | Skip((index - 1) * count, count); 84 | } 85 | return this; 86 | } 87 | 88 | public IEnumerable Select(Expression> expression, int? commandTimeout = null) 89 | { 90 | var sql = BuildSelectCommand(expression); 91 | return _context.Query(sql, _parameters, commandTimeout); 92 | } 93 | 94 | public Task> SelectAsync(Expression> expression, int? commandTimeout = null) 95 | { 96 | var sql = BuildSelectCommand(expression); 97 | return _context.QueryAsync(sql, _parameters, commandTimeout); 98 | } 99 | 100 | public (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null) 101 | { 102 | var sql1 = BuildSelectCommand(expression); 103 | var sql2 = BuildCountCommand(); 104 | using (var multi = _context.QueryMultiple($"{sql1};{sql2}", _parameters, commandTimeout)) 105 | { 106 | var list = multi.Read(); 107 | var count = multi.ReadFirst(); 108 | return (list, count); 109 | } 110 | } 111 | 112 | public async Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null) 113 | { 114 | var sql1 = BuildSelectCommand(expression); 115 | var sql2 = BuildCountCommand(); 116 | using (var multi = _context.QueryMultiple($"{sql1};{sql2}", _parameters, commandTimeout)) 117 | { 118 | var list = await multi.ReadAsync(); 119 | var count = await multi.ReadFirstAsync(); 120 | return (list, count); 121 | } 122 | } 123 | 124 | public TResult Single(Expression> expression, int? commandTimeout = null) 125 | { 126 | Take(1); 127 | return Select(expression, commandTimeout).FirstOrDefault(); 128 | } 129 | 130 | public async Task SingleAsync(Expression> expression, int? commandTimeout = null) 131 | { 132 | Take(1); 133 | return (await SelectAsync(expression, commandTimeout)).FirstOrDefault(); 134 | } 135 | 136 | public IDbQueryable Skip(int index, int count, bool condition = true) 137 | { 138 | if (condition) 139 | { 140 | SetPage(index, count); 141 | } 142 | return this; 143 | } 144 | 145 | public IDbQueryable Take(int count, bool condition = true) 146 | { 147 | if (condition) 148 | { 149 | Skip(0, count); 150 | } 151 | return this; 152 | } 153 | 154 | public IDbQueryable Where(Expression> expression, bool condition = true) 155 | { 156 | if (condition) 157 | { 158 | AddWhereExpression(expression); 159 | } 160 | return this; 161 | } 162 | 163 | public IDbQueryable With(string lockname) 164 | { 165 | SetLockName($" {lockname}"); 166 | return this; 167 | } 168 | 169 | private IDbQueryable JoinFormat(Expression> expression, string joinType) 170 | { 171 | var resovle = new BooleanExpressionResovle(_isSingleTable, expression, _parameters); 172 | var onExpression = resovle.Resovle(); 173 | var alias = resovle.GetTableAlias(); 174 | if (_tables.Count == 0) 175 | { 176 | joinType = string.Format(" {0} JOIN ", joinType); 177 | _tables.AddRange(alias.Select(s => s.Key)); 178 | var viewName = string.Join(joinType, alias.Select(s => $"{s.Value} AS {s.Key}")); 179 | AppendViewName(string.Format("{0} ON {1}", viewName, onExpression)); 180 | } 181 | else 182 | { 183 | var alia = alias.Where(a => !_tables.Contains(a.Key)).First(); 184 | joinType = string.Format("{0} JOIN ", joinType); 185 | var viewName = $"{joinType}{alia.Value} AS {alia.Key}"; 186 | AppendViewName(string.Format("{0} ON {1}", viewName, onExpression)); 187 | } 188 | return this; 189 | } 190 | public IDbQueryable Join(Expression> expression) 191 | { 192 | JoinFormat(expression, "INNER"); 193 | return this; 194 | } 195 | 196 | public IDbQueryable LeftJoin(Expression> expression) 197 | { 198 | JoinFormat(expression, "LEFT"); 199 | return this; 200 | } 201 | 202 | public IDbQueryable RightJoin(Expression> expression) 203 | { 204 | JoinFormat(expression, "RIGHT"); 205 | return this; 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/DbQueryable````.cs: -------------------------------------------------------------------------------- 1 | using SqlBatis.Expressions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace SqlBatis.Queryables 9 | { 10 | public class DbQueryable : DbQueryable, IDbQueryable 11 | { 12 | #region fields 13 | private readonly List _tables = new List(); 14 | public DbQueryable(IDbContext context) 15 | : base(context, false) 16 | { 17 | 18 | } 19 | #endregion 20 | 21 | public int Count(int? commandTimeout = null) 22 | { 23 | var sql = BuildCountCommand(); 24 | return _context.ExecuteScalar(sql, _parameters, commandTimeout); 25 | } 26 | 27 | public int Count(Expression> expression) 28 | { 29 | var sql = BuildCountCommand(expression); 30 | return _context.ExecuteScalar(sql, _parameters); 31 | } 32 | 33 | public Task CountAsync(int? commandTimeout = null) 34 | { 35 | var sql = BuildCountCommand(); 36 | return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); 37 | } 38 | 39 | public Task CountAsync(Expression> expression) 40 | { 41 | var sql = BuildCountCommand(expression); 42 | return _context.ExecuteScalarAsync(sql, _parameters); 43 | } 44 | 45 | 46 | public IDbQueryable GroupBy(Expression> expression) 47 | { 48 | AddGroupExpression(expression); 49 | return this; 50 | } 51 | 52 | public IDbQueryable Having(Expression> expression, bool condition = true) 53 | { 54 | if (condition) 55 | { 56 | AddHavingExpression(expression); 57 | } 58 | return this; 59 | } 60 | 61 | public IDbQueryable OrderBy(Expression> expression, bool condition = true) 62 | { 63 | if (condition) 64 | { 65 | AddOrderExpression(expression, true); 66 | } 67 | return this; 68 | } 69 | 70 | public IDbQueryable OrderByDescending(Expression> expression, bool condition = true) 71 | { 72 | if (condition) 73 | { 74 | AddOrderExpression(expression, false); 75 | } 76 | return this; 77 | } 78 | 79 | public IDbQueryable Page(int index, int count, bool condition = true) 80 | { 81 | if (condition) 82 | { 83 | Skip((index - 1) * count, count); 84 | } 85 | return this; 86 | } 87 | 88 | public IEnumerable Select(Expression> expression, int? commandTimeout = null) 89 | { 90 | var sql = BuildSelectCommand(expression); 91 | return _context.Query(sql, _parameters, commandTimeout); 92 | } 93 | 94 | public Task> SelectAsync(Expression> expression, int? commandTimeout = null) 95 | { 96 | var sql = BuildSelectCommand(expression); 97 | return _context.QueryAsync(sql, _parameters, commandTimeout); 98 | } 99 | 100 | public (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null) 101 | { 102 | var sql1 = BuildSelectCommand(expression); 103 | var sql2 = BuildCountCommand(); 104 | using (var multi = _context.QueryMultiple($"{sql1};{sql2}", _parameters, commandTimeout)) 105 | { 106 | var list = multi.Read(); 107 | var count = multi.ReadFirst(); 108 | return (list, count); 109 | } 110 | } 111 | 112 | public async Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null) 113 | { 114 | var sql1 = BuildSelectCommand(expression); 115 | var sql2 = BuildCountCommand(); 116 | using (var multi = _context.QueryMultiple($"{sql1};{sql2}", _parameters, commandTimeout)) 117 | { 118 | var list = await multi.ReadAsync(); 119 | var count = await multi.ReadFirstAsync(); 120 | return (list, count); 121 | } 122 | } 123 | 124 | public TResult Single(Expression> expression, int? commandTimeout = null) 125 | { 126 | Take(1); 127 | return Select(expression, commandTimeout).FirstOrDefault(); 128 | } 129 | 130 | public async Task SingleAsync(Expression> expression, int? commandTimeout = null) 131 | { 132 | Take(1); 133 | return (await SelectAsync(expression, commandTimeout)).FirstOrDefault(); 134 | } 135 | 136 | public IDbQueryable Skip(int index, int count, bool condition = true) 137 | { 138 | if (condition) 139 | { 140 | SetPage(index, count); 141 | } 142 | return this; 143 | } 144 | 145 | public IDbQueryable Take(int count, bool condition = true) 146 | { 147 | if (condition) 148 | { 149 | Skip(0, count); 150 | } 151 | return this; 152 | } 153 | 154 | public IDbQueryable Where(Expression> expression, bool condition = true) 155 | { 156 | if (condition) 157 | { 158 | AddWhereExpression(expression); 159 | } 160 | return this; 161 | } 162 | 163 | public IDbQueryable With(string lockname) 164 | { 165 | SetLockName($" {lockname}"); 166 | return this; 167 | } 168 | 169 | private IDbQueryable JoinFormat(Expression> expression, string joinType) 170 | { 171 | var resovle = new BooleanExpressionResovle(_isSingleTable, expression, _parameters); 172 | var onExpression = resovle.Resovle(); 173 | var alias = resovle.GetTableAlias(); 174 | if (_tables.Count == 0) 175 | { 176 | joinType = string.Format(" {0} JOIN ", joinType); 177 | _tables.AddRange(alias.Select(s => s.Key)); 178 | var viewName = string.Join(joinType, alias.Select(s => $"{s.Value} AS {s.Key}")); 179 | AppendViewName(string.Format("{0} ON {1}", viewName, onExpression)); 180 | } 181 | else 182 | { 183 | var alia = alias.Where(a => !_tables.Contains(a.Key)).First(); 184 | joinType = string.Format("{0} JOIN ", joinType); 185 | var viewName = $"{joinType}{alia.Value} AS {alia.Key}"; 186 | AppendViewName(string.Format("{0} ON {1}", viewName, onExpression)); 187 | } 188 | return this; 189 | } 190 | public IDbQueryable Join(Expression> expression) 191 | { 192 | JoinFormat(expression, "INNER"); 193 | return this; 194 | } 195 | 196 | public IDbQueryable LeftJoin(Expression> expression) 197 | { 198 | JoinFormat(expression, "LEFT"); 199 | return this; 200 | } 201 | 202 | public IDbQueryable RightJoin(Expression> expression) 203 | { 204 | JoinFormat(expression, "RIGHT"); 205 | return this; 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/IDbQueryable`.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace SqlBatis.Queryables 7 | { 8 | /// 9 | /// 单表查询 10 | /// 11 | /// 12 | public interface IDbQueryable 13 | { 14 | /// 15 | /// count查询 16 | /// 17 | /// 超时时间 18 | /// 19 | int Count(int? commandTimeout = null); 20 | /// 21 | /// 异步count查询 22 | /// 23 | /// 超时时间 24 | /// 25 | Task CountAsync(int? commandTimeout = null); 26 | /// 27 | /// count查询 28 | /// 29 | /// 类型推断 30 | /// 字段列表 31 | /// 32 | int Count(Expression> expression); 33 | /// 34 | /// 异步count查询 35 | /// 36 | /// 类型推断 37 | /// 字段列表 38 | /// 39 | Task CountAsync(Expression> expression); 40 | /// 41 | /// delete查询 42 | /// 43 | /// 44 | /// 45 | int Delete(int? commandTimeout = null); 46 | /// 47 | /// 异步delete查询 48 | /// 49 | /// 50 | /// 51 | Task DeleteAsync(int? commandTimeout = null); 52 | /// 53 | /// delete查询 54 | /// 55 | /// 查询条件 56 | /// 57 | int Delete(T entity); 58 | /// 59 | /// delete查询 60 | /// 61 | /// 62 | /// 63 | int DeleteBatch(IEnumerable entities); 64 | /// 65 | /// delete查询 66 | /// 67 | /// 68 | /// 69 | Task DeleteBatchAsync(IEnumerable entities); 70 | /// 71 | /// delete查询 72 | /// 73 | /// 查询条件 74 | /// 75 | Task DeleteAsync(T entity); 76 | /// 77 | /// delete查询 78 | /// 79 | /// 查询条件 80 | /// 81 | int Delete(Expression> expression); 82 | /// 83 | /// 异步delete查询 84 | /// 85 | /// 查询条件 86 | /// 87 | Task DeleteAsync(Expression> expression); 88 | /// 89 | /// exists查询 90 | /// 91 | /// 超时时间 92 | /// 93 | bool Exists(int? commandTimeout = null); 94 | /// 95 | /// 异步exists查询 96 | /// 97 | /// 超时时间 98 | /// 99 | Task ExistsAsync(int? commandTimeout = null); 100 | /// 101 | /// exists查询 102 | /// 103 | /// 查询条件 104 | /// 105 | bool Exists(Expression> expression); 106 | /// 107 | /// 异步exists查询 108 | /// 109 | /// 查询条件 110 | /// 111 | Task ExistsAsync(Expression> expression); 112 | /// 113 | /// update查询,如果没有指定where则应用到所有记录 114 | /// 115 | /// 超时时间 116 | /// 117 | int Update(int? commandTimeout = null); 118 | /// 119 | /// 异步update查询,如果没有指定where则应用到所有记录 120 | /// 121 | /// 超时时间 122 | /// 123 | Task UpdateAsync(int? commandTimeout = null); 124 | /// 125 | /// update查询,默认根据Primarkey更新,如果存在where则仅使用指定更新条件, 126 | /// 无法通过该接口更新主键字段和主键字段 127 | /// 128 | /// 129 | /// 130 | int Update(Entity entity); 131 | /// 132 | /// 异步update查询,默认根据Primarkey更新,如果存在where则仅使用指定更新条件, 133 | /// 无法通过该接口更新主键字段和主键字段 134 | /// 135 | /// 136 | /// 137 | Task UpdateAsync(Entity entity); 138 | /// 139 | /// insert查询,该接口会忽略identity字段 140 | /// 141 | /// 142 | /// 143 | /// 144 | int Insert(Entity entity); 145 | /// 146 | /// 异步insert查询,该接口会忽略identity字段 147 | /// 148 | /// 149 | /// 150 | /// 151 | Task InsertAsync(Entity entity); 152 | /// 153 | /// insert查询,并返回id,该接口会忽略identity字段 154 | /// 155 | /// 156 | /// 157 | /// 158 | int InsertReturnId(Entity entity); 159 | /// 160 | /// 异步insert查询,并返回id,该接口会忽略identity字段 161 | /// 162 | /// 163 | /// 164 | /// 165 | Task InsertReturnIdAsync(Entity entity); 166 | /// 167 | /// 批量insert查询 168 | /// 169 | /// 170 | /// 171 | /// 172 | int Insert(IEnumerable entitys); 173 | /// 174 | /// 异步批量insert查询,该接口会忽略identity字段 175 | /// 176 | /// 177 | /// 178 | /// 179 | Task InsertAsync(IEnumerable entitys); 180 | /// 181 | /// 批量新增 182 | /// 183 | /// 184 | /// 185 | /// 186 | int InsertBatch(IEnumerable entitys, int? commandTimeout = null); 187 | /// 188 | /// 批量新增 189 | /// 190 | /// 191 | /// 192 | /// 193 | Task InsertBatchAsync(IEnumerable entitys, int? commandTimeout = null); 194 | /// 195 | /// select查询 196 | /// 197 | /// 超时时间 198 | /// 199 | IEnumerable Select(int? commandTimeout = null); 200 | /// 201 | /// 异步select查询 202 | /// 203 | /// 超时时间 204 | /// 205 | Task> SelectAsync(int? commandTimeout = null); 206 | /// 207 | /// 分页select查询 208 | /// 209 | /// 超时时间 210 | /// 结果集,总记录数 211 | (IEnumerable, int) SelectMany(int? commandTimeout = null); 212 | /// 213 | /// 异步分页select查询 214 | /// 215 | /// 超时时间 216 | /// 217 | Task<(IEnumerable, int)> SelectManyAsync(int? commandTimeout = null); 218 | /// 219 | /// 指定查询字段 220 | /// 221 | /// 返回类型 222 | /// 字段列表 223 | /// 超时时间 224 | /// 225 | IEnumerable Select(Expression> expression = null, int? commandTimeout = null); 226 | /// 227 | /// 异步select查询 228 | /// 229 | /// 返回类型 230 | /// 字段列表 231 | /// 超时时间 232 | /// 233 | Task> SelectAsync(Expression> expression = null, int? commandTimeout = null); 234 | /// 235 | /// 分页select查询 236 | /// 237 | /// 返回类型 238 | /// 字段列表 239 | /// 超时时间 240 | /// 241 | (IEnumerable, int) SelectMany(Expression> expression = null, int? commandTimeout = null); 242 | /// 243 | /// 异步分页select查询 244 | /// 245 | /// 返回类型 246 | /// 字段列表 247 | /// 超时时间 248 | /// 249 | Task<(IEnumerable, int)> SelectManyAsync(Expression> expression = null, int? commandTimeout = null); 250 | /// 251 | /// select查询 252 | /// 253 | /// 超时时间 254 | /// 255 | T Single(int? commandTimeout = null); 256 | /// 257 | /// 异步select查询 258 | /// 259 | /// 超时时间 260 | /// 261 | Task SingleAsync(int? commandTimeout = null); 262 | /// 263 | /// select查询 264 | /// 265 | /// 返回类型 266 | /// 字段列表 267 | /// 超时时间 268 | /// 269 | TResult Single(Expression> expression = null, int? commandTimeout = null); 270 | /// 271 | /// 异步select查询 272 | /// 273 | /// 返回类型 274 | /// 字段列表 275 | /// 超时时间 276 | /// 277 | Task SingleAsync(Expression> expression = null, int? commandTimeout = null); 278 | /// 279 | /// 忽略所有空列 280 | /// 281 | /// 282 | /// 283 | IDbQueryable Ignore(bool ignoreAllNullColumns = true); 284 | /// 285 | /// 在insert,update,select时忽略字段 286 | /// 287 | /// 288 | /// 289 | /// 290 | /// 291 | IDbQueryable Ignore(Expression> expression, bool condition = true); 292 | /// 293 | /// set查询 294 | /// 295 | /// 类型推断 296 | /// 字段 297 | /// 参数 298 | /// 是否有效 299 | /// 300 | IDbQueryable Set(Expression> column, TResult value, bool condition = true); 301 | /// 302 | /// set查询 303 | /// 304 | /// 类型推断 305 | /// 字段 306 | /// 表达式 307 | /// 是否有效 308 | /// 309 | IDbQueryable Set(Expression> column, Expression> expression, bool condition = true); 310 | /// 311 | /// take查询,从下标为0的行获取count条记录 312 | /// 313 | /// 记录个数 314 | /// 条件 315 | /// 316 | IDbQueryable Take(int count, bool condition = true); 317 | /// 318 | /// skip,从下标为index的行获取count条记录 319 | /// 320 | /// 起始下标 321 | /// 记录个数 322 | /// 条件 323 | /// 324 | IDbQueryable Skip(int index, int count, bool condition = true); 325 | /// 326 | /// page查询,从下标为(index-1)*count的行获取count条记录 327 | /// 328 | /// 起始页码 329 | /// 记录个数 330 | /// 条件 331 | /// 332 | IDbQueryable Page(int index, int count, bool condition = true); 333 | /// 334 | /// 指定读锁 335 | /// 336 | /// 337 | /// 338 | IDbQueryable With(string lockname); 339 | /// 340 | /// where查询,多个where有效使用and连接 341 | /// 342 | /// 表达式 343 | /// 是否有效 344 | /// 345 | IDbQueryable Where(Expression> expression, bool condition = true); 346 | /// 347 | /// having查询,多个having查询有效使用and连接 348 | /// 349 | /// 表达式 350 | /// 是否有效 351 | /// 352 | IDbQueryable Having(Expression> expression, bool condition = true); 353 | /// 354 | /// group查询 355 | /// 356 | /// 类型推断 357 | /// 字段列表 358 | /// 359 | IDbQueryable GroupBy(Expression> expression); 360 | /// 361 | /// orderby查询,升序 362 | /// 363 | /// 类型推断 364 | /// 字段列表 365 | /// 是否有效 366 | /// 367 | IDbQueryable OrderBy(Expression> expression, bool condition = true); 368 | /// 369 | /// orderby查询,降序 370 | /// 371 | /// 类型推断 372 | /// 字段列表 373 | /// 是否有效 374 | /// 375 | IDbQueryable OrderByDescending(Expression> expression, bool condition = true); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/IDbQueryable``.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace SqlBatis.Queryables 7 | { 8 | /// 9 | /// linq 查询 10 | /// 11 | /// 12 | /// 13 | public interface IDbQueryable 14 | { 15 | /// 16 | /// 内连接 17 | /// 18 | /// 19 | /// 20 | IDbQueryable Join(Expression> expression); 21 | /// 22 | /// 左外连接 23 | /// 24 | /// 25 | /// 26 | IDbQueryable LeftJoin(Expression> expression); 27 | /// 28 | /// 右外连接 29 | /// 30 | /// 31 | /// 32 | IDbQueryable RightJoin(Expression> expression); 33 | /// 34 | /// count查询 35 | /// 36 | /// 超时时间 37 | /// 38 | int Count(int? commandTimeout = null); 39 | /// 40 | /// 异步count查询 41 | /// 42 | /// 超时时间 43 | /// 44 | Task CountAsync(int? commandTimeout = null); 45 | /// 46 | /// count查询 47 | /// 48 | /// 类型推断 49 | /// 字段列表 50 | /// 51 | int Count(Expression> expression); 52 | /// 53 | /// 异步count查询 54 | /// 55 | /// 类型推断 56 | /// 字段列表 57 | /// 58 | Task CountAsync(Expression> expression); 59 | /// 60 | /// select查询 61 | /// 62 | /// 返回类型 63 | /// 字段列表 64 | /// 超时时间 65 | /// 66 | IEnumerable Select(Expression> expression, int? commandTimeout = null); 67 | /// 68 | /// 异步select查询 69 | /// 70 | /// 返回类型 71 | /// 字段列表 72 | /// 超时时间 73 | /// 74 | Task> SelectAsync(Expression> expression, int? commandTimeout = null); 75 | /// 76 | /// 分页select查询 77 | /// 78 | /// 返回类型 79 | /// 字段列表 80 | /// 超时时间 81 | /// 82 | (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null); 83 | /// 84 | /// 异步分页select查询 85 | /// 86 | /// 返回类型 87 | /// 字段列表 88 | /// 超时时间 89 | /// 90 | Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null); 91 | /// 92 | /// select查询 93 | /// 94 | /// 返回类型 95 | /// 字段列表 96 | /// 超时时间 97 | /// 98 | TResult Single(Expression> expression, int? commandTimeout = null); 99 | /// 100 | /// 异步select查询 101 | /// 102 | /// 返回类型 103 | /// 字段列表 104 | /// 超时时间 105 | /// 106 | Task SingleAsync(Expression> expression, int? commandTimeout = null); 107 | /// 108 | /// take查询,从下标为0的行获取count条记录 109 | /// 110 | /// 记录个数 111 | /// 条件 112 | /// 113 | IDbQueryable Take(int count, bool condition = true); 114 | /// 115 | /// skip,从下标为index的行获取count条记录 116 | /// 117 | /// 起始下标 118 | /// 记录个数 119 | /// 条件 120 | /// 121 | IDbQueryable Skip(int index, int count, bool condition = true); 122 | /// 123 | /// page查询,从下标为(index-1)*count的行获取count条记录 124 | /// 125 | /// 起始页码 126 | /// 记录个数 127 | /// 条件 128 | /// 129 | IDbQueryable Page(int index, int count, bool condition = true); 130 | /// 131 | /// 指定读锁 132 | /// 133 | /// 134 | /// 135 | IDbQueryable With(string lockname); 136 | /// 137 | /// where查询,多个where有效使用and连接 138 | /// 139 | /// 表达式 140 | /// 是否有效 141 | /// 142 | IDbQueryable Where(Expression> expression, bool condition = true); 143 | /// 144 | /// having查询,多个having查询有效使用and连接 145 | /// 146 | /// 表达式 147 | /// 是否有效 148 | /// 149 | IDbQueryable Having(Expression> expression, bool condition = true); 150 | /// 151 | /// group查询 152 | /// 153 | /// 类型推断 154 | /// 字段列表 155 | /// 156 | IDbQueryable GroupBy(Expression> expression); 157 | /// 158 | /// orderby查询,升序 159 | /// 160 | /// 类型推断 161 | /// 字段列表 162 | /// 是否有效 163 | /// 164 | IDbQueryable OrderBy(Expression> expression, bool condition = true); 165 | /// 166 | /// orderby查询,降序 167 | /// 168 | /// 类型推断 169 | /// 字段列表 170 | /// 是否有效 171 | /// 172 | IDbQueryable OrderByDescending(Expression> expression, bool condition = true); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/IDbQueryable```.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace SqlBatis.Queryables 7 | { 8 | /// 9 | /// linq 查询 10 | /// 11 | /// 12 | /// 13 | /// 14 | public interface IDbQueryable 15 | { 16 | /// 17 | /// 内连接 18 | /// 19 | /// 20 | /// 21 | IDbQueryable Join(Expression> expression); 22 | /// 23 | /// 左外连接 24 | /// 25 | /// 26 | /// 27 | IDbQueryable LeftJoin(Expression> expression); 28 | /// 29 | /// 右外连接 30 | /// 31 | /// 32 | /// 33 | IDbQueryable RightJoin(Expression> expression); 34 | /// 35 | /// count查询 36 | /// 37 | /// 超时时间 38 | /// 39 | int Count(int? commandTimeout = null); 40 | /// 41 | /// 异步count查询 42 | /// 43 | /// 超时时间 44 | /// 45 | Task CountAsync(int? commandTimeout = null); 46 | /// 47 | /// count查询 48 | /// 49 | /// 类型推断 50 | /// 字段列表 51 | /// 52 | int Count(Expression> expression); 53 | /// 54 | /// 异步count查询 55 | /// 56 | /// 类型推断 57 | /// 字段列表 58 | /// 59 | Task CountAsync(Expression> expression); 60 | /// 61 | /// select查询 62 | /// 63 | /// 返回类型 64 | /// 字段列表 65 | /// 超时时间 66 | /// 67 | IEnumerable Select(Expression> expression, int? commandTimeout = null); 68 | /// 69 | /// 异步select查询 70 | /// 71 | /// 返回类型 72 | /// 字段列表 73 | /// 超时时间 74 | /// 75 | Task> SelectAsync(Expression> expression, int? commandTimeout = null); 76 | /// 77 | /// 分页select查询 78 | /// 79 | /// 返回类型 80 | /// 字段列表 81 | /// 超时时间 82 | /// 83 | (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null); 84 | /// 85 | /// 异步分页select查询 86 | /// 87 | /// 返回类型 88 | /// 字段列表 89 | /// 超时时间 90 | /// 91 | Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null); 92 | /// 93 | /// select查询 94 | /// 95 | /// 返回类型 96 | /// 字段列表 97 | /// 超时时间 98 | /// 99 | TResult Single(Expression> expression, int? commandTimeout = null); 100 | /// 101 | /// 异步select查询 102 | /// 103 | /// 返回类型 104 | /// 字段列表 105 | /// 超时时间 106 | /// 107 | Task SingleAsync(Expression> expression, int? commandTimeout = null); 108 | /// 109 | /// take查询,从下标为0的行获取count条记录 110 | /// 111 | /// 记录个数 112 | /// 条件 113 | /// 114 | IDbQueryable Take(int count, bool condition = true); 115 | /// 116 | /// skip,从下标为index的行获取count条记录 117 | /// 118 | /// 起始下标 119 | /// 记录个数 120 | /// 条件 121 | /// 122 | IDbQueryable Skip(int index, int count, bool condition = true); 123 | /// 124 | /// page查询,从下标为(index-1)*count的行获取count条记录 125 | /// 126 | /// 起始页码 127 | /// 记录个数 128 | /// 条件 129 | /// 130 | IDbQueryable Page(int index, int count, bool condition = true); 131 | /// 132 | /// 指定读锁 133 | /// 134 | /// 135 | /// 136 | IDbQueryable With(string lockname); 137 | /// 138 | /// where查询,多个where有效使用and连接 139 | /// 140 | /// 表达式 141 | /// 是否有效 142 | /// 143 | IDbQueryable Where(Expression> expression, bool condition = true); 144 | /// 145 | /// having查询,多个having查询有效使用and连接 146 | /// 147 | /// 表达式 148 | /// 是否有效 149 | /// 150 | IDbQueryable Having(Expression> expression, bool condition = true); 151 | /// 152 | /// group查询 153 | /// 154 | /// 类型推断 155 | /// 字段列表 156 | /// 157 | IDbQueryable GroupBy(Expression> expression); 158 | /// 159 | /// orderby查询,升序 160 | /// 161 | /// 类型推断 162 | /// 字段列表 163 | /// 是否有效 164 | /// 165 | IDbQueryable OrderBy(Expression> expression, bool condition = true); 166 | /// 167 | /// orderby查询,降序 168 | /// 169 | /// 类型推断 170 | /// 字段列表 171 | /// 是否有效 172 | /// 173 | IDbQueryable OrderByDescending(Expression> expression, bool condition = true); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/IDbQueryable````.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace SqlBatis.Queryables 7 | { 8 | /// 9 | /// linq 查询 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | public interface IDbQueryable 16 | { 17 | /// 18 | /// 内连接 19 | /// 20 | /// 21 | /// 22 | IDbQueryable Join(Expression> expression); 23 | /// 24 | /// 左外连接 25 | /// 26 | /// 27 | /// 28 | IDbQueryable LeftJoin(Expression> expression); 29 | /// 30 | /// 右外连接 31 | /// 32 | /// 33 | /// 34 | IDbQueryable RightJoin(Expression> expression); 35 | /// 36 | /// count查询 37 | /// 38 | /// 超时时间 39 | /// 40 | int Count(int? commandTimeout = null); 41 | /// 42 | /// 异步count查询 43 | /// 44 | /// 超时时间 45 | /// 46 | Task CountAsync(int? commandTimeout = null); 47 | /// 48 | /// count查询 49 | /// 50 | /// 类型推断 51 | /// 字段列表 52 | /// 53 | int Count(Expression> expression); 54 | /// 55 | /// 异步count查询 56 | /// 57 | /// 类型推断 58 | /// 字段列表 59 | /// 60 | Task CountAsync(Expression> expression); 61 | /// 62 | /// select查询 63 | /// 64 | /// 返回类型 65 | /// 字段列表 66 | /// 超时时间 67 | /// 68 | IEnumerable Select(Expression> expression, int? commandTimeout = null); 69 | /// 70 | /// 异步select查询 71 | /// 72 | /// 返回类型 73 | /// 字段列表 74 | /// 超时时间 75 | /// 76 | Task> SelectAsync(Expression> expression, int? commandTimeout = null); 77 | /// 78 | /// 分页select查询 79 | /// 80 | /// 返回类型 81 | /// 字段列表 82 | /// 超时时间 83 | /// 84 | (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null); 85 | /// 86 | /// 异步分页select查询 87 | /// 88 | /// 返回类型 89 | /// 字段列表 90 | /// 超时时间 91 | /// 92 | Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null); 93 | /// 94 | /// select查询 95 | /// 96 | /// 返回类型 97 | /// 字段列表 98 | /// 超时时间 99 | /// 100 | TResult Single(Expression> expression, int? commandTimeout = null); 101 | /// 102 | /// 异步select查询 103 | /// 104 | /// 返回类型 105 | /// 字段列表 106 | /// 超时时间 107 | /// 108 | Task SingleAsync(Expression> expression, int? commandTimeout = null); 109 | /// 110 | /// take查询,从下标为0的行获取count条记录 111 | /// 112 | /// 记录个数 113 | /// 条件 114 | /// 115 | IDbQueryable Take(int count, bool condition = true); 116 | /// 117 | /// skip,从下标为index的行获取count条记录 118 | /// 119 | /// 起始下标 120 | /// 记录个数 121 | /// 条件 122 | /// 123 | IDbQueryable Skip(int index, int count, bool condition = true); 124 | /// 125 | /// page查询,从下标为(index-1)*count的行获取count条记录 126 | /// 127 | /// 起始页码 128 | /// 记录个数 129 | /// 条件 130 | /// 131 | IDbQueryable Page(int index, int count, bool condition = true); 132 | /// 133 | /// 指定读锁 134 | /// 135 | /// 136 | /// 137 | IDbQueryable With(string lockname); 138 | /// 139 | /// where查询,多个where有效使用and连接 140 | /// 141 | /// 表达式 142 | /// 是否有效 143 | /// 144 | IDbQueryable Where(Expression> expression, bool condition = true); 145 | /// 146 | /// having查询,多个having查询有效使用and连接 147 | /// 148 | /// 表达式 149 | /// 是否有效 150 | /// 151 | IDbQueryable Having(Expression> expression, bool condition = true); 152 | /// 153 | /// group查询 154 | /// 155 | /// 类型推断 156 | /// 字段列表 157 | /// 158 | IDbQueryable GroupBy(Expression> expression); 159 | /// 160 | /// orderby查询,升序 161 | /// 162 | /// 类型推断 163 | /// 字段列表 164 | /// 是否有效 165 | /// 166 | IDbQueryable OrderBy(Expression> expression, bool condition = true); 167 | /// 168 | /// orderby查询,降序 169 | /// 170 | /// 类型推断 171 | /// 字段列表 172 | /// 是否有效 173 | /// 174 | IDbQueryable OrderByDescending(Expression> expression, bool condition = true); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/SqlBatis/Queryables/Operator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | 6 | namespace SqlBatis 7 | { 8 | /// 9 | /// 数据库操作符 10 | /// 11 | public class Operator 12 | { 13 | /// 14 | /// in 15 | /// 16 | /// 字段 17 | /// 参数 18 | /// 19 | public static bool In(object column, IEnumerable values) => default; 20 | /// 21 | /// in 22 | /// 23 | /// 字段 24 | /// 参数 25 | /// 26 | public static bool In(object column, params object[] values) => default; 27 | /// 28 | /// not in 29 | /// 30 | /// 字段 31 | /// 参数 32 | /// 33 | public static bool NotIn(object column, IEnumerable values) => default; 34 | /// 35 | /// not in 36 | /// 37 | /// 字段 38 | /// 参数 39 | /// 40 | public static bool NotIn(object column, params object[] values) => default; 41 | /// 42 | /// like %value% 43 | /// 44 | /// 字段 45 | /// 参数 46 | /// 47 | public static bool Contains(string column, string value) => default; 48 | /// 49 | /// not like %value% 50 | /// 51 | /// 字段 52 | /// 参数 53 | /// 54 | public static bool NotContains(string column, string value) => default; 55 | /// 56 | /// like value% 57 | /// 58 | /// 字段 59 | /// 参数 60 | /// 61 | public static bool StartsWith(string column, string value) => default; 62 | /// 63 | /// not like value% 64 | /// 65 | /// 字段 66 | /// 参数 67 | /// 68 | public static bool NotStartsWith(string column, string value) => default; 69 | /// 70 | /// like %value 71 | /// 72 | /// 字段 73 | /// 参数 74 | /// 75 | public static bool EndsWith(string column, string value) => default; 76 | /// 77 | /// not like %value 78 | /// 79 | /// 字段 80 | /// 参数 81 | /// 82 | public static bool NotEndsWith(string column, string value) => default; 83 | /// 84 | /// regex value 85 | /// 86 | /// 字段 87 | /// 参数 88 | /// 89 | public static bool Regexp(string column, string value) => default; 90 | /// 91 | /// not regex value 92 | /// 93 | /// 字段 94 | /// 参数 95 | /// 96 | public static bool NotRegexp(string column, string value) => default; 97 | /// 98 | /// 解析表达式 99 | /// 100 | /// 101 | /// 102 | internal static string ResovleExpressionType(ExpressionType type) 103 | { 104 | var condition = string.Empty; 105 | switch (type) 106 | { 107 | case ExpressionType.Add: 108 | condition = "+"; 109 | break; 110 | case ExpressionType.Subtract: 111 | condition = "-"; 112 | break; 113 | case ExpressionType.Multiply: 114 | condition = "*"; 115 | break; 116 | case ExpressionType.Divide: 117 | condition = "/"; 118 | break; 119 | case ExpressionType.Modulo: 120 | condition = "%"; 121 | break; 122 | case ExpressionType.Equal: 123 | condition = "="; 124 | break; 125 | case ExpressionType.NotEqual: 126 | condition = "<>"; 127 | break; 128 | case ExpressionType.GreaterThan: 129 | condition = ">"; 130 | break; 131 | case ExpressionType.GreaterThanOrEqual: 132 | condition = ">="; 133 | break; 134 | case ExpressionType.LessThan: 135 | condition = "<"; 136 | break; 137 | case ExpressionType.LessThanOrEqual: 138 | condition = "<="; 139 | break; 140 | case ExpressionType.OrElse: 141 | condition = "OR"; 142 | break; 143 | case ExpressionType.AndAlso: 144 | condition = "AND"; 145 | break; 146 | case ExpressionType.Not: 147 | condition = "NOT"; 148 | break; 149 | case ExpressionType.Or: 150 | condition = "|"; 151 | break; 152 | case ExpressionType.And: 153 | condition = "&"; 154 | break; 155 | case ExpressionType.ExclusiveOr: 156 | condition = "^"; 157 | break; 158 | case ExpressionType.LeftShift: 159 | condition = "<<"; 160 | break; 161 | case ExpressionType.RightShift: 162 | condition = ">>"; 163 | break; 164 | } 165 | return condition; 166 | } 167 | /// 168 | /// 解析表达式 169 | /// 170 | /// 171 | /// 172 | internal static string ResovleExpressionType(string name) 173 | { 174 | switch (name) 175 | { 176 | case nameof(Operator.In): 177 | name = "IN"; 178 | break; 179 | case nameof(Operator.NotIn): 180 | name = "NOT IN"; 181 | break; 182 | case nameof(Operator.Contains): 183 | case nameof(Operator.StartsWith): 184 | case nameof(Operator.EndsWith): 185 | name = "LIKE"; 186 | break; 187 | case nameof(Operator.NotContains): 188 | case nameof(Operator.NotStartsWith): 189 | case nameof(Operator.NotEndsWith): 190 | name = "NOT LIKE"; 191 | break; 192 | case nameof(Operator.Regexp): 193 | name = "REGEXP"; 194 | break; 195 | case nameof(Operator.NotRegexp): 196 | name = "NOT REGEXP"; 197 | break; 198 | case "IN": 199 | name = "IN"; 200 | break; 201 | case "NOT": 202 | name = "NOT"; 203 | break; 204 | case "~": 205 | name = "~"; 206 | break; 207 | } 208 | return name; 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/SqlBatis/SqlBatis.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | SqlBatis 4 | SqlBatis 5 | $(NoWarn);1591 6 | orm;sql;micro-orm;ibatis 7 | net45;netstandard2.0;net5 8 | 2.0.0.3 9 | https://github.com/1448376744/SqlBatis 10 | https://github.com/1448376744/SqlBatis 11 | orm;sql;micro-orm; 12 | A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite etc.. 13 | Apache-2.0 14 | true 15 | 2.0.0.3 16 | 2.0.0.3 17 | github 18 | 19 | 20 | .\SqlBatis.xml 21 | 22 | 23 | .\SqlBatis.xml 24 | 25 | 26 | .\SqlBatis.xml 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/SqlBatis/SqlBatisSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SqlBatis 6 | { 7 | /// 8 | /// 全局设置 9 | /// 10 | public static class SqlBatisSettings 11 | { 12 | /// 13 | /// 是否允许常量表达式的结果为:默认不允许将抛出NullReferenceException 14 | /// 15 | public static bool AllowConstantExpressionResultIsNull { get; set; } = false; 16 | 17 | /// 18 | /// 是否忽略DbCommand中的无效参数 19 | /// 20 | public static bool IgnoreDbCommandInvalidParameters { get; set; } = false; 21 | 22 | /// 23 | /// 数据库元信息提供程序 24 | /// 25 | internal static IDbMetaInfoProvider DbMetaInfoProvider { get; set; } 26 | = new AnnotationDbMetaInfoProvider(); 27 | } 28 | } 29 | --------------------------------------------------------------------------------