.net訪問PostgreSQL數據庫發生“找不到函數名”的問題追蹤
PostgreSQL是一個使用廣泛的免費開源的數據庫,與MySQL比較,它更適合復雜的企業計算任務,而MySQL在互聯網領域應用更為廣泛,究其原因,可能是PostgreSQL擁有支持最多的數據類型,甚至包括數組類型,IP地址類型等,可以使用C,SQL,PL/Pgsql,Phython等多種方式編寫強大的自定義函數,因此特別適合處理復雜的計算問題。如果想要將SqlServer數據庫遷移到其它類型的數據庫,PostgreSQL是比較好的選擇。
盡管PostgreSQL使用比較廣泛,但在國內相關資料太少,我們在數據庫遷移的過程中,遇到了不少問題,比如我的上一篇文章PostgreSQL的.NET驅動程序Npgsql中參數對象的一個Bug 中關于“找不到函數名”的問題,解決起來比較“辣手”,可以使用“追蹤”來形容了。本篇繼續對這個問題進行深入探究。
1,問題回顧:
在上一篇文章中說到,有一個PostgreSQL函數 updateattention ,它有一個自定義的函數參數,下面是函數頭:
- CREATE OR REPLACE FUNCTION updateattention(dm citext)
- RETURNS void AS
- $BODY$
- --函數體略
參數dm 的類型是citex,一個自定義的數據類型,使用它來作為函數參數或者變量的類型,在進行數據查詢的時候可以不區分大小寫,它的定義是:
- CREATE OR REPLACE FUNCTION citext(character)
- RETURNS citext AS
- 'rtrim1'
- LANGUAGE internal IMMUTABLE STRICT
- COST 1;
- ALTER FUNCTION citext(character) OWNER TO postgres;
下面是調用使用C#調用updateattention存儲過程的代碼:
- //獲取PostgreSQL的數據訪問對象
- PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
- //獲取PostgreSQL的參數對象
- IDataParameter para = db.GetParameter();
- para.ParameterName = "@dm";
- para.DbType = DbType.AnsiString;
- para.Value = "KF0355";
- db.ExecuteNonQuery("updateattention",
- System.Data.CommandType.StoredProcedure,
- new System.Data.IDataParameter[] { para });
程序使用PDF.NET(PWMIS數據開發框架)的數據訪問對象AdoHelper來進行相關的數據訪問操作,它采用反射工廠模式,根據系統的配置實例化具體的數據訪問類,這里使用的是PostgreSQL數據訪問類。
運行該程序,出現下面的錯誤:
- PDF.NET AdoHelper 查詢錯誤:
- DataBase ErrorMessage:ERROR: 42883: function updatefundattention(text) does not exist
- SQL:updatefundattention
- CommandType:StoredProcedure
- Parameters:
- Parameter["@jjdm"] = "KF0355" //DbType=String
PDF.NET框架內置了日志對象和異常對象,它能夠為你拋出詳細的錯誤信息。
2,問題聚焦
一開始還以為是函數名大小寫的問題,仔細核對后發現沒有問題,然后嘗試對代碼進行仔細排查。
將上面的程序中第6行代碼
- para.DbType = DbType.AnsiString;
注釋掉,程序運行通過,懷疑參數類型不能夠設置成AnsiString,設置成下面的方式:
- para.DbType = DbType.String;
程序依然運行不通過,拋出上面同樣的錯誤,只有將這行代碼注釋掉才可以允許通過,思索很久仍然沒有結果,于是昨天寫了本文開頭說的那篇文章(PostgreSQL的.NET驅動程序Npgsql中參數對象的一個Bug)。
今天再次將目光聚集在錯誤信息的函數參數上:
updatefundattention(text)
難道PostgreSQL的數據類型text 對應的.NET程序類型既不是String,也不是AnsiString?
又搜索了下,在http://npgsql.projects.postgresql.org/docs/manual/UserManual.html 找到了一張數據類型對照表:
Supported data types
Npgsql supports the following data types:
Postgresql Type | NpgsqlDbType | System.DbType Enum | .Net System Type |
int8 | Bigint | Int64 | Int64 |
bool | Boolean | Boolean | Boolean |
Box, Circle, Line, LSeg, Path, Point, Polygon | Box, Circle, Line, LSeg, Path, Point, Polygon | Object | Object |
bytea | Bytea | Binary | Byte[] |
date | Date | Date | DateTime, NpgsqlDate |
float8 | Double | Double | Double |
int4 | Integer | Int32 | Int32 |
money | Money | Decimal | Decimal |
numeric | Numeric | Decimal | Decimal |
float4 | Real | Single | Single |
int2 | Smallint | Int16 | Int16 |
text | Text | String | String |
time | Time | Time | DateTime, NpgsqlTime |
timetz | Time | Time | DateTime, NpgsqlTimeTZ |
timestamp | Timestamp | DateTime | DateTime, NpgsqlTimestamp |
timestamptz | TimestampTZ | DateTime | DateTime, NpgsqlTimestampTZ |
interval | Interval | Object | TimeSpan, NpgsqlInterval |
varchar | Varchar | String | String |
inet | Inet | Object | NpgsqlInet, IPAddress (there is an implicity cast operator to convert NpgsqlInet objects into IPAddress if you need to use IPAddress and have only NpgsqlInet) |
bit | Bit | Boolean | Boolean, Int32 (If you use an Int32 value, odd values will be translated to bit 1 and even values to bit 0) |
uuid | Uuid | Guid | Guid |
array | Array | Object | Array In order to explicitly use array type, specify NpgsqlDbType as an 'OR'ed type: NpgsqlDbType.Array | NpgsqlDbType.Integer for an array of Int32 for example. |
可以看到 數據庫的text 類型是可以對應.net程序的String類型的,看來問題的關鍵的確是函數參數類型問題。
為了驗證這個想法,將函數的參數類型改為Varchar類型:
- CREATE OR REPLACE FUNCTION updateattention(dm varchar)
- RETURNS void AS
- $BODY$
- --函數體略
再次運行前面說的.net數據訪問程序,運行通過!
故此得到結論:
PostgreSQL數據庫的函數中使用“自定義數據類型”,在.NET程序可能無法設置正確的DbType,從而出現找不到函數名的錯誤!
3,“靈異現象”分析
前面說,將
para.DbType = DbType.AnsiString;
代碼注釋即可,也就是不對NpgsqlParameter.DbType 設置任何值,那么DbType的缺省值是什么呢?
在VS2010的“即時窗口”打印了一下未設置值的para.DbType,發現它的值是:
String
由于上一篇文章已經驗證Npgsql的參數對象DbType無論怎么設置,獲取該屬性值的時候都是String,所以還是無法得知它的默認屬性值是什么。
于是一個很偶然的念頭出現:
NpgsqlParameter對象的默認值是不是Object類型?
另外我們的函數使用了自定義的citext類型,所以很可能需要使用DbType.Object類型。
重新修改代碼成下面的方式:
- //獲取PostgreSQL的數據訪問對象
- PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
- //獲取PostgreSQL的參數對象
- IDataParameter para = db.GetParameter();
- para.ParameterName = "@dm";
- para.DbType = DbType.Object;
- para.Value = "KF0355";
- db.ExecuteNonQuery("updateattention",
- System.Data.CommandType.StoredProcedure,
- new System.Data.IDataParameter[] { para });
運行程序,正常通過,看來問題找到了,就是它,在PostgreSQL的自定義類型函數參數中,.net程序的存儲過程調用參數應該設置成 DbType.Object!
原文鏈接:http://www.cnblogs.com/bluedoctor/archive/2011/05/19/2051271.html
【編者推薦】
- 通用權限管理設計之數據庫結構設計
- PostgreSQL的.NET驅動程序Npgsql中參數對象的一個Bug
- SQL Server表最小行的一個糾結問題
- 云端數據庫:微軟SQL Azure及其應用場景
- SQL點滴之收集SQL Server線程等待信息