| 
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>
 |