building dal using typed dataset

Upload: puspala-manojkumar

Post on 10-Apr-2018

226 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/8/2019 building DAL using typed dataset

    1/14

    A good data access layer is critical for most business applications, whether that layerresides on a middle tier application server, a web server or on the client. Data accesslayers tend to involve a lot of error-prone, repetitive coding if you try to do it by hand.You can alleviate some of that by designing a good set of base and helper classes toencapsulate the repeating patterns, or by using a code generation tool. However, you stillusually have to write all the data access methods by hand for the ad-hoc queries thatservice large parts of your application.

    If you are using Visual Studio 2005, that does not have to be the case. Some significantimprovements have been made to the Visual Studio 2005 DataSet designer and the codethat it generates that make it so you will rarely have to write your data access methods byhand of you choose to work with DataSets. In this article, Ill walk you through whatthose features are and how to use them. Ill also discuss some of the pros and cons ofusing the new features and give some guidance on when you might want to do somethingdifferent.

    A Quick Intro to Typed Data Sets

    Typed data sets are classes that you create declaratively through the Visual Studiodesigner. Typed data set classes derive from the ADO.NET DataSet, DataTable, andDataRow classes and expose a type-safe API for accessing data contained within aDataSet that has a particular schema. You create typed data sets in Visual Studio throughsome simple drag and drop operations and by setting properties in the Propertieswindow of the designer. What you are actually creating when you design a typed data setis an XML Schema Definition (XSD) file that contains the schema of the data that will becontained by the DataSet. The XSD file also contains annotations that associate it withthe data source it was generated from. Visual Studio then uses that XSD to generate acode file that contains the typed data set class definitions.

    When you work with data in an application, the data is usually partitioned into differenttypes of logical business entities, such as Customers, Products, Employees, and so on. Towork with that data, you need to encapsulate those logical entities into objects that youcan deal with in your code. You could write a custom class for each entity type. Thoseentity types would expose properties for each of the data values that the entity includes.You would then also want to create a custom collection type for each entity type so thatyou could have strongly typed collections to contain those entities.

    Typed data sets represent an easy alternative to creating and maintaining all thosecustom types yourself. Essentially what you are doing when you create a typed data set isthat you are creating a set of custom type definitions to contain logical business entitiesand collections of those entities, similar to writing those types by hand. The difference isthat you are doing it in a declarative way through the designer that is easy to visualize,edit, and keep synchronized with the database schema that populates those businessentities. The code generation of Visual Studio takes care of writing all the underlyingproperties and methods that give you a strongly typed API for dealing with thosebusiness entities in your consuming code. Additionally, because these types areinheriting from the ADO.NET types, you inherit the rich relational data manipulationfunctionality from those types. These types are also aligned well with the data bindingcapabilities in Windows Forms and ASP.NET, so if you will be setting up data bindingusing the objects, then you have less work to do on that front as well.

    Finally, and perhaps most importantly, when you create typed data sets in Visual Studio

    2005 from a database, you also get a table adapter type created for each table that youadd to the data set. A table adapter is a full fledged data access component that lets youretrieve and update data from the database. It encapsulates a connection, a data adapter,

    1

  • 8/8/2019 building DAL using typed dataset

    2/14

    and a set of command objects that allow you to execute queries to the database. Ill getinto more detail on table adapters in a little bit.

    DataSets vs. Business Objects an Open Debate

    Almost as common as the debate over which .NET language to choose is the argumentabout whether to use DataSets or not. As described above, typed data sets are easy togenerate through the designer, provide a type safe API for containing business entitydata, and already support advanced features such as change tracking, sorting, filtering,and searching. Some of the resistance to DataSets resulted from several performanceshortcomings in the .NET 1.1 implementations. These problems included poorperformance when working with large DataSets and the fact that DataSets alwaysserialized themselves as XML, which had bandwidth and performance implications whenpassing DataSets across remoting boundaries. These problems have been fixed in .NET2.0 and there have been a large number of other improvements. If you dismissedDataSets in .NET 1.1, they deserve another look.

    The alternative to using DataSets is to create custom object types that represent yourbusiness entities, and custom collection types to contain them. When you go this route,you end up needing to write a lot more code yourself. This has gotten a lot better inVisual Studio 2005 and .NET 2.0 with the additions of code snippets and genericcollection classes. But to support the full range of features that a DataSet provides, thereis still a fair amount of custom code that you will need to write by hand.

    Custom objects have the advantage of giving you explicit and complete control over theway the type is designed, what its internal capabilities are, and what the API is that isexposed from the object. If you prefer a pure object-oriented design approach, thencustom business entities will feel a little more comfortable to you. You can accomplishalmost anything with a typed data set that you can with a custom business entity, but

    some things may be a little less clean with a typed data set if the things you are trying todo dont map well to the relational nature of a typed data set. But if you are primarilygetting business data for the purposes of presenting the data, allowing the user to workwith the data, and then will persist the data back to the database, you will be able to getthings done quicker with typed data sets if you harness the features of the DataSetdesigner.

    When you go with typed data sets in Visual Studio 2005, you can actually support mostof the same design styles that you could with custom business entity types. The dataaccess code will always be separated into the table adapter types generated by thedesigner, or into data access components that you write. But you can add customvalidation and other logic into your business entity types (the typed data row or datatable classes) through partial class extensions. Each of the types created as part of a typeddata set definition (data set, data table, data row, and table adapter) are defined in thegenerated code as partial classes. This feature in .NET 2.0 allows you to supplement thedesigner generated code with custom code that becomes part of the compiled type, butyou do so through a separate code file. This prevents your code from being destroyed ifyou choose to regenerate the designer generated code. For more information on partialtypes, see the MSDN library documentation for .NET 2.0.

    Another argument that comes up a lot against using DataSets is the assertion that if youare using DataSets in your presentation or business layer, then you are tightly couplingyour application to the data tier. This does not have to be the case. First off, you should

    consider using stored procedures as a layer of decoupling between your actual data tierschema and your application. Your stored procedures can then return and work withresult sets that map well to the business entities that you will be manipulating in your

    2

  • 8/8/2019 building DAL using typed dataset

    3/14

    application. Additionally, if you need to provide additional decoupling beyond what thestored procedures provide, you can transform data that has been placed into a DataSetinto either a different (decoupled) typed data set definition or a custom business entitytype in your business or data access layer.

    One final thing to point out has to do with returning DataSets from web services. There isa strong argument that you should never return a DataSet from a web service method.Never is a strong word in design, but if you want to support a true service-orientedparadigm with your web services, I would have to agree with this guidance. Typed datasets do introduce both a type coupling and a .NET technology coupling for any clients ofthat service, which is something you should avoid in a service-oriented world. If yourclient is a .NET client, it is fairly simple to consume the proxy generated version of thetyped data set on the client side, and there is little harm if you are willing to lock yourselfinto only .NET clients. But in general you should think twice before exposing a DataSet asa type on a web service contract, and favor not doing so.

    If you want to achieve maximum productivity in building a .NET application, and can live

    with the limitations described in this section, then typed data sets are a great capabilitythat you should consider exploiting in your .NET applications where it makes sense. Oneof the biggest wins in using typed data sets in Visual Studio 2005 is the fact that it alsogenerates a table adapter class, which is like a typed data adapter, for each table in thedata set. The designer allows you to very quickly create customized query methods in thetable adapter that makes it so you will rarely have to write any ADO.NET code yourself ifyou are working with typed data sets.

    A Working Example

    To make things more concrete, lets step through a simple example of creating a typeddata set and using table adapters. To emphasize that nothing that I am showing in this

    article is particular to SQL Server 2005, I will use the tried-and-true Northwind databasewith SQL Server 2000 as a data source. If you are working with a machine that does nothave SQL Server 2000 on it, you can also get an instance of Northwind installed on SQLServer 2005, including the Express version. You will need to get a data connectionconfigured for the Northwind database in Server Explorer. To do so, right click on theData Connections node in Server Explorer, select Add Connection, and step through theAdd Connection dialog to configure a connection to Northwind.

    Unfortunately the Northwind database does not have a good stored procedure layer setup for CRUD (Create, Retrieve, Update, Delete) operations. It also does not have anycolumns in the tables to help enforce optimistic concurrency efficiently. To do this, youshould have a datetime, rowguid, or timestamp column that gets updated on everyINSERT or UPDATE, and that can be used to check for concurrency violations whenupdating or deleting rows. The download code for this article includes a script that youcan run against the Northwind database through Query Analyzer or SQL ServerManagement Studio that will add a Modified datetime column to the Employees tableand CRUD stored procedures to work with that table. The sample developed below worksagainst those modifications to implement the table adapter using a good technique forreal applications. The stored procedures include SelectEmployees, UpdateEmployees,InsertEmployees, and DeleteEmployees standardized CRUD procedures that I haveimplemented in a CodeSmith template so that I can generate them for any table I amworking with that uses an optimistic concurrency checking column. The CodeSmithtemplate is also included in the download code in case you use that tool for codegeneration, but for the sample you can just run the included SQL script.

    3

  • 8/8/2019 building DAL using typed dataset

    4/14

  • 8/8/2019 building DAL using typed dataset

    5/14

    5

    Figure 2: Add a Typed Data Set Definition to the Project

    Next we will add some table definitions to the data set through the Server Explorerwindow. Bring up Server Explorer (View > Server Explorer) and expand the Northwinddata connection down to the Stored Procedures level. Drag the SelectEmployees storedprocedure onto the design surface.

    When you do this, you will see a table definition added that has all the same columns asthe Employees table from the database. These are defined based on the columns that theSELECT statement in the stored procedure returns. Rename the table fromSelectEmployees to just Employees by single clicking on the name in the title bar of the

    table definition in the designer. Alternatively, you can select the table in the designer anduse the Properties window to rename it.

    You will also see that at the bottom of the Employees table, something else has beenadded called EmployeesTableAdapter (see Figure 3). This is the table adapter that Iintroduced earlier. When you save the data set, Visual Studio will kick in and do somecode generation behind the scenes.

  • 8/8/2019 building DAL using typed dataset

    6/14

    Figure 3: Employees Table Definition and Table Adapter

    For the simple drag and drop that we just did, Visual Studio created a number of newtype definitions. The EmployeesDataSet type is the top level container class for the otherdata container types we are working with. An EmployeesDataTable was generated thatprovides a strongly typed collection class for Employee entities. An EmployeesRow classwas generated that is a strongly typed entity class with properties exposed for each of thecolumns in the table. And finally, the EmployeesTableAdapter is generated to allow youto retrieve data into the Employees table using the SelectEmployees stored procedure. Inone single drag and drop operation, over 1000 lines of highly functional best-practiceADO.NET code was written for you.

    All of these types are defined as partial classes in a designer code file(EmployeesDataSet.Designer.cs in this case) when you save the XSD file. This allows youto add code to any of the classes in a separate partial class definition file that will not beaffected if you need to regenerate your code in the future. Regenerating the data setmight be necessary if you are iteratively developing the database schema, or if you just

    goon something up in the designer.

    Hooking up the Other Stored Procedures

    When you drag a SELECT stored procedure onto the designer, the table adapter onlyimplements the code to fill the generated table. Specifically, it adds a Fill method thattakes the specific table type (EmployeesDataTable in this case) as a parameter and fillsthat instance with the rows returned from the SELECT stored procedure. It alsogenerates a GetData method that will create an instance of the table type, populate it withthe rows, and return it from the method as the return value so that you do not have toconstruct one yourself first.

    To hook up the corresponding UPDATE, INSERT, and DELETE stored procedures, thereis a simple wizard available through the designer. This wizard is the same one you canuse to later modify the table adapter if you want to target different or modified storedprocedures.

    To bring up the wizard, click on the title bar of the table adapter in the designer to selectit, then right click on it and select Configure from the context menu. This will bring upthe Table Adapter Configuration Wizard, which allows you to change the storedprocedures that are called by the table adapter. Since the table adapter is already hookedup to the existing SelectEmployees stored procedure, it will start at the step labeled BindCommands to Existing Stored Procedures (see Figure 4).

    6

  • 8/8/2019 building DAL using typed dataset

    7/14

    Figure 4: Table Adapter Configuration W izard

    To hook up the InsertEmployees, UpdateEmployees, and DeleteEmployees storedprocedures, just select them from the drop down lists on the left side of the wizard. Theright side of the wizard will show the mapping of stored procedure parameters tocolumns in the associated table. If your stored procedure parameter names matchexisting columns in the table, the mappings will all be set up automatically for you, whichshould always be the case when the table is generated from the stored procedure.

    Unfortunately, at least in Visual Studio 2005 Beta 2, the wizard is not perfect and maynot match all the parameters correctly. For example, in this sample, the PhotoPathparameter on the UPDATE and INSERT stored procedures gets mapped to the Photocolumn by mistake. To fix that, you just need to select the correct column in the columnmappings as shown in Figure 5. Make sure you do this for both the insert and updatestored procs. Hopefully this behavior will be fixed by release.

    7

  • 8/8/2019 building DAL using typed dataset

    8/14

    Figure 5: Correcting the column ma ppings

    Once you have selected the stored procedures, you can simply press the Finish button.The other steps in the wizard are used for configuring other scenarios that I dont haveroom to cover here. For more comprehensive coverage of configuring table adapters, seeChapter 2 in my book Data Binding in Windows Forms 2.0. At this point Visual Studio

    adds Insert, Update, and Delete methods that allow you to perform modifications at thetable or row level, as well as overloads that allow you to pass the parameters to the storedprocedures explicitly.

    With those simple steps, we are all hooked up to retrieve employee data into instances ofthe EmployeesDataTable collection type and work with them in the middle tier or clientlayer. You can then pass modified tables, rows, or individual values back down throughthe table adapter to persist those changes to the database.

    If you work directly against the tables in a database by dragging them out instead ofdragging out a stored procedure, SELECT, UPDATE, INSERT, and DELETE SQLstatements will be written and added to the table adapter for you. Those default queries

    use optimistic concurrency checking that compares the values of every column in thetable to their original values. If you wanted to modify these queries, you could just stepinto the Table Adapter Configuration Wizard and edit the queries.

    Adding Additional Queries to a Table Adapter

    You are not stuck with the basic CRUD queries that are generated for you by default for atable adapter. Using the designer, it is easy to add additional ad-hoc queries that can beused for things like retrieving Employees by country.

    To do so, right click on the table adapter in the designer and select Add Query from the

    context menu. This brings up the TableAdapter Query Configuration Wizard. The firststep, shown in Figure 6, allows you to specify whether you will base the new query

    8

  • 8/8/2019 building DAL using typed dataset

    9/14

    9

    method on a SQL statement, a new stored procedure, or an existing stored procedure.For the first two choices, you are then led through additional steps that allow you tospecify the supporting SQL that will be called by the table adapter. The option to Useexisting stored procedures allows you to point to another stored procedure that isalready in the target database and hook it up to either return rows, return a single value,or perform update, insert, or delete forms of operations.

    Figure 6: Selecting the query com man d type

    We need to create a stored procedure to support retrieving employees by country, so wemight as well do it through the wizard. Select the option to Create new stored procedureand press Next. The next step allows you to specify what the query type will be as shownin Figure 7.

    Figure 7: Selecting the qu ery type

  • 8/8/2019 building DAL using typed dataset

    10/14

    We are designing a stored procedure that returns rows of employees for a specifiedcountry, so the first option is the one we want for this case. Press Next and you arepresented with the step that allows you to specify the SQL that the stored procedure willwrap (see Figure 8). You can press the Query Builder button to get a dialog that helps youselect the appropriate column names and table names based on the database schema, oryou can just type in the query. Type in the SQL code shown in Figure 8.

    Figure 8: Specifying the body of the stored procedur e

    The wizard will infer the parameters for the stored procedure based on the parameterspresent in the query and the underlying types of the columns. It will also automaticallytry to map the columns to the corresponding columns in table as was done whenconfiguring the original stored procedure. Any problems will be reported by the wizard atthe end and will prevent the creation of the query method in the table adapter. Press Nextto move to the step that allows you to specify the name of the stored procedure that willbe added to the database to contain the specified SQL (see Figure 9). There is a button inthis step that will allow you to preview the full SQL script that is generated to create thestored procedure if you want.

    10

  • 8/8/2019 building DAL using typed dataset

    11/14

    11

    Figure 9: Naming the stored procedur e

    Press Next to move to the next step, which allows you to specify the name(s) of thecorresponding methods that will be added to the table adapter. For a procedure that willreturn rows, two methods will be created. One takes the table type (EmployeesDataTablein this case) as a parameter, as well as any parameters required by the stored procedure(country in this case). The second method just takes the parameters that are passed tothe stored procedure, if any, and returns a populated instance of the table type. The

    naming convention for these methods is FillByXXX and GetDataByXXX, where XXX issomething indicating the criteria that is specified in the parameters (see Figure 10).

    Figure 10: Namin g the table adapter query m ethods

  • 8/8/2019 building DAL using typed dataset

    12/14

    Pressing Next just takes you to the final step where the code generation is executed. Ifyou got everything correct, you will just see confirmation of what was performed by thewizard (see Figure 11). If you had a syntax error on your query or other problemsoccurred in the code generation process, they will be indicated here.

    Figure 11: Finishing the w izard

    Make sure to save the data set at this point. The wizard is actually adding code

    (annotations) to the XSD that is the true data set definition. The code generation of thecorresponding C# or VB code is not run until you save the data set XSD file.

    In addition to adding queries to an existing table adapter, you can add ad-hoc queries toa typed data set definition, not associated with any of its tables. To do so, just right clickin an empty area in the designer and you can add a query as described above. Doing sowill define methods on a table adapter type that gets named QueriesTableAdapter andsimply resides in the data set code generated file.

    Going Beyond the Designer

    What if you want to use a data reader to populate a custom business entity instead of

    populating a data table? What if you want to execute some custom validation of a columnvalue when it is changed by client code? Do these requirements mean you need toabandon the typed data set designer? Not necessarily. These things cannot be set updirectly in the designer, but are fairly straightforward to add through partial classextensions. If you add a class to your data access layer project, and put the following codein it:

    using System;using System.ComponentModel;using System.Data.SqlClient;using System.Data;

    namespace NorthwindDataAccess.EmployeesDataSetTableAdapters{

    12

  • 8/8/2019 building DAL using typed dataset

    13/14

    public partial class EmployeesTableAdapter : Component{

    public SqlDataReader GetReader(){

    return Adapter.SelectCommand.ExecuteReader(CommandBehavior.CloseConnection);

    }}}

    You will now have added a method to the table adapter that can execute theSelectCommand on the encapsulated data adapter directly, instead of going through theFill or GetData methods. You can then do whatever you need to with the internalConnection, Adapter, or Commands properties on the table adapter to access itsencapsulated objects.

    Likewise, if you wanted to add custom validation of columns, you can just add thatvalidation logic through a partial class extension of the table type itself:

    namespace NorthwindDataAccess{

    public partial class EmployeesDataSet : DataSet{

    public partial class EmployeesDataTable : DataTable{

    public override void BeginInit(){

    this.ColumnChanging += ValidateColumn;}

    void ValidateColumn(object sender,DataColumnChangeEventArgs e)

    { if(e.Column.ColumnName == "BirthDate"){

    if((DateTime)e.ProposedValue