0
+ − 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>