PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug
2011-05-18 17:23
316 查看
最近将公司的项目从SqlServer移植到PostgreSQL数据库上来,在调用数据库的存储过程(自定义函数)的时候,发现一个奇怪的问题,老是报函数无法找到。
先看一个PgSQL存储过程:
CREATE OR REPLACE FUNCTION updateattention(dm citext)
RETURNS void AS
$BODY$
DECLARE
BEGIN
update ZB set gzd=COALESCE(gzd,0)+1 where ZB.dm=$1 ;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION updateattention(citext) OWNER TO postgres;
在PostgreSQL中,函数和存储过程没有区别,这里我们把没有返回值的函数叫做存储过程吧,也许表诉的不太准确,还望大虾指正。
上面定义一个存储过程updateattention,它有一个自定义类型 citext,用于将字符串中类型换成不区分大小写的类型,它的定义如下:
CREATE OR REPLACE FUNCTION citext(character)
RETURNS citext AS
'rtrim1'
LANGUAGE internal IMMUTABLE STRICT
COST 1;
ALTER FUNCTION citext(character) OWNER TO postgres;
下面是调用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框架内置了日志对象和异常对象,它能够为你抛出详细的错误信息。
如果采用下面的方式调用,又没有问题:
db.ExecuteNonQuery("select * from updateattention(@dm)",
System.Data.CommandType.Text,
new System.Data.IDataParameter[] { para });
------------------------------------------------------------------------------------
尽管该方式可以作为一种替代方案,但要用select * from 这种方式调用存储过程,总觉得很别扭,还得找到问题的真正原因。
这个 "function ... does not exist" 的问题很难搜索,最终在国外找到一篇文章讨论类似的问题:
http://pgfoundry.org/forum/forum.php?thread_id=637&forum_id=519
文中有人说,可能是参数的类型转换问题,但我这里只是将参数进行了大小写转换,应该不会有类似Int32到Int64这类问题。
无赖,只有将调用存储过程的.NET程序代码一个一个排查,当注释掉
para.DbType = DbType.AnsiString;
的时候,程序居然能够正常运行通过了!
之前也曾经怀疑过是不是DbType的问题,但是当把鼠标放到VS2010的编辑器中para 对象下面的时候,智能提示显示 DbType="{String}".
默认情况下,参数对象的DbType属性值是
DbType.String
难道
DbType.AnsiString==DbType.String ??
看了一下定义,它们是有区别的,DbType.AnsiString表示非Unicode的变长字符串,DbType.String 表示Unicode的变长字符串。
一般情况下,ANSI编码表示当前系统编码,所以我猜想AnsiString在我的机器上是Gb2312编码的,查了一下数据库的编码,它是UTF-8格式的,难怪难怪,PostgreSQL给我提示找不到 updatefundattention(text) 函数,注意下,实际上这个函数的参数不是text类型的,它实际上应该是 character 类型,PostgreSQL可以定义同名的函数,但函数可以有不同的参数类型,有点像C#的方法重载。
到此,问题似乎解决了,但还没完:
VS2010的智能提示有Bug?
第一次有这个念头我都觉得不可思议,因为以前在VS2008的时候曾经调试过类似的代码,赶紧将上面的.net代码中的参数对象换成其它数据库类型的参数对象试试看:
//获取PostgreSQL的数据访问对象
PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
//使用 SqlServer 的参数对象
IDataParameter para = new SqlParameter();
para.ParameterName = "@dm";
para.DbType = DbType.AnsiString;
para.Value = "KF0355";
db.ExecuteNonQuery("updateattention",
System.Data.CommandType.StoredProcedure,
new System.Data.IDataParameter[] { para });
再此将光标放到para.DbType 上,这次提示正确了,是“{AnsiString}”;
将上面的代码放到VS2008中再次验证,智能提示正确,看来不是VS2010的Bug,呵呵。
故此,得到的结论:
PostgreSQL的.NET数据访问驱动程序的参数对象DbType属性存在一个设置成AnsiString之后查看该属性的结果却是String的Bug!
PS:虽然查看属性的确有这样一个Bug,但好像程序内部做了正确的处理,要不我的程序最终是无法运行通过的。
后记
PostgreSQL的.NET数据驱动程序的这个问题引起的问题使得我困扰了2天左右的时间,不得不发帖说明一下这个过程,现在国内有关PostgreSQL的资料太少,写点东西供大家参考一下。
先看一个PgSQL存储过程:
CREATE OR REPLACE FUNCTION updateattention(dm citext)
RETURNS void AS
$BODY$
DECLARE
BEGIN
update ZB set gzd=COALESCE(gzd,0)+1 where ZB.dm=$1 ;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION updateattention(citext) OWNER TO postgres;
在PostgreSQL中,函数和存储过程没有区别,这里我们把没有返回值的函数叫做存储过程吧,也许表诉的不太准确,还望大虾指正。
上面定义一个存储过程updateattention,它有一个自定义类型 citext,用于将字符串中类型换成不区分大小写的类型,它的定义如下:
CREATE OR REPLACE FUNCTION citext(character)
RETURNS citext AS
'rtrim1'
LANGUAGE internal IMMUTABLE STRICT
COST 1;
ALTER FUNCTION citext(character) OWNER TO postgres;
下面是调用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框架内置了日志对象和异常对象,它能够为你抛出详细的错误信息。
如果采用下面的方式调用,又没有问题:
db.ExecuteNonQuery("select * from updateattention(@dm)",
System.Data.CommandType.Text,
new System.Data.IDataParameter[] { para });
------------------------------------------------------------------------------------
尽管该方式可以作为一种替代方案,但要用select * from 这种方式调用存储过程,总觉得很别扭,还得找到问题的真正原因。
这个 "function ... does not exist" 的问题很难搜索,最终在国外找到一篇文章讨论类似的问题:
http://pgfoundry.org/forum/forum.php?thread_id=637&forum_id=519
文中有人说,可能是参数的类型转换问题,但我这里只是将参数进行了大小写转换,应该不会有类似Int32到Int64这类问题。
无赖,只有将调用存储过程的.NET程序代码一个一个排查,当注释掉
para.DbType = DbType.AnsiString;
的时候,程序居然能够正常运行通过了!
之前也曾经怀疑过是不是DbType的问题,但是当把鼠标放到VS2010的编辑器中para 对象下面的时候,智能提示显示 DbType="{String}".
默认情况下,参数对象的DbType属性值是
DbType.String
难道
DbType.AnsiString==DbType.String ??
看了一下定义,它们是有区别的,DbType.AnsiString表示非Unicode的变长字符串,DbType.String 表示Unicode的变长字符串。
一般情况下,ANSI编码表示当前系统编码,所以我猜想AnsiString在我的机器上是Gb2312编码的,查了一下数据库的编码,它是UTF-8格式的,难怪难怪,PostgreSQL给我提示找不到 updatefundattention(text) 函数,注意下,实际上这个函数的参数不是text类型的,它实际上应该是 character 类型,PostgreSQL可以定义同名的函数,但函数可以有不同的参数类型,有点像C#的方法重载。
到此,问题似乎解决了,但还没完:
VS2010的智能提示有Bug?
第一次有这个念头我都觉得不可思议,因为以前在VS2008的时候曾经调试过类似的代码,赶紧将上面的.net代码中的参数对象换成其它数据库类型的参数对象试试看:
//获取PostgreSQL的数据访问对象
PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
//使用 SqlServer 的参数对象
IDataParameter para = new SqlParameter();
para.ParameterName = "@dm";
para.DbType = DbType.AnsiString;
para.Value = "KF0355";
db.ExecuteNonQuery("updateattention",
System.Data.CommandType.StoredProcedure,
new System.Data.IDataParameter[] { para });
再此将光标放到para.DbType 上,这次提示正确了,是“{AnsiString}”;
将上面的代码放到VS2008中再次验证,智能提示正确,看来不是VS2010的Bug,呵呵。
故此,得到的结论:
PostgreSQL的.NET数据访问驱动程序的参数对象DbType属性存在一个设置成AnsiString之后查看该属性的结果却是String的Bug!
PS:虽然查看属性的确有这样一个Bug,但好像程序内部做了正确的处理,要不我的程序最终是无法运行通过的。
后记
PostgreSQL的.NET数据驱动程序的这个问题引起的问题使得我困扰了2天左右的时间,不得不发帖说明一下这个过程,现在国内有关PostgreSQL的资料太少,写点东西供大家参考一下。
相关文章推荐
- PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug
- PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug
- PostgreSQL的.NET驱动程序Npgsql
- postgresql 存储过程 把一个表的部分数据插入另一个表中(条件:传入的某些参数作为检索条件并且可能为空值)
- 当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的.
- Js把URL中的参数解析为一个对象
- 一个JavaScript函数把URL参数解析成Json对象
- JAVA-JSP内置对象之request获得参数的参数值(一个值)
- .NET下WebBrowser的一个BUG以及其替代品——geckofx
- 【.NET深呼吸】清理对象引用,有一个问题容易被忽略
- IE中限制flash访问document对象的一个bug
- .Net反射技术应用解决对象不同版本方法不同参数的问题
- [转]一个关于临时对象的BUG(下)
- Javascript RegExp对象---获取url中某一个参数的值
- .Net开发笔记(十九) 创建一个可以可视化设计的对象
- .net中调用com对象的一个错误
- Java - 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
- 需要这样一个记录本-9 Delphi使用TADOQuery时提示:不正常地定义参数对象。提供了一不致或不完整的信息。
- .NET下WebBrowser的一个BUG以及其替代品——geckofx
- .net对象作为参数传递的注意事项