• PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug


    最近将公司的项目从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: 
    42883function 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的资料太少,写点东西供大家参考一下。

  • 相关阅读:
    MapReduce —— MapTask阶段源码分析(Output环节)
    MapReduce——移动数据不如移动计算
    MapReduce —— MapTask阶段源码分析(Input环节)
    MapReduce——客户端提交任务源码分析
    NameNode和SecondaryNameNode角色功能分析
    线程的实现原理
    RTFSC:SpringBoot 源码惊鸿一瞥
    垃圾收集器学习笔记
    常见的六种OOM异常和错误
    简单类型多线程加锁
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/2050276.html
Copyright © 2020-2023  润新知