The provider model is used throughout ASP.NET 2.0. It is a means of writing each of the technologies used so that new versions can easily be created and plugged in. For example, if you need to access a different database or authentication server, you can create a provider for it. ASP.NET 2.0 will then work with that provider just as it works with the existing features. This makes ASP.NET 2.0 much more flexible, expandable, and customizable than before.
---- MSDN ( Provider Toolkit )
Generally, an application can be devided into three layers at least: UI Layer, Logic Layer, and Data Access Layer. When designing an application, we should realize that our customers may use different data stores, such as Oracle, Microsoft SQL Server, MySQL, or even text files. Therefore it's very important that the Data Access Layer should be properly designed, since the other two layers of properly designed applications are rarely changed to accommodate new data stores. All what we need to do is just modifying the configuration file to tell our application to use the new Data Access Component. For example, the configuration settings for Microsoft SQL Server may look like:
- <userManager defaultProvider="sqlProvider">
- <providers>
- <clear />
- <add name="sqlProvider" type="Chenglin.Data.SqlUserDataProvider, Chenglin" />
- </providers>
- </dataProviders>
and for the MySQL,
- <userManager defaultProvider="mySQLProvider">
- <providers>
- <clear />
- <add name="mySQLProvider" type="Chenglin.Data.MySQLUserDataProvider, Chenglin" />
- </providers>
- </dataProviders>
ITransactionSupport & DataProviderBase
We should thus carefully design the Data Access Components. First, we declare an interface ITransactionSupport and a class DataProviderBase. The ITransactionSupport interface provides the transaction support for Data Access Components.
- public interface ITransactionSupport
- {
- /// <summary>
- /// </summary>
- void BeginTransaction();
-
- /// <summary>
- /// </summary>
- void Commit();
-
- /// <summary>
- /// </summary>
- void Rollback();
-
- /// <summary>
- /// </summary>
- DataProviderBase CreateInstanceForTransaction();
-
- /// <summary>
- /// </summary>
- DataProviderBase ContinueTransaction(DataProviderBase dataProvider);
-
- /// <summary>
- /// </summary>
- bool UnderTransaction { get; }
- }
The DataProviderBase class implements the ITransactionSupport interface, so that we can easily perform transactions among various Data Access Components.
- /// <summary>
- /// </summary>
- public abstract class DataProviderBase
- :
System.Configuration.Provider.ProviderBase,
System.IDisposable,
ITransactionSupport
- {
- /// <summary>
- /// </summary>
- /// <param name="dataProvider">
- /// </param>
- /// <remarks>
- /// </remarks>
- /// <exception cref="ArgumentNullException">
- /// </exception>
- /// <exception cref="ArgumentException">
- /// </exception>
- protected DataProviderBase(DataProviderBase dataProvider)
- {
- if( dataProvider == null ) {
- throw new ArgumentNullException("dataProvider");
- }
- if( !dataProvider.UnderTransaction ) {
- throw new ArgumentException(
- "The specified DataProviderBase object have not yet begun a transaction.",
- "dataObject");
- }
- }
-
- /// <summary>
- /// </summary>
- public abstract void BeginTransaction();
-
- /// <summary>
- /// </summary>
- public abstract void Commit();
-
- /// <summary>
- /// </summary>
- public abstract void Rollback();
-
- /// <summary>
- /// </summary>
- public abstract DataProviderBase CreateInstanceForTransaction();
-
- /// <summary>
- /// </summary>
- public abstract DataProviderBase ContinueTransaction(DataProviderBase dataProvider);
-
- /// <summary>
- /// </summary>
- public abstract bool UnderTransaction { get; }
-
- /// <summary>
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
-
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// </summary>
- protected DataProviderBase()
- { }
-
- /// <summary>
- /// </summary>
- protected abstract void Dispose(bool disposing);
- }
Then, it's very easy to perform transaction operations. For example:
- public class FirstDataProvider : DataProviderBase{ .... }
- public class SecondDataProvider : DataProviderBase{ .... }
-
- FirstDataProvider firstDataProvider;
- SecondDataProvider secondDataProvider;
- FirstDataProvider firstInTrans;
- SecondDataProvider secondInTrans;
-
- firstDataProvider = ....;
- secondDataProvider = ....;
-
- firstDataProvider.Method1();
- firstDataProvider.Method2();
- secondDataProvider.Method1();
-
-
- using( firstInTrans = (FirstDataProvider)firstDataProvider.CreateInstanceForTransaction() ){
- try{
- firstInTrans.BeginTransaction();
-
- firstInTrans.Method1();
- firstInTrans.Method2();
-
- secondInTrans = (SecondDataProvider)secondDataProvider.ContinueTransaction(firstInTrans);
- secondInTrans.Method1();
-
- firstInTrans.Commit();
- }catch{
- firstInTrans.Rollback();
- throw;
- }
- }