How to Write a Provider Model

简介:

Published: 21 Jan 2008
By: Keyvan Nayyeri
Download Sample Code (Ref: http://dotnetslackers.com/articles/designpatterns/HowToWriteAProviderModel.aspx)

 

In this article Keyvan teaches you how to write your own data provider for .NET applications using the provider model.

After the birth of .NET 2.0, the provider model became common in .NET development. Most built-in features that deal with data support providers. Many third party applications, components and libraries are using data providers, too. In my opinion talking about data providers in .NET is unnecessary because almost all the readers of this article know the role of data providers in .NET development. After the .NET Framework 2.0 was released, data providers became a very common part of .NET development. Not only were there many common built-in data providers in .NET, but community members and companies created third party components for other data storage systems like Oracle or MySQL.

Nowadays you can't find anything in .NET that doesn't have appropriate data providers for all common data storage systems. Usually these data providers are free and open source so you can adapt them for your own needs.

However, one thing that isn't documented very well is how to write your data provider model for .NET applications. I looked for such a resource but didn't obtain any good results, so I decided to write it myself.

In this article you will learn how to write your own data provider in order to bring this common feature to your .NET components or applications.

There are some benefits in using the data provider model in a component or application. You can probably guess, but let me outline them here:

  • You abstract your data manipulation portion of the code and isolate it from other. In this way, code becomes easier to write.
  • It lets you easily switch between different data storage systems. You can use SQL Server or Oracle or XML just by implementing the appropriate data provider.
  • You can use multiple data storage systems in parallel for different parts of your application or your components. In other words you can, for example, use SQL Server to store data about products and use XML to store details about orders.

But how can a data provider be written in .NET? To demonstrate the concepts in this article I will use the C# language, but Visual Basic can be used as well, without any differences in the structure. Only a code translation is required.

In a nutshell, the process of implementing the provider model consists of creating four main classes:

  • Main data provider class.
  • Data provider collection class.
  • Data provider configuration class.
  • Data provider manager class.

After writing these classes you usually need to implement at least a default data provider for your application or component, as well as a SQL Server or XML data provider. This lets your users be able to use your data provider without implementing a custom one. It also acts as a good implementation example to everyone who wants to write a new provider.

In the next section I'll discuss the implementation of each of the above mentioned classes with the help of a simple example. I will also implement a default SQL provider for this example.

Main Data Provider Class

The main data provider class is where you define the abstract representation of all the data manipulation methods that you need in your provider. Here you write a public abstract class that must be derived from the System.Configuration.Provider.ProviderBase base class and has some public abstract methods that must be implemented by different data providers.

As a result, you always need to have a reference to System.Configurationin your provider project.

Let's start by taking a look at Listing 1, which shows the code for the main data provider class.

Listing 1: Code for the main data provider class

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.Configuration.Provider;   
  6.   
  7. namespace SampleProvider   
  8. {   
  9.     public abstract class DataProvider : ProviderBase   
  10.     {   
  11.         // Adds a text value for a specified ID to the storage   
  12.         public abstract void AddText(int id, string text);   
  13.   
  14.         // Gets the text associated to a specified ID   
  15.         public abstract string GetText(int id);   
  16.     }   
  17. }  

As you see, the main provider class is an abstract class derived from ProviderBase. It defines two public abstract methods to add a text value for a given ID or get a text for a specified ID. These two methods must be implemented in different data provider classes.

You should understand that this class is the place where you write the abstract definition of your provider methods.

Data Provider Collection

The data provider collection class is the portion of your implementation that takes care of provider configuration. Here you implement the behavior of your configuration elements for a list of providers.

To implement the data provider collection class you need to derive it from the System.Configuration.ProviderCollection base class. ProviderCollection is a class that exposes some properties and methods to work with the list of various data providers declared in your configuration file. This class is simply a collection class that implements the ICollection and IEnumerable base interfaces, so there isn't any major point to mention about it.

The key point here is overriding the default behavior of the ProviderCollection class in order to return an instance of your main data provider class for a specified index.

Listing 2 shows the code for the DataProviderCollection class, where I've implemented my data provider collection.

Listing 2: Data provider collection class

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.Configuration.Provider;   
  6.   
  7. namespace SampleProvider   
  8. {   
  9.     public class DataProviderCollection : ProviderCollection   
  10.     {   
  11.         // Return an instance of DataProvider   
  12.         // for a specified provider name   
  13.         new public DataProvider this[string name]   
  14.         {   
  15.             get { return (DataProvider)base[name]; }   
  16.         }   
  17.     }   
  18. }  

Usually, it is enough to start from a skeleton implementation like the one shown above and then use it for your concrete providers by replacing the names of the parameters and types.

Data Provider Configuration

The next step in developing a provider is writing a data provider configuration class. It contains the logic to manage the configuration of your provider and to get user's parameters and pass them to your provider.

Data providers work based on the configuration files of your applications. For desktop applications, this is the App.config file and, for web applications, it is the Web.config file. Please note that there is no difference in the implementation with respect to the structure of these files.

To implement this class you need to derive it from the System.Configuration.ConfigSection base class and add some code for your own data provider requirements.

In your configuration class, you need to define a property of type System.Configuraiton.ProviderSettingsCollection for the collection of provider settings. The property needs to return an instance of this type by getting it from the base class.

In addition, you can also add as many properties as you like, based on the different parameters that you need to extract from the configuration sections.

All these properties must be decorated with the ConfigurationPropertyattribute. This attribute gets a string name as a parameter and has an optional DefaultValue property for specifying a default value.

Listing 3 updates the sample provider with a data provider configuration class.

Listing 3: The data provider configuration class

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.Configuration;   
  6.   
  7. namespace SampleProvider   
  8. {   
  9.     public class DataProviderConfiguration : ConfigurationSection   
  10.     {   
  11.         [ConfigurationProperty("providers")]   
  12.         public ProviderSettingsCollection Providers   
  13.         {   
  14.             get  
  15.             {   
  16.                 return (ProviderSettingsCollection)base["providers"];   
  17.             }   
  18.         }   
  19.   
  20.         [ConfigurationProperty("default", DefaultValue = "SqlProvider")]   
  21.         public string Default   
  22.         {   
  23.             get  
  24.             {   
  25.                 return (string)base["default"];   
  26.             }   
  27.             set  
  28.             {   
  29.                 base["default"] = value;   
  30.             }   
  31.         }   
  32.     }   
  33. }  

In this code I have defined two properties. The first property is called Providers and it's of type ProviderSettingsCollection. It represents the <providers /> element in the application's configuration file. The value is retrieved by the base ConfigurationSection class.

The second property, Default, gets or sets the default provider name as a string. By default, this value is set to SqlProvider.

Data Provider Manager

The last step in developing the data provider is creating the data provider manager class. To implement this class you don't need to inherit from a base class or implement an interface. Actually, you just need to write code based on your requirements.

The main purpose of the data provider manager is to initialize a concrete provider and load user configuration settings in memory.

During initialization, you need to retrieve the configuration section for your data provider and check its correctness. Then, you create an instance of the concrete provider in memory.

In order to work, your data provider manager must have a static public constructor that calls the Initialize method.

Listing 4 shows the code for my DataProviderManager class.

Listing 4: The data provider manager class

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.Configuration;   
  6. using System.Web.Configuration;   
  7.   
  8. namespace SampleProvider   
  9. {   
  10.     public class DataProviderManager   
  11.     {   
  12.         private static DataProvider defaultProvider;   
  13.         private static DataProviderCollection providers;   
  14.   
  15.         static DataProviderManager()   
  16.         {   
  17.             Initialize();   
  18.         }   
  19.   
  20.         private static void Initialize()   
  21.         {   
  22.             DataProviderConfiguration configuration =   
  23.                 (DataProviderConfiguration)   
  24.                 ConfigurationManager.GetSection("SampleProvider");   
  25.   
  26.             if (configuration == null)   
  27.                 throw new ConfigurationErrorsException   
  28.                     ("SampleProvider configuration section is not set correctly.");   
  29.   
  30.             providers = new DataProviderCollection();   
  31.   
  32.             ProvidersHelper.InstantiateProviders(configuration.Providers   
  33.                 , providers, typeof(DataProvider));   
  34.   
  35.             providers.SetReadOnly();   
  36.   
  37.             defaultProvider = providers[configuration.Default];   
  38.   
  39.             if (defaultProvider == null)   
  40.                 throw new Exception("defaultProvider");   
  41.         }   
  42.   
  43.         public static DataProvider Provider   
  44.         {   
  45.             get  
  46.             {   
  47.                 return defaultProvider;   
  48.             }   
  49.         }   
  50.   
  51.         public static DataProviderCollection Providers   
  52.         {   
  53.             get  
  54.             {   
  55.                 return providers;   
  56.             }   
  57.         }   
  58.     }   
  59. }  

In the above code snippet, I’ve declared two static fields that hold the current provider (Provider) and a collection of all defined providers in the configuration file (Providers). I’m going to set appropriate values for these two fields in the initialization phase.

In the static public constructor I've called the  Initialize method, which simply gets an instance of the  DataProviderConfiguration class by calling the  ConfigurationManager.GetSection method with the configuration element name that I've chosen for my provider (in this case, it is  <ProviderSample />).

 

The next step was to set the Providers field to the appropriate value by calling the ProvidersHelper.InstantiateProviders method, which is a very good helper method provided by the .NET Framework. Finally, I set my providers to be read-only and then I set the default provider.

Default SQL Data Provider

At this point we're done with the implementation of the data provider model. It's very common to have a default data provider in your model to let users use it for their needs. This provider is often implemented for the most appropriate storage system.

Here I'm going to implement a SQL Server data provider for my sample provider model, to show you how easy the implementation can be. This is a result of the abstraction that we achieve by leveraging the provider model.

The code in Listing 5 shows a SqlDataProvider class that implements the SQL data provider.

Listing 5: The SQLDataProvider class

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.Data.SqlClient;   
  6. using System.Configuration;   
  7. using System.Collections.Specialized;   
  8.   
  9. namespace SampleProvider   
  10. {   
  11.     public class SqlDataProvider : DataProvider   
  12.     {   
  13.         private string _connectionString = string.Empty;   
  14.   
  15.         public override void Initialize(string name, NameValueCollection config)   
  16.         {   
  17.             base.Initialize(name, config);   
  18.   
  19.             this._connectionString = config["connectionString"];   
  20.   
  21.             if (string.IsNullOrEmpty(this._connectionString))   
  22.                 throw new ConfigurationErrorsException   
  23.                     ("connectionString must be set to the appropriate value");   
  24.         }   
  25.   
  26.         public override void AddText(int id, string text)   
  27.         {   
  28.             using (SqlConnection connection = new SqlConnection(this._connectionString))   
  29.             {   
  30.                 // For real world scnearios use parameterized SQL to avoid SQL injections!   
  31.                 string commandText = string.Format   
  32.                     ("INSERT INTO SampleTable (Id, Value) VALUES ({0}, '{1}')", id, text);   
  33.                 SqlCommand command = new SqlCommand(commandText, connection);   
  34.                 command.CommandType = System.Data.CommandType.Text;   
  35.   
  36.                 connection.Open();   
  37.                 command.ExecuteNonQuery();   
  38.                 connection.Close();   
  39.                 command.Dispose();   
  40.             }   
  41.         }   
  42.   
  43.         public override string GetText(int id)   
  44.         {   
  45.             string result = string.Empty;   
  46.   
  47.             using (SqlConnection connection = new SqlConnection(this._connectionString))   
  48.             {   
  49.                 string commandText = string.Format("SELECT Value FROM SampleTable WHERE Id = {0}", id);   
  50.                 SqlCommand command = new SqlCommand(commandText, connection);   
  51.                 command.CommandType = System.Data.CommandType.Text;   
  52.   
  53.                 connection.Open();   
  54.   
  55.                 result = command.ExecuteScalar() as string;   
  56.   
  57.                 connection.Close();   
  58.                 command.Dispose();   
  59.             }   
  60.   
  61.             return result;   
  62.         }   
  63.     }   
  64. }  

Note that you need to inherit from your main data provider class and implement this abstract base class for your data provider. In addition override methods and write your own code, you usually need to override the Initialize method to get the name of the provider model along with a NameValueCollection instance with all the attributes in your provider configuration section. Using this NameValueCollection instance, you can retrieve information (like a connection string) from the configuration file.

Configuration

Okay, the provider model and a sample SQL data provider for this model are ready. Now we can configure our applications to use the provider model.

To configure the application for using the provider model first we need to define our provider configuration section in <configSections />element, which is the direct child element of <configuration />. Here we can add a <section /> element with the name of the provider model configuration section element and the type of our data provider configuration class. Based on your needs, you can also embed the <section /> element within a <sectionGroup /> element.

Listing 6 shows the configuration for my sample provider model in an ASP.NET 3.5 web application configuration file. Please pay more attention to the last <section /> element in bold.

Listing 6: Configuration of the sample provider

  1. <configSections>  
  2.   <sectionGroup name="system.web.extensions"    
  3. type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions,   
  4. Version=3.5.0.0, Culture=neutralPublicKeyToken=31BF3856AD364E35">  
  5.     <sectionGroup name="scripting"    
  6. type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions,    
  7. Version=3.5.0.0, Culture=neutralPublicKeyToken=31BF3856AD364E35">  
  8.       <section name="scriptResourceHandler"    
  9. type="System.Web.Configuration.ScriptingScriptResourceHandlerSection,    
  10. System.Web.Extensions, Version=3.5.0.0, Culture=neutralPublicKeyToken=31BF3856AD364E35" requirePermission="false"    
  11. allowDefinition="MachineToApplication"/>  
  12.       <sectionGroup name="webServices"    
  13. type="System.Web.Configuration.ScriptingWebServicesSectionGroup,    
  14. System.Web.Extensions, Version=3.5.0.0, Culture=neutral,    
  15. PublicKeyToken=31BF3856AD364E35">  
  16.         <section name="jsonSerialization"    
  17. type="System.Web.Configuration.ScriptingJsonSerializationSection,    
  18. System.Web.Extensions, Version=3.5.0.0, Culture=neutral,    
  19. PublicKeyToken=31BF3856AD364E35" requirePermission="false"    
  20. allowDefinition="Everywhere" />  
  21.         <section name="profileService"    
  22. type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions,   
  23. Version=3.5.0.0, Culture=neutralPublicKeyToken=31BF3856AD364E35"    
  24. requirePermission="false" allowDefinition="MachineToApplication" />  
  25.         <section name="authenticationService"    
  26. type="System.Web.Configuration.ScriptingAuthenticationServiceSection,   
  27. System.Web.Extensions, Version=3.5.0.0, Culture=neutral,    
  28. PublicKeyToken=31BF3856AD364E35" requirePermission="false"    
  29. allowDefinition="MachineToApplication" />  
  30.         <section name="roleService"    
  31. type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions,   
  32. Version=3.5.0.0, Culture=neutralPublicKeyToken=31BF3856AD364E35"    
  33. requirePermission="false" allowDefinition="MachineToApplication" />  
  34.       </sectionGroup>  
  35.     </sectionGroup>  
  36.   </sectionGroup>  
  37.   <section name="SampleProvider"  
  38.            type="SampleProvider.DataProviderConfiguration, SampleProvider" />  
  39. </configSections>  

The next and last step is to add the main configuration section for the provider model within the <configuration /> element.

Listing 7 shows how I added this section to my configuration file.

Listing 7: Main configuration section

  1. <SampleProvider default="SqlProvider">  
  2.   <providers>  
  3.     <add name="SqlProvider"  
  4.         type="SampleProvider.SqlDataProvider, SampleProvider"  
  5.         connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\DB.mdf;Integrated    
  6. curity=True;User Instance=True/>  
  7.   </providers>  
  8. </SampleProvider>  

Test

Now we're ready to test the provider model. I created an ASP.NET 3.5 web application project that has a simple page which gets input from the user and adds data to database. Then, it retrieves a value associated to a specified ID.

Listing 8 shows the source code for the main page of this web application, which is written using a code-inline style.

Listing 8: The main page of the web application

  1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SampleSite._Default" %>  
  2.   
  3. <%@ Import Namespace="SampleProvider" %>  
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
  5.  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  6. <html xmlns="http://www.w3.org/1999/xhtml">  
  7.   
  8. <script runat="server" language="c#">  
  9.     protected void btnAdd_Click(object sender, EventArgs e)   
  10.     {   
  11.         try   
  12.         {   
  13.             DataProviderManager.Provider.AddText   
  14.                 (Convert.ToInt32(txtId.Text), txtValue.Text);   
  15.             lblResult.Text = "Data added successfully!";   
  16.         }   
  17.         catch (Exception ex)   
  18.         {   
  19.             lblResult.Text = ex.Message + "<br /> " + ex.StackTrace;   
  20.         }   
  21.     }   
  22.   
  23.     protected void btnSelect_Click(object sender, EventArgs e)   
  24.     {   
  25.         try   
  26.         {   
  27.             lblData.Text = DataProviderManager.Provider.GetText   
  28.                 (Convert.ToInt32(txtIdToSelect.Text));   
  29.         }   
  30.         catch (Exception ex)   
  31.         {   
  32.             lblData.Text = ex.Message + "<br /> " + ex.StackTrace;   
  33.         }   
  34.     }   
  35. </script>  
  36.   
  37. <head runat="server">  
  38.     <title>Untitled Page</title>  
  39. </head>  
  40. <body>  
  41.     <form id="form1" runat="server">  
  42.     <div>  
  43.         <h1>  
  44.             Add Data</h1>  
  45.         Enter ID: <asp:TextBox ID="txtId" runat="server"></asp:TextBox><br />  
  46.         Enter Value: <asp:TextBox ID="txtValue" runat="server"></asp:TextBox><br />  
  47.         <asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click" /><br />  
  48.         <asp:Label ID="lblResult" runat="server" Text=""></asp:Label>  
  49.     </div>  
  50.     <div>  
  51.         <h1>  
  52.             Select Data</h1>  
  53.         Enter ID: <asp:TextBox ID="txtIdToSelect" runat="server"></asp:TextBox><br />  
  54.         <asp:Button ID="btnSelect" runat="server" Text="Select" OnClick="btnSelect_Click" /><br />  
  55.         <asp:Label ID="lblData" runat="server" Text=""></asp:Label>  
  56.     </div>  
  57.     </form>  
  58. </body>  
  59. </html>  

Now, I run this application and add my values to the database. Figure 1 shows the output when I add my data to the database.

Figure 1: Adding data

Figure 2 shows the output when I select the data from the database.

Figure 2: Selecting the data

Summary

In this article I showed you how to implement the data provider model for your .NET applications and libraries. You can use it for your own needs or as a template for implementing providers for various data storage systems.

After giving an introduction and a quick overview of the process of implementing a provider model in .NET, I talked about implementing the main data provider class. Then, I talked about implementing the data provider collection and data provider configuration classes that can be used for your data provider configuration.

After that, I discussed about the data provider manager class, which is responsible for loading and using a concrete provider.

Next, I wrote a default SQL data provider for my example, to show you how easy it can be to implement a data provider model in .NET. I've then configured an ASP.NET 3.5 web application to test the provider model.

 

转载请注明出处[ http://samlin.cnblogs.com/] 
作者赞赏
 


刚做的招标网: 八爪鱼招标网 请大家多意见

本文转自Sam Lin博客博客园博客,原文链接:http://www.cnblogs.com/samlin/archive/2008/09/25/How-to-Write-a-Provider-Model.html,如需转载请自行联系原作者
目录
相关文章
|
18天前
Cannot load keys from store: class path resource
Cannot load keys from store: class path resource
11 0
|
24天前
|
Oracle 安全 关系型数据库
3.6.7 Encrypted Sensitive Credential Data in the Data Dictionary
You can encrypt sensitive credential data in the SYS.LINK$ and SYS.SCHEDULER$_CREDENTIAL system tables. By default, the credential data in the SYS.LINK$ and SYS.SCHEDULER$_CREDENTIAL system tables is obfuscated. However, because of the availability of many types of de-obfuscation algorithms, Oracle
9 0
|
11月前
加载模型出现-RuntimeError: Error(s) in loading state_dict for Net:unexpected key(s) in state_dict: XXX
加载模型出现-RuntimeError: Error(s) in loading state_dict for Net:unexpected key(s) in state_dict: XXX
360 0
|
Java 应用服务中间件 Linux
SpringBoot - Processing of multipart/form-data request failed. Unexpected EOF read on the socket
SpringBoot - Processing of multipart/form-data request failed. Unexpected EOF read on the socket
1268 0
SpringBoot - Processing of multipart/form-data request failed. Unexpected EOF read on the socket
|
图形学
错误提示: "InfraWorks is unable to render your model" when trying to load a model
错误提示: "InfraWorks is unable to render your model" when trying to load a model
错误提示: "InfraWorks is unable to render your model" when trying to load a model
【1153】Decode Registration Card of PAT (25分)
【1153】Decode Registration Card of PAT (25分) 【1153】Decode Registration Card of PAT (25分)
97 0
callbackend entry point - iwfndcl_mgw_request_manager~read_entity
007. callbackend entry point - /iwfnd/cl_mgw_request_manager~read_entity Created by Wang, Jerry, last modified on Jan 06, 2015
146 0
callbackend entry point - iwfndcl_mgw_request_manager~read_entity
add to -append Backend implementation
add to -append Backend implementation
91 0
add to -append Backend implementation
|
JavaScript 前端开发
how about one resource file has two different methods both annotated with @Post
how about one resource file has two different methods both annotated with @Post
105 0
how about one resource file has two different methods both annotated with @Post