Mercurial > pub > bltoolkit
comparison Tools/DocGen/Content/Doc/DataAccess/Introduction.htm @ 0:f990fcb411a9
Копия текущей версии из github
| author | cin |
|---|---|
| date | Thu, 27 Mar 2014 21:46:09 +0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:f990fcb411a9 |
|---|---|
| 1 <% title # Introduction to abstract data accessors %> | |
| 2 <% order # 1 %> | |
| 3 <h4>Introduction</h4> | |
| 4 | |
| 5 <p class='j'> | |
| 6 Before we start talking about the data accessors, let us create a few examples of | |
| 7 typical data accessor methods. | |
| 8 </p> | |
| 9 <p class='j'> | |
| 10 The following table contains three stored procedures and | |
| 11 three data access methods implementing the stored procedure calls. | |
| 12 </p> | |
| 13 | |
| 14 <table width='100%'> | |
| 15 <tr><th>Stored procedure</th><th>Data access method</th></tr> | |
| 16 <tr><td colspan=2 style='padding-top:7px'> | |
| 17 The first stored procedure takes filter and page parameters and returns recordset from the Person table. | |
| 18 </td></tr> | |
| 19 <tr> | |
| 20 <td width=50% height=100%><% sql # | |
| 21 CREATE Procedure GetPersonListByName( | |
| 22 @firstName varchar(50), | |
| 23 @lastName varchar(50), | |
| 24 @pageNumber int, | |
| 25 @pageSize int) | |
| 26 AS | |
| 27 -- stored procedure implementation | |
| 28 -- | |
| 29 %></td> | |
| 30 <td width=50%><% cs # | |
| 31 public List<Person> GetPersonListByName( | |
| 32 string firstName, | |
| 33 string lastName, | |
| 34 int pageNumber, | |
| 35 int pageSize) | |
| 36 { | |
| 37 // method implementation. | |
| 38 } | |
| 39 %></td> | |
| 40 </tr> | |
| 41 <tr><td colspan=2> | |
| 42 Second example will return single <b>Person</b> record by <b>id</b>. | |
| 43 </td></tr> | |
| 44 <tr> | |
| 45 <td width=50% height=100%><% sql # | |
| 46 CREATE Procedure GetPersonByID(@id int) | |
| 47 AS | |
| 48 -- stored procedure implementation | |
| 49 -- | |
| 50 %> | |
| 51 </td> | |
| 52 <td width=50%><% cs # | |
| 53 public Person GetPersonByID(int id) | |
| 54 { | |
| 55 // method implementation. | |
| 56 } | |
| 57 %></td> | |
| 58 </tr> | |
| 59 <tr><td colspan=2> | |
| 60 The last example will delete a record from the database by <b>id</b>. | |
| 61 </td></tr> | |
| 62 <tr> | |
| 63 <td width=50% height=100%><% sql # | |
| 64 CREATE Procedure DeletePersonByID(@id int) | |
| 65 AS | |
| 66 -- stored procedure implementation | |
| 67 -- | |
| 68 %></td> | |
| 69 <td width=50%><% cs # | |
| 70 public void DeletePersonByID(int id) | |
| 71 { | |
| 72 // method implementation. | |
| 73 } | |
| 74 %></td> | |
| 75 </tr> | |
| 76 </table> | |
| 77 | |
| 78 <p class='j'>Now lets see what we can say if we compare the stored procedure and C# method signatures. | |
| 79 <ol> | |
| 80 <li>Stored procedure and method names match up.</li> | |
| 81 <li>Sequential order, method parameter types and names correspond to stored procedure parameters.</li> | |
| 82 <li>Methods' return values can give us an idea what <b>Execute</b> method we | |
| 83 should utilize and what object type has to be used to map data from recordset if needed.</li> | |
| 84 </ol> | |
| 85 </p> | |
| 86 | |
| 87 <p class='j'> | |
| 88 As demonstrated above method definition contains all the information we need | |
| 89 to implement the method body. Actually, by defining the method signatures, we | |
| 90 completed the most intelligent part of data accessor development. The rest of | |
| 91 work is definitely a monkey's job. Honestly, I got bored of being just a coding | |
| 92 machine writing the same data access code over and over again, especially | |
| 93 understanding that this process can be automated. | |
| 94 </p> | |
| 95 <p class='j'> | |
| 96 This introduction shows how to avoid the implementation step of data access | |
| 97 development and how to reduce this routine process to the method declaration. | |
| 98 </p> | |
| 99 | |
| 100 <h4>Abstract classes</h4> | |
| 101 | |
| 102 <p class='j'> | |
| 103 Unfortunately, mainstream .NET languages still do not have a compile-time | |
| 104 transformation system like some functional or <a href="http://nemerle.org/Macros">hybrid</a> languages do. | |
| 105 All we have today is pre-compile- and run-time code generation. | |
| 106 </p> | |
| 107 <p class='j'> | |
| 108 This introduction concentrates on run-time code generation and its support by | |
| 109 <a href="http://www.bltoolkit.net/">Business Logic Toolkit for .NET</a>. | |
| 110 </p> | |
| 111 <p class='j'> | |
| 112 Let us step back and bring the methods from the previous examples together in one class. | |
| 113 Ideally, this data accessor class could look like the following: | |
| 114 </p> | |
| 115 <% cs # | |
| 116 using System; | |
| 117 using System.Collections.Generic; | |
| 118 | |
| 119 public class PersonAccessor | |
| 120 { | |
| 121 public List<Person> GetPersonListByName( | |
| 122 string firstName, string lastName, int pageNumber, int pageSize); | |
| 123 | |
| 124 public Person GetPersonByID (int id); | |
| 125 public void DeletePersonByID(int id); | |
| 126 } | |
| 127 %> | |
| 128 | |
| 129 <p class='j'> | |
| 130 The bad news about this sample is that we cannot use such syntax as the | |
| 131 compiler expects the method's body implementation. | |
| 132 </p> | |
| 133 <p class='j'> | |
| 134 The good news is we can use abstract classes and methods that give us quite | |
| 135 similar, compilable source code. | |
| 136 </p> | |
| 137 <% cs # | |
| 138 using System; | |
| 139 using System.Collections.Generic; | |
| 140 | |
| 141 public /*[a]*/abstract/*[/a]*/ class PersonAccessor | |
| 142 { | |
| 143 public /*[a]*/abstract/*[/a]*/ List<Person> GetPersonListByName( | |
| 144 string firstName, string lastName, int pageNumber, int pageSize); | |
| 145 | |
| 146 public /*[a]*/abstract/*[/a]*/ Person GetPersonByID (int id); | |
| 147 public /*[a]*/abstract/*[/a]*/ void DeletePersonByID(int id); | |
| 148 } | |
| 149 %> | |
| 150 | |
| 151 <p class='j'>This code is 100% valid and our next step is to make it workable.</p> | |
| 152 | |
| 153 <H4>Abstract DataAccessor</H4> | |
| 154 | |
| 155 <p class='j'> | |
| 156 Business Logic Toolkit provides the <b>DataAccessor</b> class, which is | |
| 157 used as a base class to develop data accessor classes. If we add | |
| 158 <b>DataAccessor</b> to our previous example, it will look like the following: | |
| 159 </p> | |
| 160 <% cs # | |
| 161 using System; | |
| 162 using System.Collections.Generic; | |
| 163 | |
| 164 public abstract class PersonAccessor : /*[a]*/DataAccessor/*[/a]*/ | |
| 165 { | |
| 166 public abstract List<Person> GetPersonListByName( | |
| 167 string firstName, string lastName, int pageNumber, int pageSize); | |
| 168 | |
| 169 public abstract Person GetPersonByID (int id); | |
| 170 public abstract void DeletePersonByID(int id); | |
| 171 } | |
| 172 %> | |
| 173 | |
| 174 <p class='j'>That's it! Now this class is complete and fully functional. The code below shows how to use it:</p> | |
| 175 | |
| 176 <% cs # | |
| 177 using System; | |
| 178 using System.Collections.Generic; | |
| 179 | |
| 180 using BLToolkit.Reflection; | |
| 181 | |
| 182 namespace DataAccess | |
| 183 { | |
| 184 class Program | |
| 185 { | |
| 186 static void Main(string[] args) | |
| 187 { | |
| 188 PersonAccessor pa = /*[a]*/TypeAccessor/*[/a]*/<PersonAccessor>./*[a]*/CreateInstance/*[/a]*/(); | |
| 189 | |
| 190 List<Person> list = pa.GetPersonListByName("Crazy", "Frog", 0, 20); | |
| 191 | |
| 192 foreach (Person p in list) | |
| 193 Console.Write("{0} {1}", p.FirstName, p.LastName); | |
| 194 } | |
| 195 } | |
| 196 } | |
| 197 %> | |
| 198 | |
| 199 <p class='j'> | |
| 200 The only magic here is the <b>TypeAccessor.CreateInstance</b> method. First of all this | |
| 201 method creates a new class inherited from the <b>PersonAccessor</b> class and | |
| 202 then generates abstract method bodies depending on each method declaration. | |
| 203 If we wrote those methods manually, we could get something like the following: | |
| 204 </p> | |
| 205 <% cs # | |
| 206 using System; | |
| 207 using System.Collections.Generic; | |
| 208 | |
| 209 using BLToolkit.Data; | |
| 210 | |
| 211 namespace Example.BLToolkitExtension | |
| 212 { | |
| 213 public sealed class PersonAccessor : Example.PersonAccessor | |
| 214 { | |
| 215 public override List<Person> GetPersonListByName( | |
| 216 string firstName, | |
| 217 string lastName, | |
| 218 int pageNumber, | |
| 219 int pageSize) | |
| 220 { | |
| 221 using (DbManager db = GetDbManager()) | |
| 222 { | |
| 223 return db | |
| 224 .SetSpCommand("GetPersonListByName", | |
| 225 db.Parameter("@firstName", firstName), | |
| 226 db.Parameter("@lastName", lastName), | |
| 227 db.Parameter("@pageNumber", pageNumber), | |
| 228 db.Parameter("@pageSize", pageSize)) | |
| 229 .ExecuteList<Person>(); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 public override Person GetPersonByID(int id) | |
| 234 { | |
| 235 using (DbManager db = GetDbManager()) | |
| 236 { | |
| 237 return db | |
| 238 .SetSpCommand("GetPersonByID", db.Parameter("@id", id)) | |
| 239 .ExecuteObject<Person>(); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 public override void DeletePersonByID(int id) | |
| 244 { | |
| 245 using (DbManager db = GetDbManager()) | |
| 246 { | |
| 247 db | |
| 248 .SetSpCommand("DeletePersonByID", db.Parameter("@id", id)) | |
| 249 .ExecuteNonQuery(); | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 } | |
| 254 %> | |
| 255 | |
| 256 <p class='j'> | |
| 257 (The <a href='..\Data\index.htm'>DbManager</a> class is another BLToolkit class used for 'low-level' database access). | |
| 258 </p> | |
| 259 | |
| 260 <p class="j"> | |
| 261 Every part of the method declaration is important. | |
| 262 Method's return value specifies one of the Execute methods in the following way: | |
| 263 <table class='data'> | |
| 264 <tr><th>Return Type</th><th>Execute Method</th></tr> | |
| 265 <tr><td><i>IDataReader</i> interface</td><td>ExecuteReader</td></tr> | |
| 266 <tr><td>Subclass of <i>DataSet</i></td><td>ExecuteDataSet</td></tr> | |
| 267 <tr><td>Subclass of <i>DataTable</i></td><td>ExecuteDataTable</td></tr> | |
| 268 <tr><td>Class implementing the <i>IList</i> interface</td><td><a href="ExecuteList.htm">ExecuteList</a> or <a href="ExecuteList.htm">ExecuteScalarList</a></td></tr> | |
| 269 <tr><td>Class implementing the <i>IDictionary</i> interface</td><td><a href="ExecuteDictionary.htm">ExecuteDictionary</a> or <a href="ExecuteDictionary.htm">ExecuteScalarDictionary</a></td></tr> | |
| 270 <tr><td><i>void</i></td><td>ExecuteNonQuery</td></tr> | |
| 271 <tr><td><i>string</i>, <i>byte[]</i> or value type</td><td><a href="ExecuteScalar.htm">ExecuteScalar</a></td></tr> | |
| 272 <tr><td>In any other case</td><td><a href="ExecuteObject.htm">ExecuteObject</a></td></tr> | |
| 273 </table> | |
| 274 The method name explicitly defines the action name, which is converted to the stored procedure name. | |
| 275 Type, sequential order, and name of the method parameters are mapped to the command parameters. | |
| 276 Exceptions from this rule are: | |
| 277 <br> | |
| 278 <div style='margin:-10px 0px -0px -10px'><ul compact="compact"> | |
| 279 <li>a parameter of <a href="../Data/index.htm"><i>DbManager</i></a> type. In this case generator uses provided <a href="../Data/index.htm"><i>DbManager</i></a> to call the command.</li> | |
| 280 <li>parameters decorated with attribute <a href='#FormatAttribute'>FormatAttribute</a>, <a href='#DestinationAttribute'>DestinationAttribute</a>.</li> | |
| 281 </ul></div> | |
| 282 </p> | |
| 283 | |
| 284 <h4>Generating process control</h4> | |
| 285 | |
| 286 <p class='j'> | |
| 287 The <b>PersonAccessor</b> class above is a very simple example and, of | |
| 288 course, it seems too ideal to be real. In real life, we need more flexibility | |
| 289 and more control over the generated code. BLToolkit contains a bunch of | |
| 290 attributes to control DataAccessor generation in addition to <b>DataAccessor</b> virtual members. | |
| 291 </p> | |
| 292 | |
| 293 <h5>Method CreateDbManager</h5> | |
| 294 | |
| 295 <% cs # | |
| 296 protected virtual DbManager CreateDbManager() | |
| 297 { | |
| 298 return new DbManager(); | |
| 299 } | |
| 300 %> | |
| 301 | |
| 302 <p class='j'> | |
| 303 By default, this method creates a new instance of <b>DbManager</b> that uses default database configuration. | |
| 304 You can change this behavior by overriding this method. For example: | |
| 305 </p> | |
| 306 <% cs # | |
| 307 public abstract class OracleDataAccessor : DataAccessor | |
| 308 { | |
| 309 protected override DbManager CreateDbManager() | |
| 310 { | |
| 311 return new DbManager("Oracle", "Production"); | |
| 312 } | |
| 313 } | |
| 314 %> | |
| 315 | |
| 316 <p class='j'> | |
| 317 This code will use the <i>Oracle</i> data provider and <i>Production</i> configuration. | |
| 318 </p> | |
| 319 | |
| 320 <a name='GetDefaultSpName'></a><h5>Method GetDefaultSpName</h5> | |
| 321 | |
| 322 <% cs # | |
| 323 protected virtual string GetDefaultSpName(string typeName, string actionName) | |
| 324 { | |
| 325 return typeName == null? | |
| 326 actionName: | |
| 327 string.Format("{0}_{1}", typeName, actionName); | |
| 328 } | |
| 329 %> | |
| 330 | |
| 331 <p class='j'> | |
| 332 As I mentioned, the method name explicitly defines the so-called action name. | |
| 333 The final stored procedure name is created by the <b>GetDefaultSpName</b> | |
| 334 method. The default implementation uses the following naming convention: | |
| 335 </p> | |
| 336 | |
| 337 <ul> | |
| 338 <li> | |
| 339 If type name is provided, the method constructs the stored proc name by | |
| 340 concatenating the type and action names. Thus, if the type name is "Person" and | |
| 341 the action name is "GetAll", the resulting sproc name will be "Person_GetAll". | |
| 342 </li> | |
| 343 <li> | |
| 344 If the type name is NOT provided, the stored procedure name will equal the | |
| 345 action name.</li> | |
| 346 </ul> | |
| 347 | |
| 348 <p class='j'> | |
| 349 You can easily change this behavior. For example, for the naming convention "p_Person_GetAll", | |
| 350 the method implementation can be the following: | |
| 351 </p> | |
| 352 <% cs # | |
| 353 public abstract class MyBaseDataAccessor<T,A> : DataAccessor<T,A> | |
| 354 where A : DataAccessor<T,A> | |
| 355 { | |
| 356 protected override string GetDefaultSpName(string typeName, string actionName) | |
| 357 { | |
| 358 return string.Format("p_{0}_{1}", typeName, actionName); | |
| 359 } | |
| 360 } | |
| 361 %> | |
| 362 | |
| 363 <h5>Method GetTableName</h5> | |
| 364 | |
| 365 <% cs # | |
| 366 protected virtual string GetTableName(Type type) | |
| 367 { | |
| 368 // ... | |
| 369 return type.Name; | |
| 370 } | |
| 371 %> | |
| 372 | |
| 373 <p class='j'> | |
| 374 By default, the table name is the associated object type name (<i>Person</i> in our examples). | |
| 375 There are two ways to associate an object type with an accessor. By providing generic parameter: | |
| 376 </p> | |
| 377 <% cs # | |
| 378 public abstract class PersonAccessor : DataAccessor<Person> | |
| 379 { | |
| 380 } | |
| 381 %> | |
| 382 | |
| 383 <p class='j'> | |
| 384 And by the <b>ObjectType</b> attribute: | |
| 385 </p> | |
| 386 <% cs # | |
| 387 [ObjectType(typeof(Person))] | |
| 388 public abstract class PersonAccessor : DataAccessor | |
| 389 { | |
| 390 } | |
| 391 %> | |
| 392 | |
| 393 <p class='j'> | |
| 394 If you want to have different table and type names in your application, you may override the <b>GetTableName</b> method: | |
| 395 </p> | |
| 396 <% cs # | |
| 397 public abstract class OracleDataAccessor<T,A> : DataAccessor<T,A> | |
| 398 where A : DataAccessor<T,A> | |
| 399 { | |
| 400 protected override string GetTableName(Type type) | |
| 401 { | |
| 402 return base.GetTableName(type).ToUpper(); | |
| 403 } | |
| 404 } | |
| 405 %> | |
| 406 | |
| 407 <h5>TableNameAttribute</h5> | |
| 408 | |
| 409 <p class='j'> | |
| 410 Also, you can change the table name for a particular object type by decorating this object | |
| 411 with the <b>TableNameAttribute</b> attribute: | |
| 412 </p> | |
| 413 <% cs # | |
| 414 [TableName("PERSON")] | |
| 415 public class Person | |
| 416 { | |
| 417 public int ID; | |
| 418 public string FirstName; | |
| 419 public string LastName; | |
| 420 } | |
| 421 %> | |
| 422 | |
| 423 <h5>ActionNameAttribute</h5> | |
| 424 | |
| 425 <p class='j'> | |
| 426 This attribute allows changing the action name. | |
| 427 </p> | |
| 428 <% cs # | |
| 429 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 430 { | |
| 431 [ActionName("GetByID")] | |
| 432 protected abstract IDataReader GetByIDInternal(DbManager db, int id); | |
| 433 | |
| 434 public Person GetByID(int id) | |
| 435 { | |
| 436 using (DbManager db = GetDbManager()) | |
| 437 using (IDataReader rd = GetByIDInternal(db, id)) | |
| 438 { | |
| 439 Person p = new Person(); | |
| 440 | |
| 441 // do something complicated. | |
| 442 | |
| 443 return p; | |
| 444 } | |
| 445 } | |
| 446 } | |
| 447 %> | |
| 448 | |
| 449 <h5>ActionSprocNameAttribute</h5> | |
| 450 | |
| 451 <p class='j'> | |
| 452 This attribute associates the action name with a stored procedure name: | |
| 453 </p> | |
| 454 <% cs# | |
| 455 [ActionSprocName("Insert", sp_Person_Insert")] | |
| 456 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 457 { | |
| 458 public abstract void Insert(Person p); | |
| 459 } | |
| 460 %> | |
| 461 | |
| 462 <p class='j'> | |
| 463 This attribute can be useful when you need to reassign a stored procedure name for a method defined in your base class. | |
| 464 </p> | |
| 465 | |
| 466 <h5>SprocNameAttribute</h5> | |
| 467 | |
| 468 <p class='j'> | |
| 469 The regular way to assign deferent from default sproc name for a method is the <b>SprocName</b> attribute. | |
| 470 </p> | |
| 471 <% cs # | |
| 472 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 473 { | |
| 474 [SprocName("sp_Person_Insert")] | |
| 475 public abstract void Insert(Person p); | |
| 476 } | |
| 477 %> | |
| 478 | |
| 479 <a name='DestinationAttribute'></a><h5>DestinationAttribute</h5> | |
| 480 | |
| 481 <p class='j'> | |
| 482 By default, the DataAccessor generator uses method's return value to determine which <i>Execute</i> method | |
| 483 should be used to perform the current operation. | |
| 484 The <b>DestinationAttribute</b> indicates that target object is a parameter decorated with this attribute: | |
| 485 </p> | |
| 486 <% cs # | |
| 487 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 488 { | |
| 489 public abstract void GetAll([Destination] List<Person> list); | |
| 490 } | |
| 491 %> | |
| 492 | |
| 493 <h5>DirectionAttributes</h5> | |
| 494 | |
| 495 <p class='j'> | |
| 496 <i>DataAccessor</i> generator can map provided business object to stored | |
| 497 procedure parameters. <b>Direction</b> attributes allow controlling this process more precisely. | |
| 498 </p> | |
| 499 <% cs # | |
| 500 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 501 { | |
| 502 public abstract void Insert( | |
| 503 [Direction.Output("ID"), Direction.Ignore("LastName")] Person person); | |
| 504 } | |
| 505 %> | |
| 506 | |
| 507 <p class='j'> | |
| 508 In addition, BLToolkit provides two more direction attributes: | |
| 509 <b>Direction.InputOutputAttribute</b> and <b>Direction.ReturnValueAttribute</b>. | |
| 510 </p> | |
| 511 | |
| 512 <h5>DiscoverParametersAttribute</h5> | |
| 513 <p class='j'> | |
| 514 By default, BLToolkit expects method parameter names to match stored procedure | |
| 515 parameter names. The sequential order of parameters is not important in this | |
| 516 case. This attribute enforces BLToolkit to retrieve parameter information from | |
| 517 the sproc and to assign method parameters in the order they go. Parameter names | |
| 518 are ignored. | |
| 519 </p> | |
| 520 | |
| 521 <a name='FormatAttribute'></a><h5>FormatAttribute</h5> | |
| 522 | |
| 523 <p class='j'> | |
| 524 This attribute indicates that the specified parameter is used to construct the stored procedure name or SQL statement: | |
| 525 </p> | |
| 526 <% cs # | |
| 527 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 528 { | |
| 529 [SqlQuery("SELECT {0} FROM {1} WHERE {2}")] | |
| 530 public abstract List<string> GetStrings( | |
| 531 [Format(0)] string fieldName, | |
| 532 [Format(1)] string tableName, | |
| 533 [Format(2)] string whereClause); | |
| 534 } | |
| 535 %> | |
| 536 | |
| 537 <h5>IndexAttribute</h5> | |
| 538 | |
| 539 <p class='j'> | |
| 540 If you want your method to return a dictionary, you will have to specify fields to build the dictionary key. | |
| 541 The Index attribute allows you to do that: | |
| 542 </p> | |
| 543 <% cs # | |
| 544 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 545 { | |
| 546 [SqlQuery("SELECT * FROM Person")] | |
| 547 [Index("ID")] | |
| 548 public abstract Dictionary<int, Person> SelectAll1(); | |
| 549 | |
| 550 [SqlQuery("SELECT * FROM Person")] | |
| 551 [Index("@PersonID", "LastName")] | |
| 552 public abstract Dictionary<CompoundValue, Person> SelectAll2(); | |
| 553 } | |
| 554 %> | |
| 555 <p class='j'>Note: if your key has more than one field, the type of this key should be <b>CompoundValue</b>.</p> | |
| 556 <p class='j'>If the field name starts from '@' symbol, BLToolkit reads the field value from data source, | |
| 557 otherwise from an object property/field.</p> | |
| 558 | |
| 559 <h5>ParamNameAttribute</h5> | |
| 560 | |
| 561 <p class='j'> | |
| 562 By default, the method parameter name should match the stored procedure parameter name. | |
| 563 This attribute specifies the sproc parameter name explicitly. | |
| 564 </p> | |
| 565 <% cs # | |
| 566 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 567 { | |
| 568 public abstract Person SelectByName( | |
| 569 [ParamName("FirstName")] string name1, | |
| 570 [ParamName("@LastName")] string name2); | |
| 571 } | |
| 572 %> | |
| 573 | |
| 574 <h5>ScalarFieldNameAttribute</h5> | |
| 575 | |
| 576 <p class='j'> | |
| 577 If your method returns a dictionary of scalar values, you will have to specify the name or index of the field | |
| 578 used to populate the scalar list. The <b>ScalarFieldName</b> attribute allows you to do that: | |
| 579 </p> | |
| 580 <% cs # | |
| 581 public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 582 { | |
| 583 [SqlQuery("SELECT * FROM Person")] | |
| 584 [Index("@PersonID")] | |
| 585 [ScalarFieldName("FirstName")] | |
| 586 public abstract Dictionary<int, string> SelectAll1(); | |
| 587 | |
| 588 [SqlQuery("SELECT * FROM Person")] | |
| 589 [Index("PersonID", "LastName")] | |
| 590 [ScalarFieldName("FirstName")] | |
| 591 public abstract Dictionary<CompoundValue, string> SelectAll2(); | |
| 592 } | |
| 593 %> | |
| 594 | |
| 595 <h5>ScalarSourceAttribute</h5> | |
| 596 | |
| 597 <p class='j'> | |
| 598 If a method returns a scalar value, this attribute can be used to specify how database returns this value. | |
| 599 The <b>ScalarSource</b> attribute take a parameter of the <b>ScalarSourceType</b> type: | |
| 600 </p> | |
| 601 | |
| 602 <table class='data'> | |
| 603 <tr><th>ScalarSourceType</th><th>Description</th></tr> | |
| 604 <tr><td>DataReader</td><td>Calls the <b>DbManager.ExecuteReader</b> method, and then calls <b>IDataReader.GetValue</b> method to read the value.</td></tr> | |
| 605 <tr><td>OutputParameter</td><td>Calls the <b>DbManager.ExecuteNonQuery</b> method, and then reads value from the <b>IDbDataParameter.Value</b> property.</td></tr> | |
| 606 <tr><td>ReturnValue</td><td>Calls the <b>DbManager.ExecuteNonQuery</b> method, and then reads return value from command parameter collection.</td></tr> | |
| 607 <tr><td>AffectedRows</td><td>Calls the <b>DbManager.ExecuteNonQuery</b> method, and then returns its return value.</td></tr> | |
| 608 </table> | |
| 609 | |
| 610 <h5>SqlQueryAttribute</h5> | |
| 611 | |
| 612 <p class='j'> | |
| 613 This attribute allows specifying SQL statement. | |
| 614 </p> | |
| 615 <% cs # public abstract class PersonAccessor : DataAccessor<Person, PersonAccessor> | |
| 616 { | |
| 617 [SqlQuery("SELECT * FROM Person WHERE PersonID = @id")] | |
| 618 public abstract Person GetByID(int @id); | |
| 619 } | |
| 620 %> | |
| 621 | |
| 622 <h4>Conclusion</h4> | |
| 623 | |
| 624 <p class='j'> | |
| 625 I hope this brief tutorial demonstrates one of the simplest, quickest and | |
| 626 most low-maintenance ways to develop your data access layer. In addition, you | |
| 627 will get one more benefit, which is incredible object mapping performance. But | |
| 628 that is a topic we will discuss later. | |
| 629 </p> |
