Stored Procedures, Enums, Microsoft Data Access Application BlockThe Right Way to do Data Accessby Les Smith |
We have written many articles on doing Data Access correctly, from a code maintenance, performance, and security points of view. This article will set forth what I believe to be Best Practices in dealing with Data Access. Obviously, everyone has an opinion, and mine does not invalidate yours. This article reflects many years of experience and the frustration of not always doing things right. So, take my recommendations for what they are worth to you.
First, if you are not using Stored Procedures, in my humble opinion, you are not doing right with respect to maintenance, performance, or security. Several of us at KnowDotNet have gone on record to say that Dynamic SQL should go. You have the right to disagree, but if you do, then you get to pay the bill in all three of the aforementioned areas.
Secondly, if you are "hard coding" your column/field references in dealing with DataSets, then you are causing yourself a maintenance problem. If someone changes the order of fields in a SQL statement or Stored Procedure, you have code to change. If you make a practice of using Enums, you only need change the Enum and the code will be changed automatically. This can be an over simplification, but not generally.
Thirdly, if you have not tried the Microsoft Data Access Application Block, you are missing a bet! It's easy to use, and you can forget about having to worry about Command Objects, Connection objects, DataAdaptors, etc. It's all done for you in the Application Block. I would not build a database application again without using the Application Block. Figure 1 shows a complete Database class for using th Microsoft Data Access Application Block.
Figure 1 - Database Class.
First, if you are not using Stored Procedures, in my humble opinion, you are not doing right with respect to maintenance, performance, or security. Several of us at KnowDotNet have gone on record to say that Dynamic SQL should go. You have the right to disagree, but if you do, then you get to pay the bill in all three of the aforementioned areas.
Secondly, if you are "hard coding" your column/field references in dealing with DataSets, then you are causing yourself a maintenance problem. If someone changes the order of fields in a SQL statement or Stored Procedure, you have code to change. If you make a practice of using Enums, you only need change the Enum and the code will be changed automatically. This can be an over simplification, but not generally.
Thirdly, if you have not tried the Microsoft Data Access Application Block, you are missing a bet! It's easy to use, and you can forget about having to worry about Command Objects, Connection objects, DataAdaptors, etc. It's all done for you in the Application Block. I would not build a database application again without using the Application Block. Figure 1 shows a complete Database class for using th Microsoft Data Access Application Block.
Figure 1 - Database Class.
using Microsoft.ApplicationBlocks.Data;using System.Data;using System.Data.SqlClient;using System.Data.SqlTypes;using System.Diagnostics;using System; namespace WindowsApplication3_2003_CSharp.MyClasses { public class Database { public Database() { // // TODO: Add constructor logic here // } private const string PROD_STRING = "Server=LESSMITH;Database=MyDatabase;Integrated Security=SSPI"; private string ConnectionString { // Returns the value of ConnectProduction. get{return PROD_STRING;} } /// <summary> /// return LocationTitle for author /// /// /// /// /// <returns> public DataSet usp_DTP_GetLocationTitle(int dept, string author, string title) { return SqlHelper.ExecuteDataset(ConnectionString, "usp_GetLocationTitle", new object[] {dept,author,title}); } // method: usp_GetLocationTitle } } |
In addition to talking about doing your Data Access right, I will also talk about Refactoring your existing code to make it right, even if it wasn' done right to begin with. Being a little biased about Refactoring tools, I will be talking about Net Refactor, because it has some features for refactoring your data access code (and building it right to start with) that no other refactoring tool currently has, for VB.NET or C#. One of these features has been in Net Refactor for some time but others have been added and new shortcuts to old features have been implemented.
To start with, let's say you have a Stored Procedure and you need to make a method to call it and you don't want to spend the time worrying about parameters and all of the associated objects. You just need to get the method done quickly. Let's also say that you are going to use the Data Access Application Block, and that you already have a Database Class similiar to the one shown in Figure 1. Finally, after you get the DataSet (in this example) back, you want to have an enum (this example will be in C#, but could have just as well been generated in VB.NET code by Net Refactor. You want the enum so that you can reference columns by a mnemonic name and at the same time use an interger index. I certainly hope you don't hard code indexes, such as " dr[12] "...
First, I will place the cursor in my Database class where I want the new method for calling the Stored Procedure to be placed.
Next, I will go to the Server Explorer, find the desire Stored Procedure, and click on the Net Refactor menu to create a calling method. a picture of this is shown in Figure 2.
Figure 2 - Invoking Net Refactor's Create Stored Procedure Call.
Once I select the Create Calling Method menu option, Net Refactor will grab the code of the Stored Procedure and load it into the Create Calling Method dialog as shown in Figure 3. In the picture in Figure 3, I have selected to create a method for the Data Access Application Block and Net Refactor automatically deduced that I was working in a C# project. I then clicked the Generate button to create the calling method shown in the bottom pane. When the dialog loaded, the selected Stored Procedure was already in the upper pane. Net Refactor will analyze the Stored Procedure to generate the calling method.
Figure 3 - Dialog for Creating Stored Procedure Calling Method.
I can now click the Copy or Paste button to copy the new method to the Clipboard or I can directly paste the method into the Database class where I placed the cursor, as described earlier. I will choose to Paste, from the context menu on the button. and the code will be pasted into the Code Window of the Database Class, as shown in Figure 4.
Figure 4 - New Stored Procedure Calling Method.
public DataSet usp_DTP_EOD_GetUnbatchedJobs(int FacID, DateTime StDate, DateTime EndDate, string WorkTypes, int FinalSaveMDA) { return SqlHelper.ExecuteDataset(ConnectionString, "usp_DTP_EOD_GetUnbatchedJobs", new object[]{FacID,StDate,EndDate,WorkTypes,FinalSaveMDA}); } |
Now, I will click the Create Enum Tab on the Dialog, and enter a new name for my enum, ubJobs. You will notice that there is a fairly complex Select Statement in the Stored Procedure. When I click the Generate Enum button, Net Refactor will extract the body of the Select clause, removing the Select keyword and place it in the top pane code window. It will then create the enum from the analysis of the fields in the Select Clause and place the newly created enum in the bottom pane code window. This is all shown in Figure 5.
Figure 5 - Creating the Enum Automatically.
I will now click the Copy or Paste button and choose to copy the enum to the clipboard, because I did not pick a place to put the enum before I created it. Once on the clipboard, I will paste the new enum in the code window as shown below.
public enum ubJobs { @Select, Department, WorkType, JobID, JobNumber, @AuthorName, @AuthorDate, @PatientName, SSN, Length, TransID, TxFile, TxTime, DelCode } |
Now, I can reference the fields of the DataSet using code as shown in Figure 6.
Figure 6 - The Enum Provides Intellisense.
When I select the highlighted enum value, the code will look like that shown below.
private void HandleData() { foreach ( DataRow dr in ds.Tables[0].Rows ) { int jobID = Convert.ToInt32(dr[(int)ubJobs.JobID]); } } |
In the reference to JobID, shown above, not only have I used a meaningful name, but the resultant code will use an integer index into the rows column collection, which is best for performance, readability, and future maintenance.
Net Refactor has helped me to be productive, produce readable, maintainable code without the hassle of hand coding. This can be done in VB.NET just as easily. With the right tools, I can be productive and at the same time produce good code that anyone can maintain.
Please note that the Data Access Block is not the only interface for which Net Refactor will generate calling. It can generate the traditional method with a command object, DataAdapter, etc. Obviously, it can generate update methods also. Just choose the type operation that you desire to perform from the Operation Combo Box on the creation dialog.
We continue to try to improve Net Refactor to be a help to you. Why not give it a real try today? Refactoring is not for the faint at heart and no tool is perfect. No tool can be everything to everybody.