Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Entity–attribute–value model wikipedia , lookup
Extensible Storage Engine wikipedia , lookup
Microsoft Jet Database Engine wikipedia , lookup
Microsoft SQL Server wikipedia , lookup
Clusterpoint wikipedia , lookup
Relational model wikipedia , lookup
Database model wikipedia , lookup
Abstract Factory DAAB Versions of this Document 3.0 Author Diego Gonzalez (Lagash Systems). 3.1 Chris J. Breisch (Quest Information Systems, Inc.) Introduction The idea behind this version of the DAAB is to provide an easy support to make the data access independent of the ADO.NET provider. The DAAB is a great tool that allows very easy access to the Sql Server ADO.NET driver using single line methods for example ExecuteDataset, ExecuteReader, etc. The problem behind this implementation is that every method was implemented as a static method of a single class named SqlHelper. This class uses the SqlServer ADO.NET provider only and it's not easy to extend this class using OOP techniques, the only way to extend this class in order to use a different ADO.NET driver is rewriting the code whole. In ADO.NET some interfaces are provided to create the ADO.NET providers. Those interfaces are: IDbConnection, IDbTransaction, IDbCommand, IDataParameter, IDataReader, IDataRecord, etc. Every ADO.NET provider must implement those interfaces on its implementation classes, so there's an open door to write code that doesn't knows which ADO.NET provider it is using if the code is using interfaces instead of concrete classes. The ADO.NET interfaces define a common base of functionality supported by almost any database. Some drivers may define new methods that aren't defined on the interface. For example, the SQL Server managed provider defines a method named ExecuteXmlReader that uses a SQL Server 2000 feature to return XML data from the database. To get data from a table using ADO.NET without the DAAB, your code might look something like this: using( SqlConnection conn = new SqlConnection( /* conn string */ )) { conn.Open(); using( SqlCommand cmd = conn.CreateCommand() ) { cmd.CommandText = “SELECT * FROM titles;”; using( SqlDataReader reader = cmd.ExecuteReader() ) { while( reader.Read() ) { // process the result set. } } } } The following code demonstrates how to create code that uses only the ADO.NET interfaces to access the database: using( IDbConnection conn = GetConnection( /* ... */ ) ) { conn.Open(); using( IDbCommand cmd = conn.CreateCommand() ) { cmd.CommandText = “SELECT * FROM titles;”; using( IDataReader reader = cmd.ExecuteReader() ) { while( reader.Read() ) { // process the result set. } } } } In the previous code the GetConnection method is supposed to return any instance of the IDbConnection interface. The following code uses the interface methods to interact with the database. In this version of the DAAB the main helper class is named AdoHelper and it defines the same methods that were defined on SqlHelper, but using the ADO.NET interfaces instead using the SqlServer implementations. For example, the method ExecuteDataset is implemented using only ADO.NET interfaces so it can be executed using any ADO.NET provider implementation. Goals and Non-Goals Goals Complete provide a complete interface support for ADO.NET. Some important classes on ADO.NET do not define common interfaces or there’s no way to get an instance of the following classes: XxxCommandBuilder, XxxDataAdapter. Use the same base code on AdoHelper on any ADO.NET provider supporting the common interfaces. Out of Scope Provide a single SQL language for every provider. Some ADO.NET providers use different SQL syntax, if an application wants to use any database it should use any provider, AND the same SQL syntax for all of them. It’s not a goal for this implementation to solve this problem. The application must use ANSI SQL when possible, and should be aware for some differences between providers. Using the Abstract DAAB The AdoHelper is an abstract class, so it can’t be instantiated, this class can only be used by derived classes. For example, the classes named SqlServer, Odbc, OleDb, and Oracle extend AdoHelper. The AdoHelper class defines some static methods that can be used to create AdoHelper derived classes, using the Abstract Factory design pattern. The method used to create an instance of the AdoHelper derived class is CreateHelper, this method is defined with two overloads: Using the assembly and type name of a DAAB provider. The overload receiving two string parameters uses the assembly name and the type name to load the type and creating an instance using reflection. Using a configuration section. This overload receives a single string with the name of an “alias”. The aliases are defined on the default configuration file for the application domain. Usually the file is named using the EXE file name with a “.config” extension, for example “Notepad.exe.config”. Please refer to the “Configuring Applications” section on the Framework SDK documentation. If you chose the configuration section overload you must have a .config file with your application with the following structure: <configuration> <configSections> <section name="daabProviders" type="GotDotNet.ApplicationBlocks.Data.DAABSectionHandler, GotDotNet.ApplicationBlocks.Data"> </section> </configSections> <daabProviders> <daabProvider alias="accounts" assembly="GotDotNet.ApplicationBlocks.Data" type="GotDotNet.ApplicationBlocks.Data.SqlServer" /> <daabProvider alias="credits" assembly="GotDotNet.ApplicationBlocks.Data" type="GotDotNet.ApplicationBlocks.Data.OleDb" /> </daabProviders> </configuration> You must have a <configSections/> defined exactly as above, although you can choose whatever name you wish. The name “daabProviders” was chosen because it will hold the “daabProvider” elements. This configuration file defines two different providers using the aliases “accounts” and “credits”. The “accounts” database uses the SqlServer DAAB provider and the “credits” uses the OleDb provider. The CreateHelper overload with a single string parameter uses the configuration file to get the assembly and type name for the alias provided as a parameter and creates an instance using reflection. The following code demonstrates how to use the CreateHelper method overloads: using GotDotNet.ApplicationBlocks.Data; // ... AdoHelper helper = AdoHelper.CreateHelper( “GotDotNet.ApplicationBlocks.Data”, “GotDotNet.ApplicationBlocks.Data.SqlServer” ); AdoHelper otherHelper = AdoHelper.CreateHelper( “credits” ); Using the Online Help This version of the DAAB comes with online documentation that can be integrated with Visual Studio 2003 (integration with Visual Studio 2005 does not work at this time). The on-line help is not installed integrated into Visual Studio by default. There is a shortcut labeled ‘Install On-line Help’ to do this located in the Start Menu folder for the DAAB, usually ‘Start/All Programs/GotDotNet/Application Blocks/Data 3.1’. An uninstall shortcut has been provided in this same folder, should you later decide you prefer not to have the help integrated. Also, the help may be access directly through a help file. The shortcut to this is in the same folder and labeled ‘Documentation’. Note: The on-line help is automatically removed from the Visual Studio help upon uninstallation of the DAAB. Creating a provider for the Abstract DAAB To create an Abstract DAAB helper a class must be created extending the AdoHelper abstract class. Any AdoHelper derived class must implement the following methods: Method Description IDbConnection GetConnection( string connectionString ) This method must return an instance of an IDbConnection implementation for the ADO.NET provider. IDbDataAdapter GetDataAdapter() Must return an instance of a class implementing IDbDataParameter for the ADO.NET provider. void DeriveParameters( IDbCommand cmd ) This method must call the DerivedParameters on the XxxCommandBuilder for the ADO.NET provider specifying the “cmd” parameter. If the ADO.NET provider does not provide such implementation, the AdoHelper derived class must implement similar functionality. Check the ADO.NET documentation on the Framework SDK. IDataParameter GetParameter() Must return an IDataParameter derived class which is not connected to any connection. XmlReader ExecuteXmlReader( IDbCommand cmd ) Must return an XmlReader containing the resultset generated by the command. One way to do this might be to fill a data set and then use DataSet.GetXml to return an XmlReader object. See the implementations in the provided helpers for examples. Provider specific code to set up the updating/ed event handlers used by UpdateDataset. This code is fairly generic void AddUpdateEventHandlers (IDbDataAdapter dataAdapter, RowUpdatingHandler rowUpdatingHandler, RowUpdatedHandler rowUpdatedHandler) and can be copied nearly verbatim from any of the existing helpers. IDataParameter[] GetDataParameters(int size) Must return an array of IDataParameters of the specified size. IDataParameter GetBlobParameter(IDbConnection connection, IDataParameter p) Must return an IDataParameter with BLOB data. Generally this is just a pass-thru method. See the SqlServer.[cs, vb] file for details on the pass-thru, and Oracle.[cs, vb] for an example of this method that actually does something. In addition, the helper may override any or all of the following methods. The base class implementations are generally all that’s required, however special cases may exist that need to be handled in the derived class. IDataParameter GetParameter( string name, object value ) Return an IDataParameter with ParameterName = name and Value = value IDataParameter GetParameter ( string name, DbType dbType, int size, ParameterDirection direction ) Return an IDataParameter with ParameterName = name, DbType = type, Size = size, and ParameterDirection = direction IDataParameter GetParameter ( string name, DbType dbType, int size, string sourceColumn, DataRowVersion sourceVersion ) Return an IDataParameter with ParameterName = name, DbType = type, Size = size, SourceColumn = sourceColumn, SourceVersion = sourceVersion. void AttachParameters(IDbCommand command, IDataParameter[] commandParameters) Essentially takes the array of IDataParameters and attaches them to the IDbCommand object via it’s Parameters collection. Typically this might get overridden if null values need to be handled in some special way. void CleanParameterSyntax(IDbCommand command) This cleans up the parameter syntax for the call. ODBC in particular takes a much different syntax than the other ADO.NET providers. This is a way to have a more “generalized” SQL code across providers. This method is not designed to be perfect and handle all issues. See the “Out of Scope” section above. void PrepareCommand(IDbCommand command, IDbConnection connection, IDbTransaction transaction, CommandType commandType, string This method sets up everything for the IDbCommand. It associates it with any existing transactions, opens a connection commandText, IDataParameter[] commandParameters, out bool mustCloseConnection ) if necessary, sets up the command text and any parameters, and returns a bool indicating whether it opened a connection or not. void ClearCommand(IDbCommand command ) This method clears the IDbCommand objects parameter collection. It’s use is considered deprecated due to too many inconsistencies between providers and situations. All of the Execute…,Fill…,Update… methods Generally the only methods that might need to be overridden in a helper subclass are the ones that take only a command object, such as ExecuteXmlReader(IDbCommand cmd). All of the other methods for a given block eventually call this method. IDbCommand CreateCommand(…) Must return an IDbCommand object. The base class uses Connection.CreateCommand(). This is usually sufficient. IDataParameter[] GetSpParameterSet(…) Must return an IDataParameter array containing parameter information for the stored procedure, spName. If the given database does not have stored procedures, obviously this method is not necessary. First, this method looks in the ParameterCache for the information. If not found, the base class method uses the XxxCommandBuilder.DeriveParameters method from the appropriate provider. (note: the SQL Server implementation, SqlCommandBuilder.DeriveParameters is buggy, so the SqlServer helper provides its own implementation of DeriveParameters). This method also stores the retrieved information in the ParameterCache for later use. IDataParameter[] GetCachedParameterSet(string connectionString, string commandText) Must return an IDataParameter array containing parameter information for the stored procedure specified in commandText. This method uses the ParameterCache to quickly retrieve the parameter information. Every method defined on the AdoHelper base class is defined virtual, it means any method could be overridden on the derived class if the use of the interfaces within the AdoHelper class is not supported by the interfaces implementation on the ADO.NET provider. Only a summary of methods is shown... AdoHelper + GetConnection ( ) # GetDataAdapter ( ) # DeriveParameters ( ) + GetParameter ( ) + CreateHelper ( ) + CreateHelper ( ) + GetParameter ( ) # AttachParameters ( ) # AssignParameterValues ( ) # AssignParameterValues ( ) # PrepareCommand ( ) # ClearCommand ( ) + ExecuteDataset ( ) + ExecuteDataset ( ) + ExecuteDataset ... ( ) ADOHelperParameterCache ~ CloneParameters ( ) + CacheParameterSet ( ) + GetCachedParameterSet ( ) DAABSectionHandler + Create ( ) ProviderAlias + «property» AssemblyName : string + «property» TypeName : string - _assemblyName : string - _typeName : string + ProviderAlias ( ) + «get» AssemblyName ( ) + «get» TypeName ( ) Odbc SqlServer OleDb + Odbc ( ) + GetConnection ( ) # GetDataAdapter ( ) # DeriveParameters ( ) + GetParameter ( ) # PrepareCommand ( ) + SqlServer ( ) + GetConnection ( ) # GetDataAdapter ( ) # DeriveParameters ( ) + GetParameter ( ) # ClearCommand ( ) + ExecuteXmlReader ( ) + ExecuteXmlReader ( ) + ExecuteXmlReader ( ) + ExecuteXmlReader ( ) + ExecuteXmlReader ( ) + ExecuteXmlReader ( ) + ExecuteXmlReaderTypedParams ( ) + ExecuteXmlReaderTypedParams ( ) + OleDb ( ) + GetConnection ( ) # GetDataAdapter ( ) # DeriveParameters ( ) + GetParameter ( ) Figure 1 – Class diagram Note: the above diagram is out of date. The PetShop DAL vs Abstract DAAB The PetShop sample application available on MSDN is a 3-tier application, it means the application is divided in Presentation Layer (PL), Business Logic Layer (BLL) and Data Access Layer (DAL). The PetShop application implements a Data Access Layer that can be used with Oracle and SQL Server ADO.NET providers. The PetShop developers decide to implement the DAL using interfaces; those interfaces are used by the BLL to access the database to get and update the information. There’s an interface for each entity on the system with the methods needed by the BLL.: Interface Methods IAccount SignIn, GetAddress, Insert, Update IInventory CurrentQtyInStock, TakeStock IItem GetItemsByProduct, GetItem IOrder Insert, GetOrder IProduct GetProductsByCategory, GetProductsBySearch IProfile GetBannerPath There are two implementations of each interface, for each supported ADO.NET driver. The BLL uses a factory class to get an instance for the DAL which is configured on the .config file. Each implementation uses driver specific helper classes to perform the operations defined by the interface. The helper classes defined OraHelper and SqlHelper define a subset of the operations available on DAAB, the OraHelper was completely rewritten using driver specific classes for each ADO.NET provider. So the DAL is divided on two classes: The interface implementation for each entity and the XxxHelper and the provider specific helpers named OraHelper and SqlHelper. Comparing the OraHelper and the SqlHelper code there are no many differences between them, the Abstract DAAB could be used here in fact (if an OracleHelper is developed). So the entity specific data access code, only provides the correct SQL statements and parameter values. The reason to rewrite the whole data access code for each entity and each ADO.NET providers is because some issues can’t be performed with generic code, for example: SQL language. There are many differences on the SQL statements supported by each ADO.NET provider. Parameter markers. Using the SqlCommand class parameters can be placed on the CommandText using a “@” preceding the parameter name, and the Parameters collection can be filled with the parameter names and values. The SqlCommand class then executes the SQL statement using the correct data types. The same feature is available in Oracle using “:” AdoHelper Members Public Static (Shared) Methods CreateHelper Overloaded. Create an AdoHelper for working with a specific provider (i.e. Sql, Odbc, OleDb, Oracle) Public Instance Methods CacheParameterSet CleanParameterSyntax CreateCommand DeriveParameters Overloaded. Add parameter array to the cache This method cleans up the parameter syntax for the provider Overloaded. Simplify the creation of a IDbCommand object by allowing a stored procedure and optional parameters to be provided Calls the CommandBuilder.DeriveParameters method for the specified provider, doing Equals (inherited from Object) ExecuteDataset ExecuteDatasetTypedParams ExecuteNonQuery ExecuteNonQueryTypedParams ExecuteReader any setup and cleanup necessary Determines whether the specified Object is equal to the current Object. Overloaded. Execute a stored procedure via a IDbCommand (that returns a resultset) against the specified IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. Overloaded. Execute a stored procedure via a IDbCommand (that returns a resultset) against the specified IDbTransaction using the dataRow column values as the stored procedure's parameters values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on row values. Overloaded. Execute a stored procedure via a IDbCommand (that returns no resultset) against the specified IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. Overloaded. Execute a stored procedure via a IDbCommand (that returns no resultset) against the specified IDbTransaction using the dataRow column values as the stored procedure's parameters values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on row values. Overloaded. Execute a stored procedure via a IDbCommand (that returns a resultset) against the specified IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the stored procedure (the first time ExecuteReaderTypedParams ExecuteScalar ExecuteScalarTypedParams ExecuteXmlReader ExecuteXmlReaderTypedParams FillDataset each stored procedure is called), and assign the values based on parameter order. Overloaded. Execute a stored procedure via a IDbCommand (that returns a resultset) against the specified IDbTransaction using the dataRow column values as the stored procedure's parameters values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. Overloaded. Execute a stored procedure via a IDbCommand (that returns a 1x1 resultset) against the specified IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. Overloaded. Execute a stored procedure via a IDbCommand (that returns a 1x1 resultset) against the specified IDbTransaction using the dataRow column values as the stored procedure's parameters values. This method will query the database to discover the parameters for the stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. Overloaded. Execute an IDbCommand (that returns a resultset) against the provided IDbConnection. Overloaded. Execute a stored procedure via a IDbCommand (that returns a resultset) against the specified IDbConnection using the dataRow column values as the stored procedure's parameters values. This method will assign the parameter values based on parameter order. Overloaded. Execute a stored procedure via a IDbCommand (that returns a resultset) against the specified IDbTransaction using the provided parameter values. This method will query the database to discover the parameters GetCachedParameterSet GetConnection GetDataAdapter GetParameter GetSpParameterSet UpdateDataset for the stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. Overloaded. Retrieve a parameter array from the cache Returns an IDbConnection object for the given connection string Returns an IDbDataAdapter object Overloaded. Get an IDataParameter for use in a SQL command Overloaded. Retrieves the set of IDataParameterParameters appropriate for the stored procedure Overloaded. Executes the respective command for each inserted, updated, or deleted row in the DataSet. Protected Instance Constructors AdoHelper Constructor Initializes a new instance of the AdoHelper class. Protected Instance Fields m_rowUpdated m_rowUpdating Internal handler used for bubbling up the event to the user Internal handler used for bubbling up the event to the user Protected Instance Methods AddUpdateEventHandlers AssignParameterValues AttachParameters ClearCommand Provider specific code to set up the updating/ed event handlers used by UpdateDataset Overloaded. This method assigns dataRow column values to an IDataParameterCollection This method is used to attach array of IDataParameters to a IDbCommand. This method will assign a value of DbNull to any parameter with a direction of InputOutput and a value of null. This behavior will prevent default values from being used, but this will be the less common case than an intended pure output parameter (derived as InputOutput) where the user provided no input value. This method clears (if necessary) the connection, transaction, command type and parameters from the provided command ExecuteReader GetBlobParameter GetDataParameters PrepareCommand RowUpdated RowUpdating SetCommand Overloaded. Execute a IDbCommand (that returns a resultset) against the database specified in the connection string. Handle any provider-specific issues with BLOBs here by "washing" the IDataParameter and returning a new one that is set up appropriately for the provider. Returns an array of IDataParameters of the specified size This method opens (if necessary) and assigns a connection, transaction, command type and parameters to the provided command This method consumes the RowUpdatedEvent and passes it on to the consumer specifed in the call to UpdateDataset This method consumes the RowUpdatingEvent and passes it on to the consumer specifed in the call to UpdateDataset Set up a command for updating a dataset. Diego González is an MVP for .NET/C# and a co-founder of Lagash, a high-tech company based in Buenos Aires, Argentina. Diego was recently working close to PAG (msdn.microsoft.com/practices) in the development of the Application Blocks as an Architect and Lead Developer. He’s an active member in the .NET community and he’s a common speaker in Microsoft events in Argentina, you can get in touch with Diego at [email protected]. Chris J. Breisch is an MCSD and MCDBA and works for Quest Information Systems, Inc., a consulting company based in Indianapolis, Indiana, USA. You can get to know more about him and his thoughts on .NET by reading his blog at http://geekswithblogs.net/cbreisch