1. 程式人生 > 其它 >PostgreSQL的PDF.NET驅動程式構建過程

PostgreSQL的PDF.NET驅動程式構建過程

目前有兩種主要的PostgreSQL的.NET驅動程式,分別是Npgsql和dotConnector for PostgreSQL(以下簡稱dotConnector),這兩者都是第三方的.NET Provider,本文將大致講解一下這兩個驅動程式的安裝方式,並講解如何利用它們構建PDF.NET的驅動程式,使得PDF.NET資料開發框架可以支援訪問PostgreSQL資料庫。

一、安裝PostgreSQL的.NET驅動程式

1,Npgsql的安裝:

PostgreSQL資料庫程式可以去官網 http://www.postgresql.org/ 下載,在寫本篇文章的時候,最新版本已經是9.1了,我下載使用的是9.0.4. 下載安裝以後,開啟程式 Application Stack Builder,選擇已經安裝好的資料庫以後,單擊下一步進入到如下介面 

在Datase Drivers選項中,這裡選擇Npgsql v2.0.11-1,其它驅動程式根據需要安裝。選擇好以後,按照提示一步步即可完整安裝好.NET的資料驅動程式。

2,dotConnect For PostgreSQL 安裝

在網上搜索一下這個驅動程式,我是從下面的地址安裝的:

http://wzmcc.newhua.com/soft/92182.htm

安裝檔名是 dcpostgresqlfree.exe,版本是 5.30.160,安裝的時候會選擇是否將程式集編譯到GAC中。安裝完成以後在安裝目錄會有幾個簡單的示例程式解決方案:

大家可以開啟示例程式解決方案看看,都很簡單,具體如何使用可以看本篇文章的下面部分。

二、構建PDF.NET For PostgreSQL驅動程式

根據上面的步驟,安裝了.NET的PostgreSQL驅動程式以後,就可以直接按照示例來訪問PostgreSQL資料庫了,但這兩種不同的驅動程式讓我們難以選擇使用哪一種,而且它們直接提供的ADO.NET實現用起來也不是十分方便,我們有必要將它們包裝一下,簡化使用方式。PDF.NET資料開發框架內建了MS DAAB類似的AdoHelper資料訪問抽象類,所以只要繼承該類就可以擁有PDF.NET強大的資料訪問能力。

1,包裝Ngpsql驅動程式

下面以Npgsql為例,看看如何讓PDF.NET支援PostgreSQL。下面是貼出全部程式碼:

using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Data;
using Npgsql ;
namespace PWMIS.DataProvider.Data
{
    /// <summary>
    /// PostgreSQL資料訪問類
    /// </summary>
    public class PostgreSQL : AdoHelper
    {
        /// <summary>
        /// 預設建構函式
        /// </summary>
        public PostgreSQL()
        {
            //
            // TODO: 在此處新增建構函式邏輯
            //
        }
        /// <summary>
        /// 獲取當前資料庫型別的列舉
        /// </summary>
        public override PWMIS.Common.DBMSType CurrentDBMSType
        {
            get { return PWMIS.Common.DBMSType.PostgreSQL ; } 
        }
        /// <summary>
        /// 建立並且開啟資料庫連線
        /// </summary>
        /// <returns>資料庫連線</returns>
        protected override IDbConnection GetConnection()
        {
            IDbConnection conn = base.GetConnection();
            if (conn == null)
            {
                conn = new NpgsqlConnection (base.ConnectionString);
                //conn.Open ();
            }
            return conn;
        }
        /// <summary>
        /// 獲取資料介面卡例項
        /// </summary>
        /// <returns>資料介面卡</returns>
        protected override IDbDataAdapter GetDataAdapter(IDbCommand command)
        {
            IDbDataAdapter ada = new NpgsqlDataAdapter((NpgsqlCommand)command);
            return ada;
        }
        /// <summary>
        /// 獲取一個新引數物件
        /// </summary>
        /// <returns>特定於資料來源的引數物件</returns>
        public override IDataParameter GetParameter()
        {
            return new NpgsqlParameter();
        }
        /// <summary>
        ///  獲取一個新引數物件
        /// </summary>
        /// <param name="paraName">引數名</param>
        /// <param name="dbType">引數資料型別</param>
        /// <param name="size">引數大小</param>
        /// <returns>特定於資料來源的引數物件</returns>
        public override IDataParameter GetParameter(string paraName, System.Data.DbType dbType, int size)
        {
            NpgsqlParameter para = new NpgsqlParameter();
            para.ParameterName = paraName;
            para.DbType = dbType;
            para.Size = size;
            return para;
        }
        /// <summary>
        /// 返回此 NpgsqlConnection 的資料來源的架構資訊。
        /// </summary>
        /// <param name="collectionName">集合名稱</param>
        /// <param name="restrictionValues">請求的架構的一組限制值</param>
        /// <returns>資料庫架構資訊表</returns>
        public override DataTable GetSchema(string collectionName, string[] restrictionValues)
        {
            using (NpgsqlConnection conn = (NpgsqlConnection)this.GetConnection())
            {
                conn.Open();
                if (restrictionValues == null && string.IsNullOrEmpty(collectionName))
                    return conn.GetSchema();
                else if (restrictionValues == null && !string.IsNullOrEmpty(collectionName))
                {
                    if (collectionName == "Procedures")
                        return this.getProcedures();
                    else
                        return conn.GetSchema(collectionName); //Procedures
                }
                else
                { 
                    if (collectionName == "ProcedureParameters")
                        return getFunctionArgsInfo(restrictionValues[2]);
                    else
                        return conn.GetSchema(collectionName, restrictionValues);
                }
            }
        }
        /// <summary>
        /// 預處理SQL語句,語句中不能包含"`"(反引號,tab鍵上面的那個符號)號,如果需要,請使用引數化查詢。
        /// </summary>
        /// <param name="SQL"></param>
        /// <returns></returns>
        protected override string PrepareSQL(ref string SQL)
        {
            return SQL.Replace("[", """).Replace("]", """);
        }
        /// <summary>
        /// 獲取或者設定自增列對應的序列名稱
        /// </summary>
        public override string InsertKey
        {
            get
            {
                return string.Format("select currval('"{0}"')",base.InsertKey );
            }
            set
            {
                base.InsertKey = value;
            }
        }
        /// <summary>
        /// 定義獲取PostgreSQL的函式引數的函式
        /// <seealso cref="http://www.alberton.info/postgresql_meta_info.html"/>
        /// </summary>
        private void createFunctionArgsInfo()
        {
            //由於函式定義語句較長,放到了資原始檔中
            string sql = PWMIS.PostgreSQLClient.Properties.Resources.sql_function_args;
            this.SqlServerCompatible = false;
            this.ExecuteNonQuery(sql);
        }
        /// <summary>
        /// 獲取函式的引數資訊
        /// </summary>
        /// <param name="functionName">函式名</param>
        /// <returns></returns>
        private DataTable  getFunctionArgsInfo(string functionName)
        {
            string sql = string.Format("select * from function_args('{0}','public');", functionName);
            DataSet ds = null;
            try
            {
                ds= this.ExecuteDataSet(sql);
            }
            catch
            {
                createFunctionArgsInfo();
                ds = this.ExecuteDataSet(sql);
            }
           
            DataTable dt = ds.Tables[0];
            dt.Columns["pos"].ColumnName = "ordinal_position";
            dt.Columns["argname"].ColumnName = "PARAMETER_NAME";
            dt.Columns["datatype"].ColumnName = "DATA_TYPE";
            dt.Columns["direction"].ColumnName = "PARAMETER_MODE";
            dt.Columns.Add("IS_RESULT", typeof(string));
            dt.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int));
            foreach (DataRow row in dt.Rows)
            {
                if(row["PARAMETER_NAME"] == DBNull.Value)  row["PARAMETER_NAME"] =  ""; 
                row["IS_RESULT"] = row["PARAMETER_NAME"].ToString() == "RETURN VALUE" ? "YES" : "NO";
                row["PARAMETER_MODE"] = row["PARAMETER_MODE"].ToString() == "o" ? "OUT" : row["PARAMETER_MODE"].ToString() == "i" ? "IN" : row["PARAMETER_MODE"];
            }
            return dt;
        }
        private DataTable getProcedures()
        {
            string sql = @"SELECT routine_name
  FROM information_schema.routines
 WHERE specific_schema NOT IN
       ('pg_catalog', 'information_schema')
   AND type_udt_name != 'trigger';";
            return this.ExecuteDataSet(sql).Tables[0];
        }
    }
}

 注意上面程式中的 PrepareSQL 方法,它將SQLSERVER格式的SQL語句轉換成PostgreSQL支援的格式,SQLSERVER使用成對的中括號來限定物件名,而PostgreSQL使用雙引號,尤其在物件名稱使用了大小寫混合的情況。另外程式為了支援獲取資料庫的架構資訊,重寫了AdoHelper的抽象方法GetSchema,有關PostgreSQL具體獲取表架構資訊的內容,請參看 http://www.alberton.info/postgresql_meta_info.html

2,包裝dotConnect驅動程式

程式程式碼與使用Npgsql類似,區別主要是將上面程式碼中的Npgsql字樣替換成PgSql即可,引用Devart.Data.dll,Devart.Data.PostgreSql.dll,使用下面的名稱空間:

using Devart.Data.PostgreSql;

由於dotConnect的驅動程式採用Oracle驅動程式的風格,要求SQL語句的引數使用“:”作為引數名稱,而不是SqlServer樣式的“@”,所以下面的方法需要重寫:

/// <summary>
        /// 預處理SQL語句,語句中不能包含中括號,如果需要,請使用引數化查詢。
        /// </summary>
        /// <param name="SQL"></param>
        /// <returns></returns>
        protected override string PrepareSQL(ref string SQL)
        {
            return SQL.Replace("[", """).Replace("]", """).Replace("@",":");
        }
        public override string GetParameterChar
        {
            get
            {
                return ":";
            }
        }

到此為止,使用dotConnect做PDF.NET的PostgreSQL驅動程式也做好了。

三、使用PDF.NET For PostgreSQL驅動程式

1,使用配置

假定上面使用Npgsql和dotConnect驅動的程式分別是 PWMIS.PostgreSQLClient 程式集中的程式,名稱分別是

PWMIS.DataProvider.Data.PostgreSQL

PWMIS.DataProvider.Data.dotConnectPostgreSQL

那麼我們在應用程式配置檔案裡面如下使用即可:

使用Npgsql訪問:

<add name="ConnectionSetting" connectionString="server=192.168.XX.XX;User Id=postgres;password=XXXX;DataBase=XXDB" providerName="PWMIS.DataProvider.Data.PostgreSQL,PWMIS.PostgreSQLClient"/>

使用dotConnect訪問:

<add name="ConnectionSetting" connectionString="server=192.168.XX.XX;User Id=postgres;password=XXXX;DataBase=XXDB" providerName="PWMIS.DataProvider.Data.dotConnectPostgreSQL,PWMIS.PostgreSQLClient"/>

2,執行效率區別

使用這兩個不同的提供程式資料訪問效率有什麼區別呢?經過測試,它們之間僅有細微的差別,Npgsql略微勝出,下面是測試程式建立過程:

首先在SqlMap.config檔案中建立一個PostgreSQL的資料訪問指令碼:

<Select CommandName="GetFundFeat" Method="" CommandType="Text" Description="獲取業績" ResultClass="DataSet">
<![CDATA[SELECT * FROM GetFundTrend_FundAnalysis_FundFeat (#currentJJDM:String#,#OtherJJDM:String#)]]>
</Select>

然後使用整合開發工具的程式碼生成器生成一個類中下面的方法:

public DataSet GetFundFeat(String currentJJDM  , String OtherJJDM   ) 
    { 
            //獲取命令資訊
            CommandInfo cmdInfo=Mapper.GetCommandInfo("GetFundFeat");
            //引數賦值,推薦使用該種方式;
            cmdInfo.DataParameters[0].Value = currentJJDM;
            cmdInfo.DataParameters[1].Value = OtherJJDM;
            //引數賦值,使用命名方式;
            //cmdInfo.SetParameterValue("@currentJJDM", currentJJDM);
            //cmdInfo.SetParameterValue("@OtherJJDM", OtherJJDM);
            //執行查詢
            return CurrentDataBase.ExecuteDataSet(CurrentDataBase.ConnectionString, cmdInfo.CommandType, cmdInfo.CommandText , cmdInfo.DataParameters);
        //
    }//End Function

最後以不同的Pgsql驅動程式執行程式,檢視執行的SQL日誌

使用dotConnect訪問:

//2011/5/30 16:52:44 @AdoHelper 執行命令:
SQL="SELECT * FROM GetFundTrend_FundAnalysis_FundFeat (:currentJJDM,:OtherJJDM)"
//命令型別:Text
//2個命令引數:
Parameter["currentJJDM"]    =    "KF0355"              //DbType=AnsiString
Parameter["OtherJJDM"]    =    "000001,399001,H11020,000300"              //DbType=AnsiString
//2011/5/30 16:52:44 @AdoHelper :Execueted Time(ms):448

使用Npgsql訪問:

//2011/5/30 16:58:17 @AdoHelper 執行命令:
SQL="SELECT * FROM GetFundTrend_FundAnalysis_FundFeat (@currentJJDM,@OtherJJDM)"
//命令型別:Text
//2個命令引數:
Parameter["@currentJJDM"]    =    "KF0180"              //DbType=String
Parameter["@OtherJJDM"]    =    "000001,399001,H11020,000300"              //DbType=String
//2011/5/30 16:58:17 @AdoHelper :Execueted Time(ms):405

有關PDF.NET資料開發框架的詳細資訊,請看官網說明:http://www.pwmis.com/sqlmap