├── .gitattributes ├── .gitignore ├── Dapper.Extensions.Tests ├── Dapper.Extensions.Tests.xproj ├── DapperExtensionsTest.cs ├── Entitys.cs ├── Properties │ └── AssemblyInfo.cs ├── WhereExpressionTest.cs └── project.json ├── Dapper.Extensions.sln ├── README.md ├── global.json └── src └── Dapper.Extensions ├── ActiveAttribute.cs ├── Dapper.Extensions.xproj ├── DapperSort.cs ├── FieldsFormater.cs ├── IOperatorWhere.cs ├── OperatorWhereObject.cs ├── PageCondition.cs ├── PageView.cs ├── Parameter.cs ├── Properties └── AssemblyInfo.cs ├── SQLMethod.cs ├── SqlMapperExtensions.cs ├── SqlMethodNameCallBack.cs ├── SqlTranslateFormater.cs └── project.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 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 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /Dapper.Extensions.Tests/Dapper.Extensions.Tests.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | b6913d7c-21a4-4413-b1cd-68f1a3fbd57d 10 | Dapper.Extensions.Tests 11 | .\obj 12 | .\bin\ 13 | v4.5.2 14 | 15 | 16 | 2.0 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Dapper.Extensions.Tests/DapperExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.Data.SqlClient; 4 | using System.Linq; 5 | using Dapper.Contrib.Extensions; 6 | using Xunit; 7 | 8 | namespace Dapper.Extensions.Tests 9 | { 10 | public class DapperExtensionsTest 11 | { 12 | private IDbConnection GetOpenConnection() 13 | { 14 | var connection = new SqlConnection(@""); 15 | //connection.Open(); 16 | return connection; 17 | } 18 | 19 | [Fact] 20 | public void GetCustomerById() 21 | { 22 | using (var connect = GetOpenConnection()) 23 | { 24 | var customer = connect.Get(1); 25 | Assert.Equal(customer.CustomerID, 1); 26 | Assert.Equal(customer.CustomerName, "A1"); 27 | Assert.Equal(customer.CustomerNumber, "9999"); 28 | Assert.Equal(customer.CustomerCity, "x1"); 29 | } 30 | } 31 | 32 | [Fact] 33 | public void GetCustomerByQuery() 34 | { 35 | using (var connect = GetOpenConnection()) 36 | { 37 | var customer = connect.Get(c => c.CustomerID == 1); 38 | Assert.Equal(customer.CustomerID, 1); 39 | Assert.Equal(customer.CustomerName, "A1"); 40 | Assert.Equal(customer.CustomerNumber, "9999"); 41 | Assert.Equal(customer.CustomerCity, "x1"); 42 | } 43 | } 44 | 45 | 46 | [Fact] 47 | public void GetCustomerListByQuery() 48 | { 49 | using (var connect = GetOpenConnection()) 50 | { 51 | var customers = connect.GetAll(c => c.CustomerID <= 50); 52 | Assert.Equal(customers.Count(), 50); 53 | } 54 | } 55 | 56 | 57 | [Fact] 58 | public void InsertCustomer() 59 | { 60 | using (var connect = GetOpenConnection()) 61 | { 62 | int count = connect.QuerySingle("select count(1) from Customers2"); 63 | 64 | 65 | var customer = new CustomersEntity() { CustomerName = "A1", CustomerCity = "x1", CustomerNumber = "9999" }; 66 | 67 | long id = connect.Insert(customer); 68 | 69 | Assert.Equal(id - 2, count); 70 | } 71 | } 72 | 73 | 74 | /// 75 | /// 注意:Dapper自带的Update语句;不能只更新某些字段 76 | /// 77 | [Fact] 78 | public void UpdateCustomer() 79 | { 80 | using (var connect = GetOpenConnection()) 81 | { 82 | 83 | var customer = new CustomersEntity() { CustomerID = 1, CustomerName = "A1", CustomerCity = "x1", CustomerNumber = "9999" }; 84 | //更新实体,不能只更新某些字段 85 | bool s = connect.Update(customer); 86 | 87 | Assert.Equal(s, true); 88 | } 89 | } 90 | 91 | 92 | /// 93 | /// 注意:咱们框架里扩展的复杂Update语句,保证只更新表达式中的字段值 94 | /// 95 | [Fact] 96 | public void SimpleUpdateExpressionMethodTest1() 97 | { 98 | using (var connect = GetOpenConnection()) 99 | { 100 | //更新一个表达式实体,保证只更新表达式中的字段值 101 | //IOperatorWhere.Where(exp); 102 | var updater = connect.Update(c => new CustomersEntity() 103 | { 104 | CustomerName = "hello", 105 | CustomerCity = "x1" 106 | }) 107 | .Where(c => c.CustomerID == 2); 108 | 109 | string sql = updater.SQL; 110 | 111 | Assert.Equal(sql, 112 | "Update Customers2 SET CustomerName=@CustomerName,CustomerCity=@CustomerCity Where CustomerID = 2"); 113 | 114 | //IOperatorWhere.Go(); 115 | //API采用 Update({new entity expression}).Where(expression).Go()的链式使用方式。 116 | bool ret = updater.Go(); 117 | 118 | Assert.Equal(ret, true); 119 | } 120 | } 121 | 122 | [Fact] 123 | public void SimpleUpdateExpressionMethodTest2() 124 | { 125 | using (var connect = GetOpenConnection()) 126 | { 127 | // 匿名对象1 128 | var entity1 = new { CustomerName = "hello", CustomerCity = "x1" }; 129 | 130 | var updater1 = connect.Update(entity1) 131 | .Where(c => c.CustomerID == 2); 132 | 133 | string sql1 = updater1.SQL; 134 | Assert.Equal(sql1, 135 | "Update Customers2 SET CustomerName=@CustomerName,CustomerCity=@CustomerCity Where CustomerID = 2"); 136 | 137 | // 匿名对象2 138 | CustomersEntity entity2 = new CustomersEntity { CustomerName = "hello", CustomerCity = "x1", CustomerNumber = "x123" }; 139 | 140 | var updater2 = connect.Update(new { entity2.CustomerName, entity2.CustomerCity }) 141 | .Where(c => c.CustomerID == 2); 142 | 143 | string sql2 = updater2.SQL; 144 | Assert.Equal(sql2, 145 | "Update Customers2 SET CustomerName=@CustomerName,CustomerCity=@CustomerCity Where CustomerID = 2"); 146 | 147 | // 实体对象/全字段更新且忽略标记了Computed特性的属性 148 | var updater3 = connect.Update(entity2) 149 | .Where(c => c.CustomerID == 2); 150 | string sql3 = updater3.SQL; 151 | } 152 | } 153 | 154 | 155 | 156 | /// 157 | /// 注意:Dapper扩展Update语句;用于更新IsActive字段 158 | /// 159 | [Fact] 160 | public void SetActiveTest() 161 | { 162 | using (var connect = GetOpenConnection()) 163 | { 164 | 165 | bool s = connect.SetActive(1, true); 166 | 167 | Assert.Equal(s, true); 168 | } 169 | } 170 | 171 | 172 | /// 173 | /// 注意:Dapper扩展Update语句;用于更新IsActive字段 174 | /// 175 | [Fact] 176 | public void SetActiveTest2() 177 | { 178 | using (var connect = GetOpenConnection()) 179 | { 180 | var customer = new CustomersEntity() { CustomerID = 2, IsActive = false }; 181 | bool s = connect.SetActive(customer); 182 | 183 | Assert.Equal(s, true); 184 | } 185 | } 186 | 187 | /// 188 | /// 扩展Dapper默认Query方法支持Expression查询 189 | /// 190 | [Fact] 191 | public void QueryWithSortCustomer() 192 | { 193 | CustomersEntity entity = null; 194 | DapperSort sort = new DapperSort() 195 | { 196 | new Sort(index:1,field:nameof(entity.CustomerCity),sortType:ESortType.Asc), 197 | new Sort(index:0,field:nameof(entity.CustomerName),sortType:ESortType.Desc) 198 | }; 199 | 200 | using (var connect = GetOpenConnection()) 201 | { 202 | var customer = connect.Query(c => c.CustomerID == 1, null, sort); 203 | } 204 | } 205 | 206 | /// 207 | /// 查询部分字段 208 | /// 209 | [Fact] 210 | public void QueryPartial() 211 | { 212 | CustomersEntity entity = null; 213 | DapperSort sort = new DapperSort() 214 | { 215 | new Sort(index:1,field:nameof(entity.CustomerCity),sortType:ESortType.Asc), 216 | new Sort(index:0,field:nameof(entity.CustomerName),sortType:ESortType.Desc) 217 | }; 218 | 219 | using (var connect = GetOpenConnection()) 220 | { 221 | var customer = connect.Query(c => c.CustomerID == 1, 222 | c => new 223 | { 224 | c.CustomerCity, 225 | c.CustomerName 226 | }, sort); 227 | } 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /Dapper.Extensions.Tests/Entitys.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Dapper.Contrib.Extensions; 6 | 7 | namespace Dapper.Extensions.Tests 8 | { 9 | [Table("Customers2")] 10 | public partial class CustomersEntity 11 | { 12 | 13 | [Key] 14 | 15 | /// 16 | /// 17 | /// 18 | public Int32 CustomerID { set; get; } 19 | 20 | /// 21 | /// 22 | /// 23 | public String CustomerNumber { set; get; } 24 | 25 | /// 26 | /// 27 | /// 28 | public String CustomerName { set; get; } 29 | 30 | /// 31 | /// 32 | /// 33 | public String CustomerCity { set; get; } 34 | 35 | [Active] 36 | 37 | /// 38 | /// 39 | /// 40 | public Nullable IsActive { set; get; } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Dapper.Extensions.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Dapper.Extensions.Tests")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("b6913d7c-21a4-4413-b1cd-68f1a3fbd57d")] 20 | -------------------------------------------------------------------------------- /Dapper.Extensions.Tests/WhereExpressionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq.Expressions; 5 | using Dapper; 6 | using Dapper.Contrib; 7 | using Dapper.Contrib.Extensions; 8 | using Dapper.Extensions; 9 | using Dapper.Extensions.Tests; 10 | using Xunit; 11 | using System.Linq; 12 | 13 | namespace Dapper.Extensions.Tests 14 | { 15 | 16 | public class WhereExpressionTest 17 | { 18 | [Fact] 19 | public void SimpleWhereTest1() 20 | { 21 | Expression> queryExp1 = ct => ct.CustomerID < 50 && ct.CustomerCity == "B-City"; 22 | 23 | var translate = new SqlTranslateFormater(); 24 | string sql = translate.Translate(queryExp1); 25 | 26 | Assert.Equal(sql, "CustomerID < 50 AND CustomerCity = 'B-City'"); 27 | 28 | } 29 | 30 | [Fact] 31 | public void SimpleWhereTest2() 32 | { 33 | Expression> queryExp1 = ct => ct.CustomerID == 1 && 34 | (ct.CustomerCity == "B-City" || ct.CustomerNumber == "0000"); 35 | 36 | var translate = new SqlTranslateFormater(); 37 | string sql = translate.Translate(queryExp1); 38 | 39 | Assert.Equal(sql, "CustomerID = 1 AND (CustomerCity = 'B-City' OR CustomerNumber = '0000')"); 40 | 41 | } 42 | 43 | [Fact] 44 | public void SimpleWhereMethodTest1() 45 | { 46 | Expression> queryExp1 = ct => ct.CustomerID <= 50 && (SQLMethod.IsNull(ct.CustomerCity)); 47 | 48 | var translate = new SqlTranslateFormater(); 49 | string sql = translate.Translate(queryExp1); 50 | 51 | Assert.Equal(sql, "CustomerID <= 50 AND CustomerCity is NULL"); 52 | 53 | } 54 | 55 | 56 | [Fact] 57 | public void SimpleWhereMethodTest2() 58 | { 59 | Expression> queryExp1 = ct => ct.CustomerID <= 50 && (SQLMethod.IsNull(ct.CustomerCity)); 60 | 61 | var translate = new SqlTranslateFormater(); 62 | string sql = translate.Translate(queryExp1); 63 | 64 | Assert.Equal(sql, "CustomerID <= 50 AND CustomerCity is NULL"); 65 | 66 | } 67 | 68 | 69 | [Fact] 70 | public void WhereMethodForParametersTest() 71 | { 72 | TestValueTypeParam(50); 73 | } 74 | 75 | private void TestValueTypeParam(int id) 76 | { 77 | Expression> queryExp1 = ct => ct.CustomerID <= id && (SQLMethod.IsNull(ct.CustomerCity)); 78 | 79 | var translate = new SqlTranslateFormater(); 80 | string sql = translate.Translate(queryExp1); 81 | 82 | Assert.Equal(sql, "CustomerID <= 50 AND CustomerCity is NULL"); 83 | } 84 | 85 | [Fact] 86 | public void WhereMethodForParametersTest2() 87 | { 88 | TestEntityParam(new TestValue { Id = 50 }); 89 | } 90 | 91 | private void TestEntityParam(TestValue v) 92 | { 93 | Expression> queryExp1 = ct => ct.CustomerID <= v.Id && (SQLMethod.IsNull(ct.CustomerCity)); 94 | 95 | var translate = new SqlTranslateFormater(); 96 | string sql = translate.Translate(queryExp1); 97 | 98 | Assert.Equal(sql, "CustomerID <= 50 AND CustomerCity is NULL"); 99 | } 100 | 101 | [Fact] 102 | public void SimpleWhereMethodTest3() 103 | { 104 | //IEnumerable ids = new List() { 40, 50 };//通过测试 105 | //int[] ids = new[] {40, 50};//通过测试 106 | List ids = new List() { 40, 50 };//通过测试 107 | 108 | Expression> queryExp1 = ct => ids.Contains(ct.CustomerID) && (SQLMethod.IsNull(ct.CustomerCity)); 109 | 110 | var translate = new SqlTranslateFormater(); 111 | string sql = translate.Translate(queryExp1); 112 | 113 | Assert.Equal(sql, "CustomerID In (40,50) AND CustomerCity is NULL"); 114 | 115 | } 116 | 117 | 118 | } 119 | 120 | public class TestValue 121 | { 122 | public int Id { set; get; } 123 | } 124 | 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Dapper.Extensions.Tests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "buildOptions": { 4 | "debugType": "portable" 5 | }, 6 | "dependencies": { 7 | "Dapper.Extensions": "1.0.0-*", 8 | "Dapper": "1.50.2", 9 | "Dapper.Contrib": "1.50.0", 10 | "System.Data.Common": "4.1.0", 11 | "xunit": "2.2.0-beta2-build3300", 12 | "dotnet-test-xunit": "2.2.0-preview2-build1029" 13 | }, 14 | "testRunner": "xunit" 15 | , 16 | "frameworks": { 17 | "netcoreapp1.0": { 18 | "dependencies": { 19 | "Microsoft.NETCore.App": { 20 | "type": "platform", 21 | "version": "1.0.0" 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Dapper.Extensions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BD0FD322-C606-4ADE-9483-F1E0644BF63E}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A1F5B0F2-8388-42F0-A616-129E75AABAC5}" 9 | ProjectSection(SolutionItems) = preProject 10 | global.json = global.json 11 | EndProjectSection 12 | EndProject 13 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.Extensions", "src\Dapper.Extensions\Dapper.Extensions.xproj", "{F892188E-C751-4BB2-91BA-75B27236C9CD}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B7E8B5AA-8505-4A19-B195-50571030C690}" 16 | EndProject 17 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.Extensions.Tests", "Dapper.Extensions.Tests\Dapper.Extensions.Tests.xproj", "{B6913D7C-21A4-4413-B1CD-68F1A3FBD57D}" 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {F892188E-C751-4BB2-91BA-75B27236C9CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {F892188E-C751-4BB2-91BA-75B27236C9CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {F892188E-C751-4BB2-91BA-75B27236C9CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {F892188E-C751-4BB2-91BA-75B27236C9CD}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {B6913D7C-21A4-4413-B1CD-68F1A3FBD57D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {B6913D7C-21A4-4413-B1CD-68F1A3FBD57D}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {B6913D7C-21A4-4413-B1CD-68F1A3FBD57D}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {B6913D7C-21A4-4413-B1CD-68F1A3FBD57D}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | GlobalSection(NestedProjects) = preSolution 38 | {F892188E-C751-4BB2-91BA-75B27236C9CD} = {BD0FD322-C606-4ADE-9483-F1E0644BF63E} 39 | {B6913D7C-21A4-4413-B1CD-68F1A3FBD57D} = {B7E8B5AA-8505-4A19-B195-50571030C690} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dapper.Extensions 2 | YOYOFx中使用的Dapper扩展 3 | ###1. Get Entity: 4 | ```javascript 5 | using (var connect = GetOpenConnection()) 6 | { 7 | var customer = connect.Get(1); 8 | var customer1 = connect.Get(c=>c.CustomerID == 1); 9 | } 10 | ``` 11 | 12 | ###2. Insert Entity: 13 | ```javascript 14 | using (var connect = GetOpenConnection()) 15 | { 16 | int count = connect.QuerySingle("select count(1) from Customers2"); 17 | var customer = new CustomersEntity() { CustomerName = "A1", CustomerCity = "x1" , CustomerNumber = "9999"}; 18 | long id = connect.Insert(customer); 19 | } 20 | ``` 21 | 22 | ###3. Update Entity: 23 | ```javascript 24 | using (var connect = GetOpenConnection()) 25 | { 26 | var customer = new CustomersEntity() { CustomerID = 1 , CustomerName = "A1", CustomerCity = "x1", CustomerNumber = "9999" }; 27 | //1.更新实体,不能只更新某些字段 28 | bool s = connect.Update(customer); 29 | //2.更新一个表达式实体,保证只更新表达式中的字段值 30 | //IOperatorWhere.Where(exp).Go(); 31 | var updater = connect.Update(c => new CustomersEntity 32 | { 33 | CustomerName = "hello", 34 | CustomerCity = "x1" 35 | }); 36 | bool ret = updater.Go(); 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test" ], 3 | "sdk": { 4 | "version": "1.0.0-preview2-003121" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/ActiveAttribute.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 Dapper.Extensions 8 | { 9 | /// 10 | /// 逻辑删除标记 11 | /// 12 | public class ActiveAttribute : Attribute 13 | { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/Dapper.Extensions.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | f892188e-c751-4bb2-91ba-75b27236c9cd 11 | Dapper.Extensions 12 | .\obj 13 | .\bin\ 14 | v4.5.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/DapperSort.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 Dapper.Extensions 8 | { 9 | /// 10 | /// 排序对象 11 | /// 12 | public class DapperSort : List 13 | { 14 | public override string ToString() 15 | { 16 | StringBuilder sb = new StringBuilder(); 17 | this.Sort(new SortCompair()); 18 | for (int i = 0; i < this.Count; i++) 19 | { 20 | sb.AppendFormat("{0} {1}", this[i].Field, this[i].ESortType); 21 | if (i < this.Count - 1) 22 | { 23 | sb.Append(", "); 24 | } 25 | } 26 | return sb.ToString(); 27 | } 28 | } 29 | 30 | public class SortCompair : IComparer 31 | { 32 | public int Compare(Sort x, Sort y) 33 | { 34 | return x.Index - y.Index; 35 | } 36 | } 37 | 38 | public class Sort 39 | { 40 | public Sort(int index, string field, ESortType sortType) 41 | { 42 | Index = index; 43 | Field = field; 44 | ESortType = sortType; 45 | } 46 | 47 | public int Index { get; set; } 48 | public string Field { get; set; } 49 | public ESortType ESortType { get; set; } 50 | } 51 | 52 | public enum ESortType 53 | { 54 | Asc = 1, 55 | Desc = 2 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/FieldsFormater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Dapper.Extensions 7 | { 8 | /// 9 | /// 访问 o => new obj{ ID = "1" },表达式的解析器 10 | /// 11 | public class FieldsFormater : ExpressionVisitor 12 | { 13 | /// 14 | /// sql server prefix of the paramter name SQLSERVER的参数修正符 15 | /// 16 | public string ParamPrefix => "@"; 17 | 18 | public FieldsFormater() 19 | { 20 | 21 | } 22 | /// 23 | /// 解析后得到的参数如 [ { key: "ID" Value: "1" } , ... ] 24 | /// 25 | Dictionary parameters = new Dictionary(); 26 | 27 | /// 28 | /// 访问常量表达式,将字段和值添加到参数列表中。 29 | /// 30 | /// 表达式节点 31 | /// 字段名 32 | /// 33 | protected Expression VisitConstant(ConstantExpression node,string fieldName) 34 | { 35 | if (parameters.Keys.Any(k => k == fieldName)) 36 | parameters[fieldName].Value = node.Value; 37 | else 38 | { 39 | parameters.Add(fieldName, new Parameter(ParamPrefix + fieldName, node.Value)); 40 | } 41 | return base.VisitConstant(node); 42 | } 43 | 44 | //将右值表达式如 ID = Convert(i) 直接通过编译计算出来 45 | protected Expression MyVisitMember(MemberExpression node, string fieldName) 46 | { 47 | LambdaExpression lambda = Expression.Lambda(node); 48 | var fn = lambda.Compile(); 49 | VisitConstant(Expression.Constant(fn.DynamicInvoke(null), node.Type), fieldName); 50 | return base.VisitMember(node); 51 | } 52 | 53 | /// 54 | /// 访问成员,如 ID = 1 时。 55 | /// 56 | /// 57 | /// 58 | protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) 59 | { 60 | string propertyName = node.Member.Name; 61 | string fieldName = propertyName; 62 | 63 | if (node.Expression.NodeType == ExpressionType.Call) 64 | { 65 | VisitMethodCall((MethodCallExpression)node.Expression,fieldName); 66 | } 67 | else if (node.Expression.NodeType == ExpressionType.Convert) 68 | { 69 | var ue = node.Expression as UnaryExpression; 70 | var call = ue.Operand as MethodCallExpression; 71 | 72 | if (call != null) 73 | this.VisitMethodCall(call, fieldName); 74 | else 75 | this.MyVisitMember(ue.Operand as MemberExpression,fieldName); 76 | } 77 | else 78 | { 79 | parameters.Add(fieldName, new Parameter(ParamPrefix + fieldName, null)); 80 | var constant = node.Expression as ConstantExpression; 81 | if (constant != null) 82 | VisitConstant(constant, fieldName); 83 | else 84 | { 85 | LambdaExpression lambda = Expression.Lambda(node.Expression); 86 | var fn = lambda.Compile(); 87 | VisitConstant(Expression.Constant(fn.DynamicInvoke(null), node.Expression.Type), fieldName); 88 | } 89 | } 90 | return node; 91 | } 92 | 93 | /// 94 | /// 访问匿名类型成员 95 | /// 96 | /// 97 | /// 98 | protected override Expression VisitNew(NewExpression node) 99 | { 100 | if (node.Members != null) 101 | { 102 | foreach (var item in node.Members) 103 | { 104 | Parameters.Add(item.Name, null); 105 | } 106 | } 107 | return base.VisitNew(node); 108 | } 109 | 110 | /// 111 | /// 访问表达式中的函数调用 112 | /// 113 | /// 114 | /// 115 | /// 116 | protected Expression VisitMethodCall(MethodCallExpression node,string fieldName) 117 | { 118 | string sqlMethodName = node.Method.Name.ToLower(); 119 | string callName = SqlMethodNameCallBack.MethodNameCallback(sqlMethodName); 120 | if (!string.IsNullOrEmpty(callName)) 121 | { 122 | parameters.Add(fieldName, new Parameter(callName, callName) { IsMethodType = true }); 123 | } 124 | else 125 | throw new NotSupportedException(string.Format("{0}数据库中不支持函数{1}","SqlServer",sqlMethodName)); 126 | 127 | return base.VisitMethodCall(node); 128 | } 129 | 130 | 131 | internal Dictionary Parameters 132 | { 133 | get { return parameters; } 134 | } 135 | 136 | 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/IOperatorWhere.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Dapper.Extensions 5 | { 6 | /// 7 | /// 条件执行操作类 8 | /// 9 | /// 10 | public interface IOperatorWhere where TModel : class 11 | { 12 | /// 13 | /// 添加条件语句 14 | /// 15 | /// 16 | /// 17 | IOperatorWhere Where(Expression> whereExp); 18 | /// 19 | /// 执行表达式 20 | /// 21 | bool Go(); 22 | 23 | /// 24 | /// 调试用的SQL语句 25 | /// 26 | string SQL { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/OperatorWhereObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq.Expressions; 5 | using System.Text.RegularExpressions; 6 | using Dapper; 7 | using Dapper.Contrib; 8 | 9 | namespace Dapper.Extensions 10 | { 11 | /// 12 | /// 条件执行操作类 13 | ///API采用 .Where(expression).Go()的链式使用方式。 14 | /// 15 | /// 16 | public class OperatorWhereObject : IOperatorWhere where TModel:class 17 | { 18 | internal OperatorWhereObject(IDbConnection p,string sql,List ps ) 19 | { 20 | Provider = p; 21 | Parameters = ps; 22 | _sql = sql; 23 | } 24 | 25 | private string _sql; 26 | private List Parameters { set; get; } 27 | private IDbConnection Provider {set; get; } 28 | /// 29 | /// 执行条件执行的表达式语句 30 | /// 31 | public bool Go() 32 | { 33 | if (Provider == null) 34 | return false; 35 | 36 | var dynParms = new DynamicParameters(); 37 | Parameters.ForEach(p=> dynParms.Add(p.Name,p.Value)); 38 | bool ret = Provider.Execute(this._sql, dynParms) > 0; 39 | 40 | if(Provider.State == ConnectionState.Open) Provider.Close(); 41 | 42 | this._sql = null; 43 | return ret; 44 | } 45 | 46 | /// 47 | /// 在没有执行Go之前生成的SQL语句 48 | /// 49 | public string SQL { 50 | get { return _sql; } 51 | } 52 | 53 | /// 54 | /// 添加条件表达式 55 | /// 56 | /// 表达式 57 | /// 条件执行对象 58 | public IOperatorWhere Where(Expression> whereExp) 59 | { 60 | var translate = new SqlTranslateFormater(); 61 | string whereSql = translate.Translate(whereExp); 62 | _sql += whereSql; 63 | return this; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/PageCondition.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 Dapper.Extensions 8 | { 9 | /// 10 | /// 通用分页输入条件 11 | /// 12 | public class PageCondition 13 | { 14 | /// 15 | /// 通用分页输入条件构造器 16 | /// 17 | /// 表名或SQL语句如1.customer 2.(select * from customer) as t1 18 | /// 当前页数 19 | /// 每页输出的记录数 20 | /// 排序不含'ORDER BY'字符,当@SortType=3时生效,必须指定ASC或DESC,建议在最后加上主键 21 | /// 排序规则(1:单列正序ASC;2:单列倒序DESC;3:多列排序;) 22 | /// 要显示的列名,如果是全部字段则为* 23 | /// 查询条件 不含'WHERE'字符,如t1.Id > 6 5 AND t1.userid > 10000 ,字段应该与表名对应(TableName)如 t1.Id > 6 24 | public PageCondition(string tableName, int pageIndex, int pageSize, string orderField , PageOrderType orderType = PageOrderType.Asc, string fieldNames = "*", string where = null) 25 | { 26 | this.TableName = tableName; 27 | this.Index = pageIndex; 28 | this.Size = pageSize; 29 | this.FieldNames = fieldNames; 30 | this.Where = where; 31 | this.OrderField = orderField; 32 | this.OrderType = orderType; 33 | } 34 | 35 | /// 36 | /// 表名或SQL语句如1.customer 2.(select * from customer) as t1 37 | /// 38 | public string TableName { set; get; } 39 | 40 | /// 41 | /// 要显示的列名,如果是全部字段则为* 42 | /// 43 | public string FieldNames { set; get; } 44 | 45 | /// 46 | /// 查询条件 不含'WHERE'字符,如t1.Id > 6 5 AND t1.userid > 10000 ,字段应该与表名对应(TableName)如 t1.Id > 6 47 | /// 48 | public string Where { set; get; } 49 | /// 50 | /// 排序不含'ORDER BY'字符,当@SortType=3时生效,必须指定ASC或DESC,建议在最后加上主键 51 | /// 52 | public string OrderField { set; get; } 53 | /// 54 | /// 排序规则(1:单列正序ASC;2:单列倒序DESC;3:多列排序;) 55 | /// 56 | public PageOrderType OrderType { set; get; } 57 | /// 58 | /// 当前页数 59 | /// 60 | public int Index { set; get; } 61 | /// 62 | /// 每页输出的记录数 63 | /// 64 | public int Size { set; get; } 65 | 66 | } 67 | 68 | /// 69 | /// 1:单列正序ASC; 70 | /// 2:单列倒序DESC; 71 | /// 3:多列排序; 72 | /// 73 | public enum PageOrderType 74 | { 75 | Asc = 1, 76 | Desc = 2, 77 | Muti =3 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/PageView.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 Dapper.Extensions 8 | { 9 | /// 10 | /// 数据分页模型 11 | /// 12 | /// 返回的分页数据 13 | public class PageView where TModel : class 14 | { 15 | public PageView(IEnumerable data, int count) 16 | { 17 | this.Data = data; 18 | this.Count = count; 19 | } 20 | 21 | /// 22 | /// 分页数据 23 | /// 24 | public IEnumerable Data { set; get; } 25 | 26 | /// 27 | /// 要分页数据源的总条数 28 | /// 29 | public int Count { set; get; } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/Parameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | 4 | namespace Dapper.Extensions 5 | { 6 | /// 7 | /// new 表达式对象中的SQL语句参数, 8 | /// 9 | internal class Parameter 10 | { 11 | public string Name { get; set; } 12 | public object Value { get; set; } 13 | 14 | public bool IsMethodType { set; get; } 15 | 16 | public Parameter() { } 17 | 18 | public Parameter(string name, object value) 19 | { 20 | Name = name; 21 | if(value != null) 22 | Value = value; 23 | } 24 | 25 | public override string ToString() 26 | { 27 | return String.Format(" {0}={1}", Name, Value); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Dapper.Extensions/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Dapper.Extensions")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("f892188e-c751-4bb2-91ba-75b27236c9cd")] 20 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/SQLMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dapper.Extensions 4 | { 5 | /// 6 | /// SQL中支持的函数 7 | /// 8 | public class SQLMethod 9 | { 10 | /// 11 | /// 服务器当前时间 12 | /// 13 | /// 14 | static public DateTime GetDate() 15 | { 16 | return new DateTime(); 17 | } 18 | /// 19 | /// 字段为空 20 | /// 21 | /// 22 | /// 23 | public static bool IsNull(object obj) 24 | { 25 | return obj == null; 26 | } 27 | /// 28 | /// 字段不为空 29 | /// 30 | /// 31 | /// 32 | public static bool IsNotNull(object obj) 33 | { 34 | return obj != null; 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/SqlMapperExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | using Dapper.Contrib.Extensions; 10 | 11 | namespace Dapper.Extensions 12 | { 13 | using Dapper; 14 | using Dapper.Contrib; 15 | using System.Linq.Expressions; 16 | /// 17 | /// Dapper扩展类,扩展支持单表的where语句生成 18 | /// 19 | public static partial class SqlMapperExtensions 20 | { 21 | private static string GetTableName(Type entityType) 22 | { 23 | string tableName = string.Empty; 24 | var attrs = entityType.GetTypeInfo().GetCustomAttributes(typeof(TableAttribute), true).ToArray(); 25 | if (attrs != null && attrs.Length > 0) 26 | tableName = ((TableAttribute)attrs[0]).Name; 27 | else 28 | throw new NotSupportedException("实体类上没有TableAttribute特性!"); 29 | 30 | return tableName; 31 | 32 | } 33 | 34 | 35 | internal static string CreateQuerySQL(Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) where T : class 36 | { 37 | string fieldNames = "*"; 38 | if (fieldExp != null) 39 | { 40 | FieldsFormater format = new FieldsFormater(); 41 | format.Visit(fieldExp); 42 | if (format.Parameters.Count > 0) 43 | { 44 | fieldNames = string.Join(",", format.Parameters.Select(x => x.Key)); 45 | } 46 | } 47 | 48 | var translate = new SqlTranslateFormater(); 49 | string sqlWhere = translate.Translate(whereExp); 50 | 51 | if (sort != null && sort.Count > 0) 52 | { 53 | sqlWhere += $" Order By {sort}"; 54 | } 55 | 56 | string tableName = GetTableName(typeof(T)); 57 | 58 | StringBuilder sqlBuilder = new StringBuilder($"select {fieldNames} from {tableName} where "); 59 | sqlBuilder.Append(sqlWhere); 60 | return sqlBuilder.ToString(); 61 | } 62 | 63 | 64 | internal static string CreateQuerySQLWithActive(Expression> expression, Expression> fieldExp = null, DapperSort sort = null) where T : class 65 | { 66 | string fieldNames = "*"; 67 | if (fieldExp != null) 68 | { 69 | FieldsFormater format = new FieldsFormater(); 70 | format.Visit(fieldExp); 71 | if (format.Parameters.Count > 0) 72 | { 73 | fieldNames = string.Join(",", format.Parameters.Select(x => x.Key)); 74 | } 75 | } 76 | 77 | var translate = new SqlTranslateFormater(); 78 | string sqlWhere = translate.Translate(expression); 79 | 80 | if (sort != null && sort.Count > 0) 81 | { 82 | sqlWhere += $" Order By {sort}"; 83 | } 84 | 85 | string tableName = GetTableName(typeof(T)); 86 | 87 | StringBuilder sqlBuilder = new StringBuilder($"select {fieldNames} from {tableName} where IsActive=1 AND "); 88 | sqlBuilder.Append(sqlWhere); 89 | return sqlBuilder.ToString(); 90 | } 91 | 92 | /// 93 | /// 查找一条数据,并且IsActive=1 94 | /// 95 | /// 表名 96 | /// 数据库连接 97 | /// where表达式 98 | /// 一条实体记录 99 | public static T GetEntityForIsActive(this IDbConnection connection, Expression> expression) where T : class 100 | { 101 | return connection.QueryFirstOrDefault(CreateQuerySQLWithActive(expression)); 102 | } 103 | 104 | 105 | /// 106 | /// 同步查找一条数据 107 | /// 108 | /// 表名 109 | /// 数据库连接 110 | /// where表达式 111 | /// 指定查询字段 112 | /// 113 | /// 一条实体记录 114 | public static T Get(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) where T : class 115 | { 116 | return connection.QueryFirstOrDefault(CreateQuerySQL(whereExp, fieldExp, sort)); 117 | } 118 | 119 | /// 120 | /// 异步查找一条数据 121 | /// 122 | /// 表名 123 | /// 数据库连接 124 | /// where表达式 125 | /// 指定查询字段 126 | /// 127 | /// 一条实体记录 128 | public static Task GetAsync(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) 129 | where T : class 130 | { 131 | return connection.QueryFirstAsync(CreateQuerySQL(whereExp, fieldExp, sort)); 132 | } 133 | 134 | 135 | /// 136 | /// 查找数据集合,并且IsActive=1 137 | /// 138 | /// 表名 139 | /// 数据库连接 140 | /// where表达式 141 | /// 指定查询字段 142 | /// 143 | /// 实体记录集合 144 | public static IEnumerable GetAllForIsActive(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) where T : class 145 | { 146 | return connection.Query(CreateQuerySQLWithActive(whereExp, fieldExp, sort)); 147 | } 148 | 149 | 150 | /// 151 | /// 同步查找数据数据集合 152 | /// 153 | /// 表名 154 | /// 数据库连接 155 | /// where表达式 156 | /// 指定查询字段 157 | /// 158 | /// 实体记录集合 159 | public static IEnumerable GetAll(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) where T : class 160 | { 161 | return connection.Query(CreateQuerySQL(whereExp, fieldExp, sort)); 162 | } 163 | 164 | /// 165 | /// 异步查找数据数据集合 166 | /// 167 | /// 表名 168 | /// 数据库连接 169 | /// where表达式 170 | /// 指定查询字段 171 | /// 172 | /// 实体记录集合 173 | public static Task> GetAllAsync(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) 174 | where T : class 175 | { 176 | return connection.QueryAsync(CreateQuerySQL(whereExp, fieldExp, sort)); 177 | } 178 | 179 | 180 | /// 181 | /// 立即执行更新语句,API采用 Update({new entity expression}).Where(expression).Go()的链式使用方式 182 | /// 183 | /// 模型名称 184 | /// 185 | /// 更新对象表达式,表示要更新的列信息 186 | /// 将返回一个操作接口,此接口会有一个Where方法和Go方法,Where表示要添加条件,Go则表示立即执行语句。 187 | /// session.Update(u => new AdminUser1 {ID = "5", NameA = "maxzhang"}).Where(p => p.Age > 5).Go 188 | public static IOperatorWhere Update(this IDbConnection connection, Expression> objExp) where TModel : class 189 | { 190 | string tableName = GetTableName(typeof(TModel)); 191 | 192 | FieldsFormater format = new FieldsFormater(); 193 | format.Visit(objExp); 194 | string paramterNameAndValues = string.Join(",", format.Parameters.Select(kv => kv.Key + "=" + kv.Value.Name)); 195 | 196 | string template = string.Format("Update {0} SET {1} Where ", tableName, paramterNameAndValues); 197 | var ps = format.Parameters.Values.Where(p => p.IsMethodType == false).ToList(); 198 | 199 | return new OperatorWhereObject(connection, template, ps); 200 | } 201 | 202 | /// 203 | /// 逻辑删除表对应的一条数据记录 204 | /// 205 | /// 表实体 206 | /// 数据库连接 207 | /// 记录ID 208 | /// 是否逻辑删除 209 | /// 是否逻辑删除成功 210 | public static bool SetActive(this IDbConnection connection, dynamic id, bool isActive = false) where TModel : class 211 | { 212 | var modelType = typeof(TModel); 213 | var keyPropertyInfo = modelType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0); 214 | var IsActivePropertyInfo = modelType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(ActiveAttribute), true).Count() > 0); 215 | if (IsActivePropertyInfo == null) 216 | throw new NotSupportedException("实体类没有定义Active特性!"); 217 | 218 | string tableName = GetTableName(typeof(TModel)); 219 | string template = string.Format("Update {0} SET IsActive=@IsActive Where {1}=@Id", tableName, keyPropertyInfo.Name); 220 | var dynParms = new DynamicParameters(); 221 | dynParms.Add("@IsActive", isActive); 222 | dynParms.Add("@Id", id); 223 | 224 | bool ret = connection.Execute(template, dynParms) > 0; 225 | return ret; 226 | } 227 | 228 | /// 229 | /// 逻辑删除表实体对应的一条数据记录 230 | /// 231 | /// 表实体 232 | /// 数据库连接 233 | /// 表实体对象 234 | /// 是否逻辑删除成功 235 | public static bool SetActive(this IDbConnection connection, TModel model) where TModel : class 236 | { 237 | var modelType = typeof(TModel); 238 | var keyPropertyInfo = modelType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0); 239 | var IsActivePropertyInfo = modelType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(ActiveAttribute), true).Count() > 0); 240 | 241 | if (keyPropertyInfo == null) 242 | throw new NotSupportedException("实体类没有定义主键(Key)特性!"); 243 | 244 | if (IsActivePropertyInfo == null) 245 | throw new NotSupportedException("实体类没有定义逻辑删除(Active)特性!"); 246 | 247 | //取得表实体对象的值 id and isactive field 248 | object id = keyPropertyInfo.GetValue(model, null); 249 | object isActive = IsActivePropertyInfo.GetValue(model, null); 250 | 251 | string tableName = GetTableName(typeof(TModel)); 252 | //execute sql 253 | string template = string.Format("Update {0} SET IsActive=@IsActive Where {1}=@Id", tableName, keyPropertyInfo.Name); 254 | var dynParms = new DynamicParameters(); 255 | dynParms.Add("@IsActive", isActive); 256 | dynParms.Add("@Id", id); 257 | 258 | bool ret = connection.Execute(template, dynParms) > 0; 259 | return ret; 260 | } 261 | 262 | 263 | /// 264 | /// 查找一条数据,并且IsActive=1 265 | /// 266 | /// 表名 267 | /// 数据库连接 268 | /// 表记录ID 269 | /// 270 | /// 一条实体记录 271 | public static TModel GetIsActive(this IDbConnection connection, dynamic id, DapperSort sort = null) 272 | { 273 | var modelType = typeof(TModel); 274 | var tableName = GetTableName(modelType); 275 | var keyPropertyInfo = modelType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Count() > 0); 276 | if (keyPropertyInfo == null) 277 | throw new NotSupportedException("实体类没有定义主键(Key)特性!"); 278 | 279 | string template = $"Select * from {tableName} Where {keyPropertyInfo.Name}=@Id AND IsActive=@IsActive"; 280 | if (sort != null && sort.Count > 0) 281 | { 282 | template += string.Format(" Order by {0} ", sort); 283 | } 284 | var dynParms = new DynamicParameters(); 285 | dynParms.Add("@IsActive", 1); 286 | dynParms.Add("@Id", id); 287 | return connection.QueryFirstOrDefault(template, dynParms); 288 | 289 | } 290 | 291 | /// 292 | /// 查找数据集合,并且IsActive=1 293 | /// 294 | /// 表名 295 | /// 数据库连接 296 | /// 297 | /// 实体记录集合 298 | public static IEnumerable GetAllIsActive(this IDbConnection connection, DapperSort sort) 299 | { 300 | var modelType = typeof(TModel); 301 | var tableName = GetTableName(modelType); 302 | string template = $"Select * from {tableName} Where IsActive=@IsActive"; 303 | if (sort != null && sort.Count > 0) 304 | { 305 | template += string.Format(" Order by {0} ", sort); 306 | } 307 | var dynParms = new DynamicParameters(); 308 | dynParms.Add("@IsActive", 1); 309 | return connection.Query(template, dynParms); 310 | } 311 | 312 | 313 | public static PageView GetPager(this IDbConnection connection, PageCondition condition) where TModel : class 314 | { 315 | if (condition == null) throw new ArgumentNullException("condition"); 316 | 317 | var dps = new DynamicParameters(); 318 | dps.Add("@TableName", condition.TableName); 319 | dps.Add("@FieldNames", condition.FieldNames); 320 | 321 | if (!string.IsNullOrEmpty(condition.Where)) 322 | dps.Add("@WhereString", condition.Where); 323 | 324 | dps.Add("@OrderField", condition.OrderField); 325 | dps.Add("@OrderType", Convert.ToInt32(condition.OrderType)); 326 | 327 | dps.Add("@PageIndex", condition.Index); 328 | dps.Add("@PageSize", condition.Size); 329 | 330 | PageView pageModel = null; 331 | 332 | 333 | using (var muti = connection.QueryMultiple("Proc_Common_PagerHelper", dps)) 334 | { 335 | int count = muti.ReadSingle(); 336 | var data = muti.Read(); 337 | pageModel = new PageView(data, count); 338 | } 339 | 340 | return pageModel; 341 | } 342 | 343 | /// 344 | /// 立即执行更新语句,API采用 Update(new {ID = "5" NameA = "imyundong"}).Where(expression).Go()的链式使用方式 345 | /// 346 | /// 模型名称 347 | /// 348 | /// 匿名对象或实体对象 349 | /// session.Update(new {ID = "5", NameA = "imyundong"}).Where(p => p.Age > 5).Go 350 | /// session.Update(new {entity.ID, entity.NameA}).Where(p => p.Age > 5).Go 351 | /// session.Update(entity).Where(p => p.Age > 5).Go 352 | /// 353 | public static IOperatorWhere Update(this IDbConnection connection, object param) 354 | where TModel : class 355 | { 356 | string tableName = GetTableName(typeof(TModel)); 357 | List ps = new List(); 358 | 359 | 360 | IDictionary filesDic = GetObjectValues(param); 361 | List setSql = new List(); 362 | foreach (KeyValuePair f in filesDic) 363 | { 364 | setSql.Add(string.Format("{0}=@{0}", f.Key)); 365 | ps.Add(new Parameter() 366 | { 367 | IsMethodType = false, 368 | Name = "@" + f.Key, 369 | Value = f.Value 370 | }); 371 | } 372 | 373 | string paramterNameAndValues = string.Join(",", setSql); 374 | string template = string.Format("Update {0} SET {1} Where ", tableName, paramterNameAndValues); 375 | 376 | return new OperatorWhereObject(connection, template, ps); 377 | } 378 | 379 | /// 380 | /// 查询数据 381 | /// 382 | /// 383 | /// 384 | /// 查询条件 385 | /// 指定查询字段 386 | /// 排序对象 387 | /// 388 | public static IEnumerable Query(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) where T : class 389 | { 390 | return connection.Query(CreateQuerySQL(whereExp, fieldExp, sort)); 391 | } 392 | 393 | /// 394 | /// 异步查询数据 395 | /// 396 | /// 397 | /// 398 | /// 查询条件 399 | /// 指定查询字段 400 | /// 排序对象 401 | /// 402 | public static Task> QueryAsync(this IDbConnection connection, Expression> whereExp, Expression> fieldExp = null, DapperSort sort = null) where T : class 403 | { 404 | return connection.QueryAsync(CreateQuerySQL(whereExp, fieldExp, sort)); 405 | } 406 | 407 | /// 408 | /// 数据是否存在 409 | /// 410 | /// 411 | /// 412 | /// 查询条件 413 | /// 是否逻辑删除状态,默认否 414 | /// 415 | public static bool Exist(this IDbConnection connection, Expression> expression, bool isActive = false) 416 | { 417 | Type type = typeof(TModel); 418 | string tableName = GetTableName(type); 419 | 420 | var translate = new SqlTranslateFormater(); 421 | string sqlWhere = translate.Translate(expression); 422 | 423 | StringBuilder sqlBuilder = new StringBuilder($"select 1 from {tableName} where "); 424 | if (isActive) 425 | { 426 | sqlBuilder.Append(" IsActive=1 AND "); 427 | } 428 | sqlBuilder.Append(sqlWhere); 429 | 430 | return connection.ExecuteScalar(sqlBuilder.ToString()) > 0; 431 | } 432 | 433 | /// 434 | /// 反射获取匿名类型或实体的属性键和值 435 | /// 436 | /// 437 | /// 438 | /// 439 | public static IDictionary GetObjectValues(object obj, bool filterComputed = true) 440 | { 441 | IDictionary result = new Dictionary(); 442 | if (obj == null) 443 | { 444 | return result; 445 | } 446 | 447 | foreach (var propertyInfo in obj.GetType().GetProperties()) 448 | { 449 | string name = propertyInfo.Name; 450 | object value = propertyInfo.GetValue(obj, null); 451 | if (filterComputed) 452 | { 453 | // 忽略标记Computed特性的属性 454 | var computedAttr = propertyInfo 455 | .GetCustomAttributes(false) 456 | .SingleOrDefault(attr => attr.GetType().Name == "ComputedAttribute") 457 | as dynamic; 458 | if (computedAttr == null) 459 | { 460 | result[name] = value; 461 | } 462 | } 463 | else 464 | { 465 | result[name] = value; 466 | } 467 | } 468 | 469 | 470 | return result; 471 | } 472 | } 473 | } -------------------------------------------------------------------------------- /src/Dapper.Extensions/SqlMethodNameCallBack.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 Dapper.Extensions 8 | { 9 | /// 10 | /// SQL语句中的函数映射 11 | /// 12 | public class SqlMethodNameCallBack 13 | { 14 | /// 15 | /// 返回在SQLMethod类中定义函数在SQLServer中调用名称 16 | /// 17 | /// SQLMethod类的静态函数名 18 | /// SQLServer中的函数名称 19 | public static string MethodNameCallback(string methodName) 20 | { 21 | string retName = string.Empty; 22 | switch (methodName) 23 | { 24 | case "getdate": 25 | retName = "GETDATE()"; 26 | break; 27 | 28 | default: 29 | break; 30 | } 31 | 32 | return retName; 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/SqlTranslateFormater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Text; 6 | using System.Collections.Generic; 7 | using System.Collections.Concurrent; 8 | using System.Reflection; 9 | 10 | namespace Dapper.Extensions 11 | { 12 | /// 13 | /// Maker:张磊 14 | /// 解析整个Linq成SQL语句的入口 15 | /// 16 | public class SqlTranslateFormater : ExpressionVisitor 17 | { 18 | /// 19 | /// 20 | /// 21 | private ConcurrentDictionary Ass = new ConcurrentDictionary(); 22 | /// 23 | /// sql语句变量 24 | /// 25 | StringBuilder sb = null; 26 | StringBuilder ordby = null; 27 | /// 28 | /// 入口 29 | /// 30 | /// 31 | /// 32 | public string Translate(Expression expr) 33 | { 34 | sb = new StringBuilder(); 35 | ordby = new StringBuilder(); 36 | 37 | this.Visit(expr); 38 | return sb.ToString(); 39 | } 40 | 41 | bool isleft = true; 42 | 43 | /// 44 | /// 访问二元操作符, 如 a >= b, 表达式的left 为a , right为b , 表达式的NodeType为 操作符 45 | /// 46 | /// 47 | /// 48 | protected override Expression VisitBinary(BinaryExpression b) 49 | { 50 | isleft = true; 51 | 52 | this.VisitBinaryWithParent(b, b.Left); 53 | 54 | switch (b.NodeType) 55 | { 56 | case ExpressionType.AndAlso: 57 | sb.Append(" AND "); 58 | break; 59 | case ExpressionType.OrElse: 60 | sb.Append(" OR "); 61 | break; 62 | case ExpressionType.GreaterThan: 63 | sb.Append(" > "); 64 | break; 65 | case ExpressionType.LessThan: 66 | sb.Append(" < "); 67 | break; 68 | case ExpressionType.Equal: 69 | sb.Append(" = "); 70 | break; 71 | case ExpressionType.LessThanOrEqual: 72 | sb.Append(" <= "); 73 | break; 74 | case ExpressionType.GreaterThanOrEqual: 75 | sb.Append(" >= "); 76 | break; 77 | case ExpressionType.NotEqual: 78 | sb.Append(" <> "); 79 | break; 80 | } 81 | 82 | isleft = false; 83 | 84 | this.VisitBinaryWithParent(b, b.Right); 85 | 86 | 87 | return b; 88 | } 89 | 90 | //访问父节点,用于优先级计算 91 | private void VisitBinaryWithParent(Expression parent, Expression node) 92 | { 93 | var left = isleft; 94 | if (IsDiffNoteType(parent, node)) 95 | sb.Append("("); 96 | 97 | var bnode = node as BinaryExpression; 98 | 99 | if (bnode != null && bnode.Left is BinaryExpression) 100 | this.VisitBinaryWithParent(node, bnode.Left); 101 | else 102 | { 103 | isleft = true; 104 | if (bnode != null) 105 | this.Visit(bnode.Left); 106 | else 107 | this.Visit(node); 108 | isleft = left; 109 | } 110 | 111 | switch (node.NodeType) 112 | { 113 | case ExpressionType.AndAlso: 114 | sb.Append(" AND "); 115 | break; 116 | case ExpressionType.OrElse: 117 | sb.Append(" OR "); 118 | break; 119 | case ExpressionType.GreaterThan: 120 | sb.Append(" > "); 121 | break; 122 | case ExpressionType.LessThan: 123 | sb.Append(" < "); 124 | break; 125 | case ExpressionType.Equal: 126 | sb.Append(" = "); 127 | break; 128 | case ExpressionType.LessThanOrEqual: 129 | sb.Append(" <= "); 130 | break; 131 | case ExpressionType.GreaterThanOrEqual: 132 | sb.Append(" >= "); 133 | break; 134 | case ExpressionType.NotEqual: 135 | sb.Append(" <> "); 136 | break; 137 | default: 138 | return; 139 | } 140 | 141 | if (bnode != null && bnode.Right is BinaryExpression) 142 | this.VisitBinaryWithParent(node, bnode.Right); 143 | else 144 | { 145 | isleft = false; 146 | if (bnode != null) 147 | this.Visit(bnode.Right); 148 | else 149 | this.Visit(node); 150 | isleft = left; 151 | } 152 | 153 | if (IsDiffNoteType(parent, node)) 154 | sb.Append(")"); 155 | } 156 | 157 | //得到表达式中c.ID中的c 158 | private void GetParentMemberName(Expression node) 159 | { 160 | if (node.NodeType == ExpressionType.MemberAccess) 161 | { 162 | var memberEx = node as MemberExpression; 163 | GetParentMemberName(memberEx.Expression); 164 | sb.Append(memberEx.Member.Name); 165 | sb.Append("."); 166 | } 167 | } 168 | 169 | //看上一个节点和下一个是不是一样的操作符 170 | private bool IsDiffNoteType(Expression parent, Expression children) 171 | { 172 | if (IsOpertaorType(parent) && IsOpertaorType(children)) 173 | { 174 | if (parent.NodeType == children.NodeType) 175 | return false; 176 | else 177 | return true; 178 | } 179 | else 180 | { 181 | 182 | return false; 183 | } 184 | 185 | } 186 | 187 | bool IsOpertaorType(Expression node) 188 | { 189 | switch (node.NodeType) 190 | { 191 | case ExpressionType.AndAlso: 192 | case ExpressionType.OrElse: 193 | return true; 194 | default: 195 | return false; 196 | } 197 | } 198 | 199 | 200 | 201 | 202 | /// 203 | /// 访问成员 204 | /// 205 | /// 206 | /// 207 | protected override Expression VisitMember(MemberExpression node) 208 | { 209 | if (node.Expression != null && (node.Expression.NodeType == ExpressionType.Parameter || node.Expression.NodeType == ExpressionType.MemberAccess) && isleft) 210 | { 211 | bool isAsName = false; 212 | var pse = node.Expression as ParameterExpression; 213 | if (pse != null && pse.Type == node.Member.DeclaringType) 214 | { 215 | if (!Ass.Keys.Contains(pse.Type)) 216 | Ass.TryAdd(pse.Type, pse.Name); 217 | isAsName = true; 218 | } 219 | string ts; 220 | if (node.Member.DeclaringType != null && (!isAsName && !Ass.TryGetValue(node.Member.DeclaringType, out ts))) 221 | GetParentMemberName(node.Expression); 222 | 223 | var fieldName = node.Member.Name; 224 | 225 | string tn = string.Empty; 226 | //成员表达式的别名 如 c.Name 227 | //if (Ass.TryGetValue(node.Member.DeclaringType,out tn)) 228 | //{ 229 | // sb.Append(tn); 230 | // sb.Append("."); 231 | //} 232 | 233 | sb.Append(fieldName); 234 | return node; 235 | } 236 | else 237 | { 238 | LambdaExpression lambda = Expression.Lambda(node); 239 | var fn = lambda.Compile(); 240 | this.Visit(Expression.Constant(fn.DynamicInvoke(null), node.Type)); 241 | 242 | } 243 | 244 | return node; 245 | } 246 | /// 247 | /// 访问常量 248 | /// 249 | /// 250 | /// 251 | protected override Expression VisitConstant(ConstantExpression c) 252 | { 253 | IQueryable q = c.Value as IQueryable; 254 | if (q != null) 255 | { 256 | 257 | var tableName = q.ElementType.Name; 258 | 259 | sb.Append(tableName); 260 | 261 | sb.Append(" "); 262 | if (Ass.Keys.Count > 0) 263 | sb.Append(Ass[q.ElementType]); 264 | else 265 | sb.Append("T"); 266 | } 267 | else if (c.Value == null) 268 | { 269 | sb.Append("NULL"); 270 | } 271 | else 272 | { 273 | switch (Type.GetTypeCode(c.Value.GetType())) 274 | { 275 | case TypeCode.Boolean: 276 | sb.Append(((bool)c.Value) ? 1 : 0); 277 | break; 278 | case TypeCode.DateTime: 279 | string dtfs = "'{0}'"; 280 | sb.AppendFormat(dtfs, c.Value); 281 | break; 282 | case TypeCode.String: 283 | sb.Append("'"); 284 | sb.Append(c.Value); 285 | sb.Append("'"); 286 | break; 287 | case TypeCode.Object: 288 | #if COREFX 289 | if (c.Type.GetTypeInfo().GetInterface("IEnumerable", false) != null) 290 | #else 291 | if (c.Type.GetInterface("IEnumerable", false) != null) 292 | #endif 293 | { 294 | List list = new List(); 295 | foreach (var item in (IEnumerable)c.Value) 296 | { 297 | list.Add(item); 298 | } 299 | 300 | if (list.Count == 0) 301 | throw new InvalidOperationException($"{c.Value}语句中的数组数据不能为空值。"); 302 | object elementOne = list[0]; 303 | Type elementType = elementOne.GetType(); 304 | 305 | if (Type.GetTypeCode(elementType) == TypeCode.Boolean) 306 | throw new NotSupportedException($"{c.Value}不支持包含多个bool类型,请使用是否相等作为条件"); 307 | 308 | string format = "{0}"; 309 | 310 | // 字符类型、时间类型、GUID类型单独处理 311 | if (elementType == typeof(string) || elementType == typeof(DateTime) || elementType == typeof(Guid)) 312 | format = "'{0}'"; 313 | 314 | StringBuilder value = new StringBuilder(); 315 | value.Append("("); 316 | for (int i = 0; i < list.Count; i++) 317 | { 318 | value.AppendFormat(format, list[i]); 319 | if (i != list.Count - 1) 320 | value.Append(","); 321 | } 322 | value.Append(")"); 323 | sb.Append(value); 324 | } 325 | else if (c.Type == typeof(Guid)) 326 | { 327 | sb.Append("'"); 328 | sb.Append(c.Value); 329 | sb.Append("'"); 330 | } 331 | else 332 | throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value)); 333 | break; 334 | default: 335 | sb.Append(c.Value); 336 | break; 337 | } 338 | } 339 | return c; 340 | } 341 | /// 342 | /// 访问转换 343 | /// 344 | /// 345 | /// 346 | protected override Expression VisitUnary(UnaryExpression node) 347 | { 348 | if (node.NodeType == ExpressionType.Convert) 349 | this.Visit(node.Operand); 350 | return node; 351 | } 352 | 353 | 354 | /// 355 | /// 访问方法 356 | /// 357 | /// 358 | /// 359 | protected override Expression VisitMethodCall(MethodCallExpression node) 360 | { 361 | var methodInfo = node.Method; 362 | 363 | if (methodInfo.DeclaringType == typeof(SQLMethod)) 364 | { 365 | bool tleft = isleft; 366 | isleft = true; 367 | if (node.Arguments.Count > 0) 368 | this.Visit(node.Arguments[0]); 369 | isleft = tleft; 370 | switch (methodInfo.Name) 371 | { 372 | case "IsNull": 373 | sb.Append(" is NULL"); 374 | break; 375 | case "IsNotNull": 376 | sb.Append(" is NOT NULL"); 377 | break; 378 | default: 379 | string methodName = methodInfo.Name.ToLower(); 380 | string funcName = SqlMethodNameCallBack.MethodNameCallback(methodName); 381 | 382 | if (!string.IsNullOrEmpty(funcName)) 383 | sb.Append(funcName); 384 | else 385 | throw new NotSupportedException( 386 | string.Format("{0}数据库中不支持函数{1}", "sqlserver", methodName)); 387 | break; 388 | } 389 | 390 | 391 | 392 | } 393 | 394 | 395 | if (methodInfo.DeclaringType == typeof(string)) 396 | { 397 | switch (methodInfo.Name) 398 | { 399 | case "Contains": 400 | VisitStringFuncByOneParamter(node, 401 | v => string.Format("%{0}%", v)); 402 | break; 403 | case "StartsWith": 404 | VisitStringFuncByOneParamter(node, 405 | v => string.Format("{0}%", v)); 406 | break; 407 | case "EndsWith": 408 | VisitStringFuncByOneParamter(node, 409 | v => string.Format("%{0}", v)); 410 | break; 411 | } 412 | } 413 | if (methodInfo.DeclaringType == typeof(Enumerable)) 414 | if (methodInfo.DeclaringType == typeof(Enumerable)) 415 | { 416 | this.Visit(node.Arguments[1]); 417 | sb.Append(" In "); 418 | this.Visit(node.Arguments[0]); 419 | } 420 | #if COREFX 421 | if (methodInfo.DeclaringType.GetTypeInfo().GetInterface("IEnumerable", false) != null) 422 | #else 423 | if (methodInfo.DeclaringType?.GetInterface("IEnumerable", false) != null) 424 | #endif 425 | { 426 | this.Visit(node.Arguments[0]); 427 | sb.Append(" In "); 428 | this.Visit(node.Object); 429 | } 430 | 431 | if (methodInfo.DeclaringType == typeof(System.Convert)) 432 | { 433 | this.Visit(node.Arguments[0]); 434 | } 435 | 436 | 437 | return node; 438 | } 439 | 440 | private static Expression StripQuotes(Expression e) 441 | { 442 | 443 | while (e.NodeType == ExpressionType.Quote) 444 | { 445 | 446 | e = ((UnaryExpression)e).Operand; 447 | 448 | } 449 | 450 | return e; 451 | 452 | } 453 | 454 | 455 | /// 456 | /// 访问String成员函数中有一个参数和返回值的方法 457 | /// 此方法 只用于生成Like SQL表达式 。 执行的方法名:Contains , StartWith ,EndWith 458 | /// 459 | /// 460 | private void VisitStringFuncByOneParamter(MethodCallExpression node, Func valueFunc) 461 | { 462 | this.Visit(node.Object); 463 | sb.Append(" Like "); 464 | var right = node.Arguments[0]; 465 | this.ChangeMethodParamExpression(ref right, valueFunc); 466 | this.Visit(right); 467 | } 468 | 469 | /// 470 | /// 修改MethodExpression表达式中的参数表达式,使其值改变为valueFunc函数的结果 471 | /// 472 | /// 参数表达式 473 | /// 函数 : string->string 474 | private void ChangeMethodParamExpression(ref Expression right, Func valueFunc) 475 | { 476 | if (right.NodeType == ExpressionType.Constant) 477 | { 478 | var value = (right as ConstantExpression).Value; 479 | 480 | right = Expression.Constant(valueFunc(value), typeof(string)); 481 | } 482 | else if (right.NodeType == ExpressionType.MemberAccess) 483 | { 484 | LambdaExpression lambda = Expression.Lambda(right); 485 | var fn = lambda.Compile(); 486 | var value = fn.DynamicInvoke(null); 487 | right = Expression.Constant(valueFunc(value), typeof(string)); 488 | } 489 | } 490 | 491 | /// 492 | /// 访问对象构造函数 493 | /// 494 | /// 495 | /// 496 | protected override Expression VisitNew(NewExpression node) 497 | { 498 | if (node.Type == typeof(DateTime)) 499 | { 500 | LambdaExpression lambda = Expression.Lambda(node); 501 | var fn = lambda.Compile(); 502 | var value = fn.DynamicInvoke(null); 503 | this.Visit(Expression.Constant(value, typeof(DateTime))); 504 | } 505 | else 506 | { 507 | string names = String.Join(",", node.Members.Select(m => m.Name).ToArray()); 508 | sb.Append(names); 509 | } 510 | return node; 511 | } 512 | 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /src/Dapper.Extensions/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Dapper.Extensions", 3 | "copyright": "YOYOFx-Maxzhang", 4 | "description": "YOYOFx's Dapper.Extensions", 5 | "language": "zh-Hans", 6 | "version": "1.0.0-*", 7 | 8 | "dependencies": { 9 | "Dapper": "1.50.2", 10 | "Dapper.Contrib": "1.50.0", 11 | "NETStandard.Library": "1.6.0", 12 | "System.Data.Common": "4.1.0" 13 | 14 | }, 15 | 16 | "frameworks": { 17 | "netstandard1.6": { 18 | "buildOptions": { 19 | "define": [ "ASYNC", "COREFX" ] 20 | }, 21 | "imports": "dnxcore50" 22 | }, 23 | "net451": {} 24 | } 25 | } 26 | --------------------------------------------------------------------------------