【原创】打造基于Dapper的数据访问层
【原创】打造基于Dapper的数据访问层
- 前言
闲来无事,花几天功夫将之前项目里用到的一个数据访问层整理了出来。实现单个实体的增删改查,可执行存储过程,可输出返回参数,查询结果集可根据实际情况返回DataTable、DataSet和强类型,同时支持不同类型数据库。目前成熟的ORM框架多不胜数,再写一个出来,并非想证明自己写的有多好,一来认为现有成熟的ORM框架并不能灵活适用于大型ERP项目,二来有感于工作多年有必要写下一些东西。虽然有种重复造轮子的感觉,但相信朋友们和我一样,享受造轮子的过程并把它当成一种乐趣,对吧。
- 调用示例
1、LinQ 语法查询
1 //LinQ 语法查询
2 query = rptBase.Query<Bas_Company>();
3 query = rptBase.Query<Bas_Company>(x => true);
4 query = rptBase.Query<Bas_Company>(x => true && (x.CompanyID ?? null) != null && new[] { "1", "2" }.Contains(x.CompanyID) &&
5 x.CompanyID.Substring(2, 5).TrimEnd() == "OK" && x.AllowUsed);
2、分页查询
1 //分页查询 2 query = rptBase.Query<Bas_Company>(new PageInfo(3, 20)); 3 query = rptBase.Query<Bas_Company>(new PageInfo(3, 20), x => x.CompanyID == "FT");
3、自定义脚本查询
1 d = new DynamicParameters();
2 d.Add("CompanyName", "美之源科技有限公司", DbType.String, null, 20);
3 query = rptBase.Query<Bas_Company>("Select * From Bas_Company WHERE CompanyName = @CompanyName", d);
4、自定义参数查询
1 //自定义参数查询
2 d = new DynamicParameters();
3 d.Add("CompanyName", "美之源科技有限公司");
4 query = rptBase.Query<Bas_Company>("selectByName", null, d);
5、带返回值查询
1 //带返回值查询
2 d = new DynamicParameters();
3 d.Add("Row", null);
4 table = rptBase.QueryDataTable<Bas_Company>("returnValue", x => x.CompanyID != "FT", d);
5 eff = d.Get<int?>("Row");
6、自定义实体查询
1 //查询自定义实体 2 var query1 = rptBase.Query<ThinEntity>(typeof(Bas_Company).FullName, "thinEntity", "And CompanyID <> 'FT' ");
7、DataTable 查询
1 DataTable table = null; 2 table = rptBase.QueryDataTable<Bas_Company>(); 3 table = rptBase.QueryDataTable<Bas_Company>(x => true);
8、DataSet 查询
1 DataSet data = null;
2 data = rptBase.QueryDataSet<Bas_Company>("Select",x => true);
9、增删改
1 //新增
2 Bas_Company company = new Bas_Company();
3 company.CompanyID = "TH";
4 company.CompanyCode = "TH001";
5 rptBase.Insert(company);
6
7 //修改
8 company.CompanyCode = "TH00x";
9 rptBase.Update(company);
10 //批量修改
11 rptBase.Update<Bas_Company>(x => new Bas_Company { CompanyCode = "TH003" }, x => x.CompanyID == "TH");
12
13 //删除
14 rptBase.Delete(company);
10、解析成字符串
1 sql = rptBase.Resolve<Bas_Company>("Select",x => true && (x.CompanyID ?? null) != null && new[] { "1", "2" }.Contains(x.CompanyID) &&
2 x.CompanyID.Substring(2, 5).TrimEnd() == "OK" && x.AllowUsed);
3 sqlList.Add(sql);
还有其它的重载这里就不一一列举,源代码的单元测试里有很详细的说明。
- 详细解析
先来看看项目架构截图:

03.Src:第三方开源组件源码,有的目前没有用到,先收藏着;
04.Infrastructure:整个项目公用类库,包含一些Helper和公用类;
05.DataAccess:数据访问核心类库,实现Lambda表达式解析、Dapper封装等;
06.Model:实体层,实体使用CodeSmith生成,包含实体和脚本映射两个部分;
09.Presentation:展示层,目前只有一个单元测试项目。
Dapper,一个基于IDbConnection扩展的完全开源的轻、小、快的ORM框架(项目源码 https://github.com/SamSaffron/dapper-dot-net )。在本框架中,需要解决的主要问题有如下几点:
- 如何生成Dapper查询所需要的TSQL脚本和参数
- 如何将Lambda表达式解析成查询条件
- 如何将Dapper返回的IDataReader转化成DataTable和DataSet
- 如何解决多数据库的问题
--------------------------------------- 华丽丽的分割线 --------------------------------------------
1. 如何生成Dapper查询所需要的SQL语句和参数
基于SQL和代码分离原则,数据库中每一张表都有一个POCO实体与之对应并且用一个Xml文件来描述,包括表名称、字段、主键和增删改查SQL及参数。因为Xml文件的结构都是一样的,我这里用CodeSmith Studio来自动生成。CodeSmith的语法跟Asp.Net的语法类似,这里 http://blog.csdn.net/mapdigit/article/category/1264541 有比较全面的学习资源,本文不做过多着墨。另外若有自定义SQL需求,则需要把自定义Xml文件放到另外目录,以免被CodeSmith覆盖。Xml文件结构如下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <EntityMapper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 3 <TableType> 4 <TableName>Bas_Bank</TableName> 5 <TypeFullName>XFramework.Model.Bas_Bank</TypeFullName> 6 </TableType> 7 <Properties> 8 <Property> 9 <Name>CompanyID</Name> 10 <DbType>AnsiString</DbType> 11 <NativeType>varchar</NativeType> 12 <Precision>0</Precision> 13 <Scale>0</Scale> 14 <Size>10</Size> 15 </Property> 16 <Property> 17 <Name>BankID</Name> 18 <DbType>AnsiString</DbType> 19 <NativeType>varchar</NativeType> 20 <Precision>0</Precision> 21 <Scale>0</Scale> 22 <Size>20</Size> 23 </Property> 24 <Property> 25 <Name>BankCode</Name> 26 <DbType>String</DbType> 27 <NativeType>nvarchar</NativeType> 28 <Precision>0</Precision> 29 <Scale>0</Scale> 30 <Size>20</Size> 31 </Property> 32 <Property> 33 <Name>BankName</Name> 34 <DbType>String</DbType> 35 <NativeType>nvarchar</NativeType> 36 <Precision>0</Precision> 37 <Scale>0</Scale> 38 <Size>40</Size> 39 </Property> 40 <Property> 41 <Name>SWIFT</Name> 42 <DbType>String</DbType> 43 <NativeType>nvarchar</NativeType> 44 <Precision>0</Precision> 45 <Scale>0</Scale> 46 <Size>20</Size> 47 </Property> 48 <Property> 49 <Name>AreaID</Name> 50 <DbType>AnsiString</DbType> 51 <NativeType>varchar</NativeType> 52 <Precision>0</Precision> 53 <Scale>0</Scale> 54 <Size>19</Size> 55 </Property> 56 <Property> 57 <Name>Address</Name> 58 <DbType>String</DbType> 59 <NativeType>nvarchar</NativeType> 60 <Precision>0</Precision> 61 <Scale>0</Scale> 62 <Size>100</Size> 63 </Property> 64 <Property> 65 <Name>Phone</Name> 66 <DbType>AnsiString</DbType> 67 <NativeType>varchar</NativeType> 68 <Precision>0</Precision> 69 <Scale>0</Scale> 70 <Size>60</Size> 71 </Property> 72 <Property> 73 <Name>ParentID</Name> 74 <DbType>AnsiString</DbType> 75 <NativeType>varchar</NativeType> 76 <Precision>0</Precision> 77 <Scale>0</Scale> 78 <Size>20</Size> 79 </Property> 80 <Property> 81 <Name>Level</Name> 82 <DbType>Int32</DbType> 83 <NativeType>int</NativeType> 84 <Precision>10</Precision> 85 <Scale>0</Scale> 86 <Size>4</Size> 87 </Property> 88 <Property> 89 <Name>IsDetail</Name> 90 <DbType>Boolean</DbType> 91 <NativeType>bit</NativeType> 92 <Precision>1</Precision> 93 <Scale>0</Scale> 94 <Size>1</Size> 95 </Property> 96 <Property> 97 <Name>FullName</Name> 98 <DbType>String</DbType> 99 <NativeType>nvarchar</NativeType> 100 <Precision>0</Precision> 101 <Scale>0</Scale> 102 <Size>100</Size> 103 </Property> 104 <Property> 105 <Name>FullParentID</Name> 106 <DbType>String</DbType> 107 <NativeType>nvarchar</NativeType> 108 <Precision>0</Precision> 109 <Scale>0</Scale> 110 <Size>80</Size> 111 </Property> 112 <Property> 113 <Name>ModifyDTM</Name> 114 <DbType>DateTime</DbType> 115 <NativeType>datetime</NativeType> 116 <Precision>23</Precision> 117 <Scale>3</Scale> 118 <Size>8</Size> 119 </Property> 120 <Property> 121 <Name>Remark</Name> 122 <DbType>String</DbType> 123 <NativeType>nvarchar</NativeType> 124 <Precision>0</Precision> 125 <Scale>0</Scale> 126 <Size>200</Size> 127 </Property> 128 <Property> 129 <Name>AllowUsed</Name> 130 <DbType>Boolean</DbType> 131 <NativeType>bit</NativeType> 132 <Precision>1</Precision> 133 <Scale>0</Scale> 134 <Size>1</Size> 135 </Property> 136 </Properties> 137 <Keys> 138 <Property> 139 <Name>CompanyID</Name> 140 <DbType>AnsiString</DbType> 141 <NativeType>varchar</NativeType> 142 <Precision>0</Precision> 143 <Scale>0</Scale> 144 <Size>10</Size> 145 </Property> 146 <Property> 147 <Name>BankID</Name> 148 <DbType>AnsiString</DbType> 149 <NativeType>varchar</NativeType> 150 <Precision>0</Precision> 151 <Scale>0</Scale> 152 <Size>20</Size> 153 </Property> 154 </Keys> 155 <Commands> 156 <Command> 157 <Key>Select</Key> 158 <CommandType>Text</CommandType> 159 <Text> 160 SELECT 161 [CompanyID], 162 [BankID], 163 [BankCode], 164 [BankName], 165 [SWIFT], 166 [AreaID], 167 [Address], 168 [Phone], 169 [ParentID], 170 [Level], 171 [IsDetail], 172 [FullName], 173 [FullParentID], 174 [ModifyDTM], 175 [Remark], 176 [AllowUsed] 177 FROM [Bas_Bank] 178 WHERE 1=1 #WHERE# 179 </Text> 180 </Command> 181 <Command> 182 <Key>SelectByPaging</Key> 183 <CommandType>Text</CommandType> 184 <Text> 185 SELECT 186 [CompanyID], 187 [BankID], 188 [BankCode], 189 [BankName], 190 [SWIFT], 191 [AreaID], 192 [Address], 193 [Phone], 194 [ParentID], 195 [Level], 196 [IsDetail], 197 [FullName], 198 [FullParentID], 199 [ModifyDTM], 200 [Remark], 201 [AllowUsed], 202 [XRecordCount], 203 [XRowNum] 204 FROM( 205 SELECT 206 [CompanyID], 207 [BankID], 208 [BankCode], 209 [BankName], 210 [SWIFT], 211 [AreaID], 212 [Address], 213 [Phone], 214 [ParentID], 215 [Level], 216 [IsDetail], 217 [FullName], 218 [FullParentID], 219 [ModifyDTM], 220 [Remark], 221 [AllowUsed], 222 Count(*) Over() as [XRecordCount], 223 Row_Number() Over(Order By [CompanyID],[BankID],[BankCode],[BankName],[SWIFT],[AreaID],[Address],[Phone],[ParentID],[Level],[IsDetail],[FullName],[FullParentID],[ModifyDTM],[Remark],[AllowUsed]) as [XRowNum] 224 FROM [Bas_Bank] 225 WHERE 1=1 #WHERE# 226 ) a WHERE [XRowNum] BETWEEN #BETWEEN# 227 </Text> 228 </Command> 229 <Command> 230 <Key>SelectByKey</Key> 231 <CommandType>Text</CommandType> 232 <Text> 233 SELECT 234 [CompanyID], 235 [BankID], 236 [BankCode], 237 [BankName], 238 [SWIFT], 239 [AreaID], 240 [Address], 241 [Phone], 242 [ParentID], 243 [Level], 244 [IsDetail], 245 [FullName], 246 [FullParentID], 247 [ModifyDTM], 248 [Remark], 249 [AllowUsed] 250 FROM [Bas_Bank] 251 WHERE 1=1 252 And [CompanyID]=@CompanyID 253 And [BankID]=@BankID 254 </Text> 255 <Parameters> 256 <Parameter> 257 <Name>CompanyID</Name> 258 <DbType>AnsiString</DbType> 259 <NativeType>varchar</NativeType> 260 <Precision>0</Precision> 261 <Scale>0</Scale> 262 <Size>10</Size> 263 </Parameter> 264 <Parameter> 265 <Name>BankID</Name> 266 <DbType>AnsiString</DbType> 267 <NativeType>varchar</NativeType> 268 <Precision>0</Precision> 269 <Scale>0</Scale> 270 <Size>20</Size> 271 </Parameter> 272 </Parameters> 273 </Command> 274 <Command> 275 <Key>Update</Key> 276 <CommandType>Text</CommandType> 277 <Text> 278 UPDATE [Bas_Bank] SET 279 [BankCode] = @BankCode, 280 [BankName] = @BankName, 281 [SWIFT] = @SWIFT, 282 [AreaID] = @AreaID, 283 [Address] = @Address, 284 [Phone] = @Phone, 285 [ParentID] = @ParentID, 286 [Level] = @Level, 287 [IsDetail] = @IsDetail, 288 [FullName] = @FullName, 289 [FullParentID] = @FullParentID, 290 [ModifyDTM] = @ModifyDTM, 291 [Remark] = @Remark, 292 [AllowUsed] = @AllowUsed 293 WHERE 1=1 #WHERE# 294 </Text> 295 <Parameters> 296 <Parameter> 297 <Name>BankCode</Name> 298 <DbType>String</DbType> 299 <NativeType>nvarchar</NativeType> 300 <Precision>0</Precision> 301 <Scale>0</Scale> 302 <Size>20</Size> 303 </Parameter> 304 <Parameter> 305 <Name>BankName</Name> 306 <DbType>String</DbType> 307 <NativeType>nvarchar</NativeType> 308 <Precision>0</Precision> 309 <Scale>0</Scale> 310 <Size>40</Size> 311 </Parameter> 312 <Parameter> 313 <Name>SWIFT</Name> 314 <DbType>String</DbType> 315 <NativeType>nvarchar</NativeType> 316 <Precision>0</Precision> 317 <Scale>0</Scale> 318 <Size>20</Size> 319 </Parameter> 320 <Parameter> 321 <Name>AreaID</Name> 322 <DbType>AnsiString</DbType> 323 <NativeType>varchar</NativeType> 324 <Precision>0</Precision> 325 <Scale>0</Scale> 326 <Size>19</Size> 327 </Parameter> 328 <Parameter> 329 <Name>Address</Name> 330 <DbType>String</DbType> 331 <NativeType>nvarchar</NativeType> 332 <Precision>0</Precision> 333 <Scale>0</Scale> 334 <Size>100</Size> 335 </Parameter> 336 <Parameter> 337 <Name>Phone</Name> 338 <DbType>AnsiString</DbType> 339 <NativeType>varchar</NativeType> 340 <Precision>0</Precision> 341 <Scale>0</Scale> 342 <Size>60</Size> 343 </Parameter> 344 <Parameter> 345 <Name>ParentID</Name> 346 <DbType>AnsiString</DbType> 347 <NativeType>varchar</NativeType> 348 <Precision>0</Precision> 349 <Scale>0</Scale> 350 <Size>20</Size> 351 </Parameter> 352 <Parameter> 353 <Name>Level</Name> 354 <DbType>Int32</DbType> 355 <NativeType>int</NativeType> 356 <Precision>10</Precision> 357 <Scale>0</Scale> 358 <Size>4</Size> 359 </Parameter> 360 <Parameter> 361 <Name>IsDetail</Name> 362 <DbType>Boolean</DbType> 363 <NativeType>bit</NativeType> 364 <Precision>1</Precision> 365 <Scale>0</Scale> 366 <Size>1</Size> 367 </Parameter> 368 <Parameter> 369 <Name>FullName</Name> 370 <DbType>String</DbType> 371 <NativeType>nvarchar</NativeType> 372 <Precision>0</Precision> 373 <Scale>0</Scale> 374 <Size>100</Size> 375 </Parameter> 376 <Parameter> 377 <Name>FullParentID</Name> 378 <DbType>String</DbType> 379 <NativeType>nvarchar</NativeType> 380 <Precision>0</Precision> 381 <Scale>0</Scale> 382 <Size>80</Size> 383 </Parameter> 384 <Parameter> 385 <Name>ModifyDTM</Name> 386 <DbType>DateTime</DbType> 387 <NativeType>datetime</NativeType> 388 <Precision>23</Precision> 389 <Scale>3</Scale> 390 <Size>8</Size> 391 </Parameter> 392 <Parameter> 393 <Name>Remark</Name> 394 <DbType>String</DbType> 395 <NativeType>nvarchar</NativeType> 396 <Precision>0</Precision> 397 <Scale>0</Scale> 398 <Size>200</Size> 399 </Parameter> 400 <Parameter> 401 <Name>AllowUsed</Name> 402 <DbType>Boolean</DbType> 403 <NativeType>bit</NativeType> 404 <Precision>1</Precision> 405 <Scale>0</Scale> 406 <Size>1</Size> 407 </Parameter> 408 </Parameters> 409 </Command> 410 <Command> 411 <Key>UpdateByKey</Key> 412 <CommandType>Text</CommandType> 413 <Text> 414 UPDATE [Bas_Bank] SET 415 [BankCode] = @BankCode, 416 [BankName] = @BankName, 417 [SWIFT] = @SWIFT, 418 [AreaID] = @AreaID, 419 [Address] = @Address, 420 [Phone] = @Phone, 421 [ParentID] = @ParentID, 422 [Level] = @Level, 423 [IsDetail] = @IsDetail, 424 [FullName] = @FullName, 425 [FullParentID] = @FullParentID, 426 [ModifyDTM] = @ModifyDTM, 427 [Remark] = @Remark, 428 [AllowUsed] = @AllowUsed 429 WHERE 1=1 430 And [CompanyID]=@CompanyID 431 And [BankID]=@BankID 432 </Text> 433 <Parameters> 434 <Parameter> 435 <Name>BankCode</Name> 436 <DbType>String</DbType> 437 <NativeType>nvarchar</NativeType> 438 <Precision>0</Precision> 439 <Scale>0</Scale> 440 <Size>20</Size> 441 </Parameter> 442 <Parameter> 443 <Name>BankName</Name> 444 <DbType>String</DbType> 445 <NativeType>nvarchar</NativeType> 446 <Precision>0</Precision> 447 <Scale>0</Scale> 448 <Size>40</Size> 449 </Parameter> 450 <Parameter> 451 <Name>SWIFT</Name> 452 <DbType>String</DbType> 453 <NativeType>nvarchar</NativeType> 454 <Precision>0</Precision> 455 <Scale>0</Scale> 456 <Size>20</Size> 457 </Parameter> 458 <Parameter> 459 <Name>AreaID</Name> 460 <DbType>AnsiString</DbType> 461 <NativeType>varchar</NativeType> 462 <Precision>0</Precision> 463 <Scale>0</Scale> 464 <Size>19</Size> 465 </Parameter> 466 <Parameter> 467 <Name>Address</Name> 468 <DbType>String</DbType> 469 <NativeType>nvarchar</NativeType> 470 <Precision>0</Precision> 471 <Scale>0</Scale> 472 <Size>100</Size> 473 </Parameter> 474 <Parameter> 475 <Name>Phone</Name> 476 <DbType>AnsiString</DbType> 477 <NativeType>varchar</NativeType> 478 <Precision>0</Precision> 479 <Scale>0</Scale> 480 <Size>60</Size> 481 </Parameter> 482 <Parameter> 483 <Name>ParentID</Name> 484 <DbType>AnsiString</DbType> 485 <NativeType>varchar</NativeType> 486 <Precision>0</Precision> 487 <Scale>0</Scale> 488 <Size>20</Size> 489 </Parameter> 490 <Parameter> 491 <Name>Level</Name> 492 <DbType>Int32</DbType> 493 <NativeType>int</NativeType> 494 <Precision>10</Precision> 495 <Scale>0</Scale> 496 <Size>4</Size> 497 </Parameter> 498 <Parameter> 499 <Name>IsDetail</Name> 500 <DbType>Boolean</DbType> 501 <NativeType>bit</NativeType> 502 <Precision>1</Precision> 503 <Scale>0</Scale> 504 <Size>1</Size> 505 </Parameter> 506 <Parameter> 507 <Name>FullName</Name> 508 <DbType>String</DbType> 509 <NativeType>nvarchar</NativeType> 510 <Precision>0</Precision> 511 <Scale>0</Scale> 512 <Size>100</Size> 513 </Parameter> 514 <Parameter> 515 <Name>FullParentID</Name> 516 <DbType>String</DbType> 517 <NativeType>nvarchar</NativeType> 518 <Precision>0</Precision> 519 <Scale>0</Scale> 520 <Size>80</Size> 521 </Parameter> 522 <Parameter> 523 <Name>ModifyDTM</Name> 524 <DbType>DateTime</DbType> 525 <NativeType>datetime</NativeType> 526 <Precision>23</Precision> 527 <Scale>3</Scale> 528 <Size>8</Size> 529 </Parameter> 530 <Parameter> 531 <Name>Remark</Name> 532 <DbType>String</DbType> 533 <NativeType>nvarchar</NativeType> 534 <Precision>0</Precision> 535 <Scale>0</Scale> 536 <Size>200</Size> 537 </Parameter> 538 <Parameter> 539 <Name>AllowUsed</Name> 540 <DbType>Boolean</DbType> 541 <NativeType>bit</NativeType> 542 <Precision>1</Precision> 543 <Scale>0</Scale> 544 <Size>1</Size> 545 </Parameter> 546 <Parameter> 547 <Name>CompanyID</Name> 548 <DbType>AnsiString</DbType> 549 <NativeType>varchar</NativeType> 550 <Precision>0</Precision> 551 <Scale>0</Scale> 552 <Size>10</Size> 553 </Parameter> 554 <Parameter> 555 <Name>BankID</Name> 556 <DbType>AnsiString</DbType> 557 <NativeType>varchar</NativeType> 558 <Precision>0</Precision> 559 <Scale>0</Scale> 560 <Size>20</Size> 561 </Parameter> 562 </Parameters> 563 </Command> 564 <Command> 565 <Key>UpdateByExpr</Key> 566 <CommandType>Text</CommandType> 567 <Text> 568 UPDATE [Bas_Bank] SET 569 #SET# 570 WHERE 1=1 #WHERE# 571 </Text> 572 </Command> 573 <Command> 574 <Key>Insert</Key> 575 <CommandType>Text</CommandType> 576 <Text> 577 INSERT INTO [Bas_Bank]( 578 [CompanyID], 579 [BankID], 580 [BankCode], 581 [BankName], 582 [SWIFT], 583 [AreaID], 584 [Address], 585 [Phone], 586 [ParentID], 587 [Level], 588 [IsDetail], 589 [FullName], 590 [FullParentID], 591 [ModifyDTM], 592 [Remark], 593 [AllowUsed] 594 ) VALUES( 595 @CompanyID, 596 @BankID, 597 @BankCode, 598 @BankName, 599 @SWIFT, 600 @AreaID, 601 @Address, 602 @Phone, 603 @ParentID, 604 @Level, 605 @IsDetail, 606 @FullName, 607 @FullParentID, 608 @ModifyDTM, 609 @Remark, 610 @AllowUsed 611 ) 612 613 </Text> 614 <Parameters> 615 <Parameter> 616 <Name>CompanyID</Name> 617 <DbType>AnsiString</DbType> 618 <NativeType>varchar</NativeType> 619 <Precision>0</Precision> 620 <Scale>0</Scale> 621 <Size>10</Size> 622 </Parameter> 623 <Parameter> 624 <Name>BankID</Name> 625 <DbType>AnsiString</DbType> 626 <NativeType>varchar</NativeType> 627 <Precision>0</Precision> 628 <Scale>0</Scale> 629 <Size>20</Size> 630 </Parameter> 631 <Parameter> 632 <Name>BankCode</Name> 633 <DbType>String</DbType> 634 <NativeType>nvarchar</NativeType> 635 <Precision>0</Precision> 636 <Scale>0</Scale> 637 <Size>20</Size> 638 </Parameter> 639 <Parameter> 640 <Name>BankName</Name> 641 <DbType>String</DbType> 642 <NativeType>nvarchar</NativeType> 643 <Precision>0</Precision> 644 <Scale>0</Scale> 645 <Size>40</Size> 646 </Parameter> 647 <Parameter> 648 <Name>SWIFT</Name> 649 <DbType>String</DbType> 650 <NativeType>nvarchar</NativeType> 651 <Precision>0</Precision> 652 <Scale>0</Scale> 653 <Size>20</Size> 654 </Parameter> 655 <Parameter> 656 <Name>AreaID</Name> 657 <DbType>AnsiString</DbType> 658 <NativeType>varchar</NativeType> 659 <Precision>0</Precision> 660 <Scale>0</Scale> 661 <Size>19</Size> 662 </Parameter> 663 <Parameter> 664 <Name>Address</Name> 665 <DbType>String</DbType> 666 <NativeType>nvarchar</NativeType> 667 <Precision>0</Precision> 668 <Scale>0</Scale> 669 <Size>100</Size> 670 </Parameter> 671 <Parameter> 672 <Name>Phone</Name> 673 <DbType>AnsiString</DbType> 674 <NativeType>varchar</NativeType> 675 <Precision>0</Precision> 676 <Scale>0</Scale> 677 <Size>60</Size> 678 </Parameter> 679 <Parameter> 680 <Name>ParentID</Name> 681 <DbType>AnsiString</DbType> 682 <NativeType>varchar</NativeType> 683 <Precision>0</Precision> 684 <Scale>0</Scale> 685 <Size>20</Size> 686 </Parameter> 687 <Parameter> 688 <Name>Level</Name> 689 <DbType>Int32</DbType> 690 <NativeType>int</NativeType> 691 <Precision>10</Precision> 692 <Scale>0</Scale> 693 <Size>4</Size> 694 </Parameter> 695 <Parameter> 696 <Name>IsDetail</Name> 697 <DbType>Boolean</DbType> 698 <NativeType>bit</NativeType> 699 <Precision>1</Precision> 700 <Scale>0</Scale> 701 <Size>1</Size> 702 </Parameter> 703 <Parameter> 704 <Name>FullName</Name> 705 <DbType>String</DbType> 706 <NativeType>nvarchar</NativeType> 707 <Precision>0</Precision> 708 <Scale>0</Scale> 709 <Size>100</Size> 710 </Parameter> 711 <Parameter> 712 <Name>FullParentID</Name> 713 <DbType>String</DbType> 714 <NativeType>nvarchar</NativeType> 715 <Precision>0</Precision> 716 <Scale>0</Scale> 717 <Size>80</Size> 718 </Parameter> 719 <Parameter> 720 <Name>ModifyDTM</Name> 721 <DbType>DateTime</DbType> 722 <NativeType>datetime</NativeType> 723 <Precision>23</Precision> 724 <Scale>3</Scale> 725 <Size>8</Size> 726 </Parameter> 727 <Parameter> 728 <Name>Remark</Name> 729 <DbType>String</DbType> 730 <NativeType>nvarchar</NativeType> 731 <Precision>0</Precision> 732 <Scale>0</Scale> 733 <Size>200</Size> 734 </Parameter> 735 <Parameter> 736 <Name>AllowUsed</Name> 737 <DbType>Boolean</DbType> 738 <NativeType>bit</NativeType> 739 <Precision>1</Precision> 740 <Scale>0</Scale> 741 <Size>1</Size> 742 </Parameter> 743 </Parameters> 744 </Command> 745 <Command> 746 <Key>Delete</Key> 747 <CommandType>Text</CommandType> 748 <Text> 749 DELETE FROM [Bas_Bank] 750 WHERE 1=1 #WHERE# 751 </Text> 752 </Command> 753 <Command> 754 <Key>DeleteByKey</Key> 755 <CommandType>Text</CommandType> 756 <Text> 757 DELETE FROM [Bas_Bank] 758 WHERE 1=1 759 And [CompanyID]=@CompanyID 760 And [BankID]=@BankID 761 </Text> 762 <Parameters> 763 <Parameter> 764 <Name>CompanyID</Name> 765 <DbType>AnsiString</DbType> 766 <NativeType>varchar</NativeType> 767 <Precision>0</Precision> 768 <Scale>0</Scale> 769 <Size>10</Size> 770 </Parameter> 771 <Parameter> 772 <Name>BankID</Name> 773 <DbType>AnsiString</DbType> 774 <NativeType>varchar</NativeType> 775 <Precision>0</Precision> 776 <Scale>0</Scale> 777 <Size>20</Size> 778 </Parameter> 779 </Parameters> 780 </Command> 781 </Commands> 782 </EntityMapper>
注意看Command节点,可以简单理解为数据库命令(下称命令),比较关键的是Text和Parameters子节点。这些命令有的带有参数有的则没有,没带参数的会有一个 ## 占位符。没带参数的命0000000令,其参数可能通过硬编码生成也有可能通过解析Lambda表达式生成,如何解析Lambda表达式会在接下来的第二点介绍。带有参数的命令,其参数名跟字段名一致,根据实体实例与字段名称就可以确定参数的值。
1 public Command Build<T>(string cmdName, T TEntity)
2 where T : class
3 {
4 Command cmd = this.GetCommand(typeof(T), cmdName);
5 foreach (Parameter parameter in cmd.Parameters)
6 {
7 //赋参数值
8 object value = AccFacHelper.Get(TEntity, parameter.Name);
9 parameter.Value = value;
10 }
11
12 return cmd;
13 }
2. 如何将Lambda表达式解析成查询条件
很早之前大牛老赵就写过一篇博文 [扩展LINQ to SQL:使用Lambda Expression批量删除数据],基本思路是实现一个Expression<Func<T,bool>>解析器并将Lambda解析为最终需要执行的TSQL。但是老赵的实现并不完整,不能解析像 f=>true f=>!f.FieldName f=>string.Length f=>string[].Contains(s) 等表达式。我在他的基础上再增加了处理,并且把条件和参数分开来以适应Dapper的参数要求,看代码片段:
1 case ExpressionType.Constant:
2 //True常量解析成1==1 Flase常量解析成1==2
3 bool value = Convert.ToBoolean(((ConstantExpression)expr).Value);
4 leftExpr = Expression.Constant(1);
5 rightExpr = Expression.Constant(value ? 1 : 2);
6
7 break;
8
9 ... ...
10
11 string condition = b.NodeType == ExpressionType.Coalesce ?
12 string.Format("({0}({1},{2}))", opr, left, right) :
13 string.Format("({0} {1} {2})", left, opr, right);
14
15 ......
16
17 condition = string.Format(" AND {0}", _stcConditions.Pop());
18 MatchCollection matches = Regex.Matches(condition, string.Format(@"{0}(?<Name>p(?<Index>[0-9]+))", _parameterPrefix));
19 foreach (Match match in matches)
20 {
21 if (!match.Success) continue;
22
23 string index = match.Groups["Index"].Value;
24 string parameterName = match.Groups["Name"].Value;
25 if (_parameters[parameterName] == null) _parameters.Add(parameterName, _lstArguments[Convert.ToInt32(index)]);
26 }
3. 如何将Dapper返回的IDataReader转化成DataTable和DataSet
IDataReader转化成DataTable相对容易,直接调用DataTable.Load(IDataReader)重载就可以,比较麻烦的是转成DataSet。DataSet.Load方法的三个重载都要传递DataTable[]形参,但在IDataReader填充DataSet之前我们是无法知道它包含有多少个数据集,也就无法确定如何给DataSet.Load传参,这似乎真的是个互相矛盾的命题。先别着急,想想之前经常用的SqlDataAdapter,它就有SqlDataAdapter.Fill(DataSet)重载。它能直接填充DataSet而不用传递DataTable[]形参,那么理论上来说DataSet.Load方法也不需要传递才对,因为实际上无论是DataSet.Load还是SqlDataAdapter.Fill,它们里面无非都是对IDataReader的层层封装而已。如此看来,只要弄清楚SqlDataAdapter.Fill(DataSet)重载,我们的问题便会迎刃而解了。
祭出反编译利器.NET Reflector,先来看看SqlDataAdapter.Fill(DataSet)到底都干了些什么:
1 public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
2 {
3 public override int Fill(DataSet dataSet)
4 {
5 try
6 {
7 IDbCommand selectCommand = this._IDbDataAdapter.SelectCommand;
8 CommandBehavior fillCommandBehavior = this.FillCommandBehavior;
9 num = this.Fill(dataSet, 0, 0, "Table", selectCommand, fillCommandBehavior);
10 }
11 finally
12 {
13 Bid.ScopeLeave(ref ptr);
14 }
15 return num;
16 }
17 }
18
19 public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
20 {
21 protected virtual int Fill(DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
22 {
23 try
24 {
25 //srcTable="Table",注意跟踪形参
26 num = this.FillInternal(dataSet, null, startRecord, maxRecords, srcTable, command, behavior);
27 }
28 finally
29 {
30 Bid.ScopeLeave(ref ptr);
31 }
32 return num;
33 }
34 }
35
36 public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
37 {
38 private int FillInternal(DataSet dataset, DataTable[] datatables, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
39 {
40 bool flag = null == command.Connection;
41 try
42 {
43 try
44 {
45 using (IDataReader reader = null)
46 {
47 reader = command.ExecuteReader(behavior);
48 ... ...
49 return this.Fill(dataset, srcTable, reader, startRecord, maxRecords);
50 }
51 }
52 finally
53 {
54 QuietClose(connection, open);
55 }
56 }
57 finally
58 {
59 if (flag)
60 {
61 command.Transaction = null;
62 command.Connection = null;
63 }
64 }
65 return 0;
66 }
67 }
68
69 public class DataAdapter : Component, IDataAdapter
70 {
71 protected virtual int Fill(DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
72 {
73 try
74 {
75 DataReaderContainer container = DataReaderContainer.Create(dataReader, this.ReturnProviderSpecificTypes);
76 num = this.FillFromReader(dataSet, null, srcTable, container, startRecord, maxRecords, null, null);
77 }
78 finally
79 {
80 Bid.ScopeLeave(ref ptr);
81 }
82 return num;
83 }
84 }
看到了没,SqlDataAdapter.Fill(DataSet)方法内部是调用了另外一个重载,形参srcTable就是一个硬编码的"Table"。
再来看看DataSet.Load的内部处理:
1 public class DataSet : MarshalByValueComponent, IListSource, IXmlSerializable, ISupportInitializeNotification, ISupportInitialize, ISerializable
2 {
3 public virtual void Load(IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler, params DataTable[] tables)
4 {
5 try
6 {
7 LoadAdapter adapter = new LoadAdapter {
8 FillLoadOption = loadOption,
9 MissingSchemaAction = MissingSchemaAction.AddWithKey
10 };
11 if (errorHandler != null)
12 {
13 adapter.FillError += errorHandler;
14 }
15 adapter.FillFromReader(tables, reader, 0, 0);
16 ... ...
17 }
18 finally
19 {
20 ... ...
21 }
22 }
23 }
24
25 internal sealed class LoadAdapter : DataAdapter
26 {
27 internal int FillFromReader(DataTable[] dataTables, IDataReader dataReader, int startRecord, int maxRecords)
28 {
29 return this.Fill(dataTables, dataReader, startRecord, maxRecords);
30 }
31 }
32
33
假如我们把LoadAdapter.FillFromReader方法修改一个,调用LoadAdapter.Fill的另外一个重载LoadAdapter.Fill(DataSet,string,IDataReader,int,int),而第二个形参只需要传"Table"而已。最终完成代码:
1 /// <summary>
2 /// 数据适配器,扩展Fill方法
3 /// .NET的DataSet.Load方法,底层调用DataAdapter.Fill(DataTable[], IDataReader, int, int)
4 /// Dapper想要返回DataSet,需要重写Load方法,不必传入DataTable[],因为数组长度不确定
5 /// </summary>
6 public class XLoadAdapter : DataAdapter
7 {
8 public XLoadAdapter()
9 {
10 }
11
12 public int FillFromReader(DataSet ds, IDataReader dataReader, int startRecord, int maxRecords)
13 {
14 return this.Fill(ds, "Table", dataReader, startRecord, maxRecords);
15 }
16 }
17
18 /// <summary>
19 /// 扩展Load方法
20 /// </summary>
21 public class XDataSet : DataSet
22 {
23 public override void Load(IDataReader reader, LoadOption loadOption, FillErrorEventHandler handler, params DataTable[] tables)
24 {
25 XLoadAdapter adapter = new XLoadAdapter
26 {
27 FillLoadOption = loadOption,
28 MissingSchemaAction = MissingSchemaAction.AddWithKey
29 };
30 if (handler != null)
31 {
32 adapter.FillError += handler;
33 }
34 adapter.FillFromReader(this, reader, 0, 0);
35 if (!reader.IsClosed && !reader.NextResult())
36 {
37 reader.Close();
38 }
39 }
40 }
41
42 //调用
43 IDataReader reader = _session.Connection.ExecuteReader(command, dynParameters,
44 _session.Transaction, _session.DataSource.CommandTimeout, commandType);
45 DataSet ds = new XDataSet();
46 ds.Load(reader, LoadOption.OverwriteChanges, null, new DataTable[] { });
4. 总结
本框架在Dapper的基础上再做封装,支持Lambda表达式树查询也支持纯Sql查询,相对来说比较灵活。但限于个人水平,没有把EmitMapper完美整合进来,只是简单的进行了引用,如果朋友们有好的建议,在下诚心请教。需要源码的朋友留个QQ邮箱我单独发吧,园子的附件最大10M导致源码上传不了。(已上传至百度云盘 http://pan.baidu.com/s/1mihKTZU)
【原创】重复造轮子之高仿EntityFramework
- 前言
在上一篇《【原创】打造基于Dapper的数据访问层》中,Dapper在应付多表自由关联、分组查询、匿名查询等应用场景时经常要手动写SQL语句。看着代码里满屏的红色SQL字符串,简直头大,于是便萌生重复造ORM这个轮子的念头。本ORM在API设计上最大程度地借鉴 EF 的写法,支持链式查询(点标记)、查询表达式、聚合查询、分组排序、批量插入、批量更新、批量删除、1:1关系外键等。在实体绑定层面,使用 Emit 来动态构建绑定指令,性能最大限度地接近原生水平。
- 性能

7000笔记录循环读1000次,同时加载1:1关系的外键,速度比 EF 稍快。
- 语法
1. 单表查询
// 查询表达式
var query = from a in context.GetTable<Inte_CRM.Demo>()
select a;
var r1 = query.ToList();
// 点标记
query = context.GetTable<Inte_CRM.Demo>();
r1 = query.ToList();
//SQL=>
//SELECT
//t0.[DemoId] AS [DemoId],
//t0.[DemoCode] AS [DemoCode],
//t0.[DemoName] AS [DemoName],
//...
//t0.[DemoLong] AS [DemoLong],
//t0.[DemoLong_Nullable] AS [DemoLong_Nullable]
//FROM [Sys_Demo] t0
2. 关联查询
// INNER JOIN
var query =
from a in context.GetTable<Inte_CRM.CRM_SaleOrder>()
join b in context.GetTable<Inte_CRM.Client>() on a.ClientId equals b.ClientId
join c in context.GetTable<Inte_CRM.CloudServer>() on b.CloudServerId equals c.CloudServerId
where a.ClientId > 0
select a;
var r1 = query.ToList();
// 点标记
query = context
.GetTable<Inte_CRM.CRM_SaleOrder>()
.Join(context.GetTable<Inte_CRM.Client>(), a => a.ClientId, b => b.ClientId, (a, b) => new { Sale = a, Buyer = b })
.Join(context.GetTable<Inte_CRM.CloudServer>(), b => b.Buyer.CloudServerId, c => c.CloudServerId, (a, c) => new Inte_CRM.CRM_SaleOrder { })
.Where(a => a.ClientId > 0);
//r1 = query.ToList();
//SQL=>
//SELECT
//t0.[OrderId] AS [OrderId],
//t0.[OrderNo] AS [OrderNo],
//t0.[Remark] AS [Remark],
//t0.[ClientId] AS [ClientId]
//FROM [CRM_SaleOrder] t0
//INNER JOIN [Bas_Client] t1 ON t0.[ClientId] = t1.[ClientId]
//INNER JOIN [Sys_CloudServer] t2 ON t1.[CloudServerId] = t2.[CloudServerId]
//WHERE t0.[ClientId] > 0
3. 分组分页
// 分组后再分页
query =
from a in context.GetTable<Inte_CRM.Client>()
where a.ClientName == "TAN"
group a by new { a.ClientId, a.ClientName } into g
where g.Key.ClientId > 0
orderby new { g.Key.ClientName, g.Key.ClientId }
select new
{
Id = g.Key.ClientId,
Name = g.Min(a => a.ClientId)
};
query = query.Skip(2).Take(3);
r1 = query.ToList();
//SQL=>
//SELECT
//t0.[Id],
//t0.[Name]
//FROM (
// SELECT
// t0.[ClientId] AS [Id],
// MIN(t0.[ClientId]) AS [Name],
// t0.[ClientName] AS [ClientName]
// FROM [Bas_Client] t0
// WHERE t0.[ClientName] = N'TAN'
// GROUP BY t0.[ClientId],t0.[ClientName]
// Having t0.[ClientId] > 0
// ) t0
//ORDER BY t0.[ClientName]
//OFFSET 2 ROWS FETCH NEXT 3 ROWS ONLY
4. 批量插入
context.Insert<Inte_CRM.Thin>(collection); context.SubmitChanges(); //SQL=> //INSERT INTO[Sys_Thin] //([ThinId],[ThinName]) //VALUES //(2, N'002'),(3,N'003')
5. 导航属性
// 更简单的赋值方式
// 适用场景:在显示列表时只想显示外键表的一两个字段
query =
from a in context.GetTable<Inte_CRM.CRM_SaleOrder>()
select new Inte_CRM.CRM_SaleOrder(a)
{
Client = new Inte_CRM.Client(a.Client)
{
CloudServer = new Inte_CRM.CloudServer
{
CloudServerId = a.Client.CloudServer.CloudServerId,
CloudServerName = a.Client.CloudServer.CloudServerName
}
},
HeavyBuyer = new Inte_CRM.Client
{
ClientId = a.Client.ClientId + 10,
ClientName = a.Client.ClientName + "_heavy",
CloudServer = new Inte_CRM.CloudServer
{
CloudServerId = a.Client.CloudServer.CloudServerId + 10,
CloudServerName = a.Client.CloudServer.CloudServerName + "_heavy",
}
}
};
r1 = query.ToList();
//SQL=>
//SELECT
//t0.[OrderId] AS [OrderId],
//t0.[OrderNo] AS [OrderNo],
//t0.[Remark] AS [Remark],
//t0.[ClientId] AS [ClientId],
//t1.[ClientId] AS [ClientId1],
//t1.[ClientCode] AS [ClientCode],
//t1.[ClientName] AS [ClientName],
//t1.[State] AS [State],
//t1.[ActiveDate] AS [ActiveDate],
//t1.[CloudServerId] AS [CloudServerId],
//t2.[CloudServerId] AS [CloudServerId1],
//t2.[CloudServerName] AS [CloudServerName],
//t1.[ClientId] + 10 AS [ClientId2],
//t1.[ClientName] + N'_heavy' AS [ClientName1],
//t2.[CloudServerId] + 10 AS [CloudServerId2],
//t2.[CloudServerName] + N'_heavy' AS [CloudServerName1]
//FROM [CRM_SaleOrder] t0
//LEFT JOIN [Bas_Client] t1 ON t0.[ClientId] = t1.[ClientId]
//LEFT JOIN [Sys_CloudServer] t2 ON t1.[CloudServerId] = t2.[CloudServerId]
其它更多示例在源码的 demo 中有详细说明,源码地址:https://github.com/TANZAME/Inte.XFramework
【原创】重复造轮子之高仿EntityFramework
- 前言
在上一篇《【原创】打造基于Dapper的数据访问层》中,Dapper在应付多表自由关联、分组查询、匿名查询等应用场景时经常要手动写SQL语句。看着代码里满屏的红色SQL字符串,简直头大,于是便萌生重复造ORM这个轮子的念头。本ORM在API设计上最大程度地借鉴 EF 的写法,支持链式查询(点标记)、查询表达式、聚合查询、分组排序、批量插入、批量更新、批量删除、1:1关系外键等。在实体绑定层面,使用 Emit 来动态构建绑定指令,性能最大限度地接近原生水平。
- 性能

7000笔记录循环读1000次,同时加载1:1关系的外键,速度比 EF 稍快。
- 语法
1. 单表查询
// 查询表达式
var query = from a in context.GetTable<Inte_CRM.Demo>()
select a;
var r1 = query.ToList();
// 点标记
query = context.GetTable<Inte_CRM.Demo>();
r1 = query.ToList();
//SQL=>
//SELECT
//t0.[DemoId] AS [DemoId],
//t0.[DemoCode] AS [DemoCode],
//t0.[DemoName] AS [DemoName],
//...
//t0.[DemoLong] AS [DemoLong],
//t0.[DemoLong_Nullable] AS [DemoLong_Nullable]
//FROM [Sys_Demo] t0
2. 关联查询
// INNER JOIN
var query =
from a in context.GetTable<Inte_CRM.CRM_SaleOrder>()
join b in context.GetTable<Inte_CRM.Client>() on a.ClientId equals b.ClientId
join c in context.GetTable<Inte_CRM.CloudServer>() on b.CloudServerId equals c.CloudServerId
where a.ClientId > 0
select a;
var r1 = query.ToList();
// 点标记
query = context
.GetTable<Inte_CRM.CRM_SaleOrder>()
.Join(context.GetTable<Inte_CRM.Client>(), a => a.ClientId, b => b.ClientId, (a, b) => new { Sale = a, Buyer = b })
.Join(context.GetTable<Inte_CRM.CloudServer>(), b => b.Buyer.CloudServerId, c => c.CloudServerId, (a, c) => new Inte_CRM.CRM_SaleOrder { })
.Where(a => a.ClientId > 0);
//r1 = query.ToList();
//SQL=>
//SELECT
//t0.[OrderId] AS [OrderId],
//t0.[OrderNo] AS [OrderNo],
//t0.[Remark] AS [Remark],
//t0.[ClientId] AS [ClientId]
//FROM [CRM_SaleOrder] t0
//INNER JOIN [Bas_Client] t1 ON t0.[ClientId] = t1.[ClientId]
//INNER JOIN [Sys_CloudServer] t2 ON t1.[CloudServerId] = t2.[CloudServerId]
//WHERE t0.[ClientId] > 0
3. 分组分页
// 分组后再分页
query =
from a in context.GetTable<Inte_CRM.Client>()
where a.ClientName == "TAN"
group a by new { a.ClientId, a.ClientName } into g
where g.Key.ClientId > 0
orderby new { g.Key.ClientName, g.Key.ClientId }
select new
{
Id = g.Key.ClientId,
Name = g.Min(a => a.ClientId)
};
query = query.Skip(2).Take(3);
r1 = query.ToList();
//SQL=>
//SELECT
//t0.[Id],
//t0.[Name]
//FROM (
// SELECT
// t0.[ClientId] AS [Id],
// MIN(t0.[ClientId]) AS [Name],
// t0.[ClientName] AS [ClientName]
// FROM [Bas_Client] t0
// WHERE t0.[ClientName] = N'TAN'
// GROUP BY t0.[ClientId],t0.[ClientName]
// Having t0.[ClientId] > 0
// ) t0
//ORDER BY t0.[ClientName]
//OFFSET 2 ROWS FETCH NEXT 3 ROWS ONLY
4. 批量插入
context.Insert<Inte_CRM.Thin>(collection); context.SubmitChanges(); //SQL=> //INSERT INTO[Sys_Thin] //([ThinId],[ThinName]) //VALUES //(2, N'002'),(3,N'003')
5. 导航属性
// 更简单的赋值方式
// 适用场景:在显示列表时只想显示外键表的一两个字段
query =
from a in context.GetTable<Inte_CRM.CRM_SaleOrder>()
select new Inte_CRM.CRM_SaleOrder(a)
{
Client = new Inte_CRM.Client(a.Client)
{
CloudServer = new Inte_CRM.CloudServer
{
CloudServerId = a.Client.CloudServer.CloudServerId,
CloudServerName = a.Client.CloudServer.CloudServerName
}
},
HeavyBuyer = new Inte_CRM.Client
{
ClientId = a.Client.ClientId + 10,
ClientName = a.Client.ClientName + "_heavy",
CloudServer = new Inte_CRM.CloudServer
{
CloudServerId = a.Client.CloudServer.CloudServerId + 10,
CloudServerName = a.Client.CloudServer.CloudServerName + "_heavy",
}
}
};
r1 = query.ToList();
//SQL=>
//SELECT
//t0.[OrderId] AS [OrderId],
//t0.[OrderNo] AS [OrderNo],
//t0.[Remark] AS [Remark],
//t0.[ClientId] AS [ClientId],
//t1.[ClientId] AS [ClientId1],
//t1.[ClientCode] AS [ClientCode],
//t1.[ClientName] AS [ClientName],
//t1.[State] AS [State],
//t1.[ActiveDate] AS [ActiveDate],
//t1.[CloudServerId] AS [CloudServerId],
//t2.[CloudServerId] AS [CloudServerId1],
//t2.[CloudServerName] AS [CloudServerName],
//t1.[ClientId] + 10 AS [ClientId2],
//t1.[ClientName] + N'_heavy' AS [ClientName1],
//t2.[CloudServerId] + 10 AS [CloudServerId2],
//t2.[CloudServerName] + N'_heavy' AS [CloudServerName1]
//FROM [CRM_SaleOrder] t0
//LEFT JOIN [Bas_Client] t1 ON t0.[ClientId] = t1.[ClientId]
//LEFT JOIN [Sys_CloudServer] t2 ON t1.[CloudServerId] = t2.[CloudServerId]
其它更多示例在源码的 demo 中有详细说明,源码地址:https://github.com/TANZAME/Inte.XFramework



浙公网安备 33010602011771号