抽象工厂在ADO.Net中的应用

https://msdn.microsoft.com/zh-cn/library/ms971499.aspx

http://www.c-sharpcorner.com/UploadFile/mosessaur/abstractfactoryadonet202152006053643AM/abstractfactoryadonet2.aspx

Introduction

Most Web applications contain data access code to access the underlying data store to perform basic data operations such as SelectUpdateDelete, and Insert. This article uses a step-by-step approach to show how page developers can take advantage of different ASP.NET 2.0 and ADO.NET 2.0 tools and techniques to write generic data access code that can be used to access different types of data stores. Writing generic data access code is especially important in data-driven Web applications because data comes from many different sources, including Microsoft SQL Server, Oracle, XML documents, flat files, and Web services, just to name a few.

This article uses a simple Web application as a test bed for all the code presented here. The application consists of two parts: the first part allows the system administrator to send newsletters to all subscribers of a mailing list. The second part allows users to subscribe or unsubscribe from a mailing list. The first part of the article begins by implementing a simple data access code (see Figure 1) to access Microsoft SQL Server and extract the list of subscribers. The code is modified and made more generic over the course of the article.

Figure 1. The GetSubscribers method extracts the list of all subscribers.

public IEnumerable GetSubscribers()
{
    SqlConnection con = new SqlConnection();
    con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";

    SqlCommand com = new SqlCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    SqlDataAdapter ad = new SqlDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Since the data access code in Figure 1 contains code for creating the ADO.NET objects, such as the SqlConnectionSqlCommand, and SqlDataAdapter instances. The data access code cannot be used to retrieve the list of subscribers from other data stores, such as an Oracle database. Page developers have to modify the data access code (using the GetSubscribers method) every time they need to access a new data store. Next, see how ADO.NET 2.0 uses the provider pattern to help page developers write generic data access code to access different types of data stores.

Provider Pattern in ADO.NET 2.0

The main problem with the GetSubscribers method is that it contains the code for creating the ADO.NET objects. According to the provider pattern, the data access code must delegate the responsibility of providing the code for creating the ADO.NET objects to another class. I refer to this class as the "code provider class" because it provides the code for creating the ADO.NET objects. The code provider class exposes methods such as CreateConnectionCreateCommand, and CreateDataAdapter, where each method provides the code for creating the corresponding ADO.NET object.

Since the code provider class contains the actual code, the same class cannot be used to access different data stores. Therefore, the data access code (the GetSubscribers method) has to be modified and reconfigured to delegate the responsibility of providing the code to a new code provider class each time it is used to access a new data store. The GetSubscribers method is still tied to the code even though it does not contain the code.

The provider pattern offers a solution to this problem and consists of the following steps:

  1. Design and implement an abstract base provider class.
  2. Derive all code provider classes from the abstract base provider class.
  3. Have the data access code (the GetSubscribers method) to use the abstract base class instead of the individual code provider classes.

    The abstract base class delegates the responsibility of providing the code for creating the ADO.NET objects to the appropriate subclass. The abstract base class is named DbProviderFactory. The following presents some of the methods of this class:
public abstract class DbProviderFactory
{
        public virtual DbConnection CreateConnection();
        public virtual DbCommand CreateCommand();
        public virtual DbDataAdapter CreateDataAdapter();
}

Each subclass provides the code for creating the appropriate ADO.NET objects for a particular data store. For instance, the SqlClientFactory subclass provides the code for creating the ADO.NET objects to access Microsoft SQL Server, as shown in Figure 2.

Figure 2. The SqlClientFactory class and some of its methods

The provider pattern allows the data access code to treat all the subclasses the same because they are all subclasses of the same base class. As far as the data access code is concerned, all subclasses are of type DbProviderFactory. The data access code has no way of knowing the specific type of the subclass being used. This introduces a new problem. If the data access code (the GetSubscribers method) does not know the type of subclass, how can it then instantiate an instance of the subclass?

The provider pattern solution to this problem consists of the following three parts:

  1. A unique string is used to identify each subclass. ADO.NET 2.0 uses the namespace of the subclass as its unique string id. For instance, the unique string id‘s System.Data.SqlClient andSystem.Data.OracleClient identify SqlClientFactory and OracleClientFactory subclasses, respectively.
  2. A text file (normally an XML file) is used to store information about all the subclasses. ADO.NET 2.0 uses the machine.config and web.config files to store the required information. The information about a subclass contains, among other things, the unique string id and the name of the type of the subclass. For instance, the information about the SqlClientFactory subclass includes the unique string idSystem.Data.SqlClient and the name of the type of the subclass, i.e., System.Data.SqlClient.SqlClientFactory.
  3. A static method is designed and implemented. The method could be part of the abstract base class or part of a separate class. ADO.NET 2.0 uses a separate class named DbProviderFactories that exposes theGetFactory static method. The method takes the unique string id of the desired subclass as its only argument and searches through the machine.config file for a subclass with the given unique string id. The method extracts the name of the type of the desired subclass and uses reflection to dynamically create an instance of the subclass.

Data access code (the GetSubscribers method) calls the GetFactory static method and passes the appropriate unique string id to access the instance of the corresponding subclass. After the GetSubscribers method accesses the instance, it calls the appropriate creation methods, such as CreateConnection(), CreateCommand(), etc., to instantiate the appropriate ADO.NET objects, as shown in Figure 3.

Figure 3. The version of the GetSubscribers method that uses the new ADO.NET provider pattern

public IEnumerable GetSubscribers()
{
    DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
    DbConnection con = provider.CreateConnection();
    con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";
    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

The data access code (the GetSubscribers method) delegates the responsibility of providing the code for creating the ADO.NET objects to the code provider class instance that the GetFactory method instantiates and returns. Therefore, the same data access code can be used to access different data stores, such as Microsoft SQL Server and Oracle.

The code for creating the right ADO.NET objects is data-store–specific. The provider pattern in ADO.NET 2.0 removes these data-store–specific parts from the data access code (the GetSubscribers method) to make it more generic. However, the provider pattern does not remove all the data-store–specific parts. Closer inspection of the GetSubscribers method reveals the following remaining data-store–specific parts:

  1. Connection string
  2. Unique string id that identifies the underlying code provider class
  3. Command text
  4. Command type

Unless something is done about the above parts, the data access code is still tied to a particular type of data store. The provider pattern in ADO.NET 2.0 does not help with this problem. However, ADO.NET 2.0 provides us with other tools and techniques to remove the first two data-store–specific parts, such as the connection string and unique string id from the GetSubscribers method.

Connection Strings

Connection strings are some of the most valuable resources in a Web application. They are so important that the .NET Framework 2.0 treats them as "first-class citizens". The web.config file now supports a new section named <connectionStrings> that contains all the connection strings used in an application. Therefore, we will move the connection string from the GetSubscribers method to this section:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
          <add
            name="MySqlConnectionString"
            connectionString="Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
      providerName="System.Data.SqlClient"/>
    </connectionStrings>
</configuration>

The <add> subelement of the <connectionStrings> element exposes the following three important attributes:

  • Name—The friendly name of the connection string
  • connectionString—The actual connection string
  • providerName—The unique string id or invariant of the code provider class

NET Framework 2.0 provides the data access code (the GetSubscribers method) with the right tools to generically extract the connection string value from the web.config file as described in the following. TheSystem.Configuration namespace in .NET Framework 2.0 includes a new class named Configuration. This class represents the entire content of a web.config or machine.config file. The data access code cannot use the new operator to directly create an instance of this class.

Figure 4 shows the new version of the GetSubscribers method that contains the required code to extract the connection string in generic fashion.

Figure 4. The version of the GetSubscribers method that extracts the connection string from the web.config file

public IEnumerable GetSubscribers()
{
    DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
    DbConnection con = provider.CreateConnection();

    Configuration configuration = Configuration.GetWebConfiguration("~/");
    ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];
    con.ConnectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;

    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

The GetSubscribers method now includes the MySqlConnectionString string, so we still have to modify the GetSubscribers method to add support for a different data store such as Oracle database. It would seem that we are back to square one. Not really. We have gained a few important benefits by moving the connection string from the data access code to the web.config file:

  • The connection string is now the value of the connectionString attribute of the <add> sub element of the connectionStrings element of the web.config file, which is an XML document. The great thing about an XML document is that we can encrypt a single element in the document. We do not have to encrypt the entire document if we only need to protect small part of it. The .NET Framework 2.0 comes with a tool that allows us to encrypt the <connectionStrings> section to protect our most important resource, the connection strings. Imagine how much damage a hacker can do to our valuable database if he gets his hand on our connection strings. Remember connection strings are all a hacker needs to access our database.
  • It may seem that all we have done is to replace the following string
    "Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
    

    with the new string, MySqlConnectionString. However, there is one big difference. The former string contains the SQL Server database-specific information that does not apply to another database such as Oracle, but the latter string is just a friendly name.

However, the friendly name could still cause problems because it refers to a specific connection string within the <connectionStrings> section of the web.config file. In our example, it refers to the connection string used to access Microsoft SQL Server. This means that the GetSubscribers method (the data access code) has to be modified to use a different friendly name to access a different data store such as Oracle.

To avoid modifying the data access code, we can move the friendly name from the data access code to the <appSettings> section of the web.config file and have the data access code dynamically extract it in runtime as follows:

string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];

We also move the provider name to the <appSettings> section:

string providerName = ConfigurationSettings.AppSettings["ProviderName"];

Page developers simply change the value attribute of the <add> subelement of the <appSettings> element to the same data access code to access a different data store without making any changes in the data access code itself.

Figure 5 presents the version of the data access code (the GetSubscribers method) that contains the recent changes.

Figure 5. The version of the GetSubscribers method to extract the provider name and the friendly name of the connection string from the web.config file

public IEnumerable GetSubscribers()
{
    string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];
    string providerName = ConfigurationSettings.AppSettings["ProviderName"];
    Configuration configuration = Configuration.GetWebConfiguration("~/");
    ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];

    DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
    DbConnection con = provider.CreateConnection();
    con.ConnectionString = section.ConnectionStrings[connectionStringName].ConnectionString;

    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}
时间: 2024-07-30 06:56:12

抽象工厂在ADO.Net中的应用的相关文章

抽象工厂模式在android中使用

抽象工厂模式(Abstract Factory) 抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步延伸和拓展的结果.抽象工厂模式更加抽象化,更具一般性特点. 我们知道,工厂方法模式是针对单一的产品等级结构而产生的,而这里的抽象工厂模式则是针对多个产品等级结构的.当然,单一的产品等级结构也同样适用于抽象工厂模式的.下面为其具体的等级结构图说明: 从上面的结构示意图,可以看到抽象工厂模式中涉及到了多个等级结构的产品角色:同时,针对每一种产品等级结构,都有一种专门的工厂角色负责创建对应的产品,这

SAF 中抽象工厂的实现

本文是<Developing Application Frameworks in .NET>的读书笔记.SAF 是书中的一个范例框架,意为 Simple Application Framework(简单应用程序框架).这篇文章主要向大家说明了SAF中抽象工厂模式的实现方式. 设计思想概述 抽象工厂是很常用的一种创建型模式,它的主要作用在于向程序员屏蔽了创建对象的复杂细节,在获取对象时,只需要在工厂类上调用 GetXXX(),或者 CreateXXX(),便可以得到一个类型实例,而不是通常所使用

【转】设计模式:简单工厂、工厂方法、抽象工厂之小结与区别

简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式.其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性. 本文是本人对这三种模式学习后的一个小结以及对他们之间的区别的理解. 简单工厂 简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例. 不修改代码的话,是无法扩展的. 工厂方法 工厂方法是针对每一种产品提供一个工厂类.通过不同的工厂实例来创建不同的产品实例. 在同一等级结构中,支持增加任意产品. 抽象工厂 抽象工厂是应

C#设计模式——抽象工厂模式

一.引言 在上一专题中介绍了工厂方法模式,工厂方法模式是为了克服简单工厂模式的缺点而设计出来的,简单工厂模式的工厂类随着产品类的增加需要增加额外的代码),而工厂方法模式每个具体工厂类只完成单个实例的创建,所以它具有很好的可扩展性.但是在现实生活中,一个工厂只创建单个产品这样的例子很少,因为现在的工厂都多元化了,一个工厂创建一系列的产品,如果我们要设计这样的系统时,工厂方法模式显然在这里不适用,然后抽象工厂模式却可以很好地解决一系列产品创建的问题,这是本专题所要介绍的内容. 二.抽象工厂详细介绍

抽象工厂模式实例

用户需求:       用程序模拟以下产品线并代码实现    设计思路:      1.UML图  2.采用抽象工厂模式,将具体产品从客户代码中分离,容易改变产品的系列,能将一个系列的产品族统一到一起创建. 具体代码实现:          1.抽象工厂模式实现类 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace 设计模式作业 7 {

C#设计模式——抽象工厂模式(原文转自:http://blog.jobbole.com/78059/)

一.引言 在上一专题中介绍了工厂方法模式,工厂方法模式是为了克服简单工厂模式的缺点而设计出来的,简单工厂模式的工厂类随着产品类的增加需要增加额外的代码),而工厂方法模式每个具体工厂类只完成单个实例的创建,所以它具有很好的可扩展性.但是在现实生活中,一个工厂只创建单个产品这样的例子很少,因为现在的工厂都多元化了,一个工厂创建一系列的产品,如果我们要设计这样的系统时,工厂方法模式显然在这里不适用,然后抽象工厂模式却可以很好地解决一系列产品创建的问题,这是本专题所要介绍的内容. 二.抽象工厂详细介绍

设计模式:简单工厂、工厂方法、抽象工厂之小结与区别 (转)

简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式.其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性. 本文是本人对这三种模式学习后的一个小结以及对他们之间的区别的理解. 简单工厂 简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例. 不修改代码的话,是无法扩展的. 工厂方法 工厂方法是针对每一种产品提供一个工厂类.通过不同的工厂实例来创建不同的产品实例. 在同一等级结构中,支持增加任意产品. 抽象工厂 抽象工厂是应

设计模式之创建型抽象工厂模式

通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责某一类产品的实例.抽象类是一种声明但不能使用的类,当你使用的时候就会报错.JavaScript中的抽象类不能像传统面向对象语言那样轻松地创建,我们可以在类的方法中手动抛出错误来模拟抽象类.你可能会想,这样的类什么都不能做有什么用?其实它在继承上是很有用的.抽象工厂模式不能用来创建具体对象,一般用它作为父类创建一些子类. // 抽象工厂方法 var PageFactory = function(parent, child) { // 判断抽象

简单工厂、工厂方法、抽象工厂之小结、区别

很多时候,我发现这三种设计模式难以区分,常常会张冠李戴闹了笑话.很有必要深入总结一下三种设计模式的特点.相同之处和不同之处. 1 本质 三个设计模式名字中都含有“工厂”二字,其含义是使用工厂(一个或一系列方法)去生产产品(一个或一系列类的实例). 另外,有时候,我们常常会将生产产品的一个或一系列方法封装到一个类中,我习惯把这个类叫做“工厂类”:而被实例化的类称作“产品类”. 2 简单工厂 工厂类(SimpleFactory)拥有一个工厂方法(create),接受了一个参数,通过不同的参数实例化不