前言:

很久之前,就有同学问我CYQ.Data能不能支持下PostgreSQL,之后小做了下调查,发现这个数据库用的人少,加上各种因素,就一直没动手。

前两天,不小心看了一下Github上的消息:

看到这个问题又被重新提了出来了,于是,闹吧!

下面分享一下支持该数据库要处理的过程,让大伙明白CYQ.Data要支持一种新的数据库,需要花多少功夫。

1、找到数据库的驱动程序:Npgsql.dll

网上查找了点相关知识,发现.NET 里操作PostgreSQL有两种提供的dll,一种是正规的收费的,另一种是开源的Npgsql.dll,因此这里选择了开源的。

在Nuget上可以搜索Npgsql,不过上面的版本要求依赖的版本很高,于是我找了最早的版本开始支持,毕竟CYQ.Data 是从支持最低2.0及以上的。

这里是找到的下载低版本支持的网址:http://pgfoundry.org/frs/?group_id=1000140&release_id=1889

同时,下载的两个2.0和4.0两个版本,也一并上传到:https://github.com/cyq1162/cyqdata/tree/master/文档

2、创建PostgreDal.cs,实现动态加载DLL

添加动态加载的代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data.Common;
using CYQ.Data.Cache;
using System.IO; namespace CYQ.Data
{
internal class PostgreDal : DbBase
{
public PostgreDal(ConnObject co)
: base(co)
{ }
internal static Assembly GetAssembly()
{
object ass = CacheManage.LocalInstance.Get("Postgre_Assembly");
if (ass == null)
{
try
{
string name = string.Empty;
if (File.Exists(AppConst.RunFolderPath + "Npgsql.dll"))
{
name = "Npgsql";
}
else
{
name = "Can't find the Npgsql.dll";
Error.Throw(name);
}
ass = Assembly.Load(name);
CacheManage.LocalInstance.Set("Postgre_Assembly", ass, );
}
catch (Exception err)
{
string errMsg = err.Message;
Error.Throw(errMsg);
}
}
return ass as Assembly;
}
protected override DbProviderFactory GetFactory(string providerName)
{
object factory = CacheManage.LocalInstance.Get("Postgre_Factory");
if (factory == null)
{
Assembly ass = GetAssembly();
factory = ass.GetType("Npgsql.NpgsqlFactory").GetField("Instance").GetValue(null);
// factory = ass.CreateInstance("Npgsql.NpgsqlFactory.Instance");
if (factory == null)
{
throw new System.Exception("Can't Create NpgsqlFactory in Npgsql.dll");
}
else
{
CacheManage.LocalInstance.Set("Postgre_Factory", factory, );
} }
return factory as DbProviderFactory; } protected override bool IsExistsDbName(string dbName)
{
try
{
IsAllowRecordSql = false;
bool result = ExeScalar("select 1 from pg_catalog.pg_database where datname='" + dbName + "'", false) != null;
IsAllowRecordSql = true;
return result;
}
catch
{
return true;
}
}
public override char Pre
{
get
{
return ':';
}
}
public override void AddReturnPara()
{ }
}
}

几点说明:

、GetFactory方法,其它dll框架提供的都是直接实例化,而Npgsql.dll提供却是单例属性,所以代码有点变化。
、Npgsql操作参数化的符号是“:”号。

3、DalCreate.cs追加PostgreSql类型及数据库链接解析

这里重点发现postgresql和mssql两者的数据库链接格式都一致:

server=...;uid=xxx;pwd=xxx;database=xxx;

因此从单纯的语句上,根本无法判断从属于哪种数据库。

经过小小的思考,解决方案出来了:

else
{
//postgre和mssql的链接语句一样,这里用database=和uid=顺序来决定;database写在后面的,为postgre
int dbIndex = connString.IndexOf("database=", StringComparison.OrdinalIgnoreCase);
int uid = connString.IndexOf("uid=", StringComparison.OrdinalIgnoreCase);
if (uid > && uid < dbIndex && File.Exists(AppConfig.RunPath + "Npgsql.dll"))
{
return PostgreClient;
}
return SqlClient;
}

简的说:只有满足引用了npgsql.dll以及database写在uid之后两种条件下,判断为postgresql,其它的都回归到mssql。

4、处理表结构语句:获取数据库表以及表的结构语句:

这一块花的时间比较多,网上也费了点时间查了不少资料,最后自己写了语句:

获取数据库所有表:

internal static string GetPostgreTables(string dbName)
{
return string.Format("select table_name as TableName,cast(obj_description(relfilenode,'pg_class') as varchar) as Description from information_schema.tables t left join pg_class p on t.table_name=p.relname where table_schema='public' and table_catalog='{0}'", dbName);
}

获取某表的结构:

internal static string GetPostgreColumns()
{
return @"select
a.attname AS ColumnName,
case t.typname when 'int4' then 'int' when 'int8' then 'bigint' else t.typname end AS SqlType,
coalesce(character_maximum_length,numeric_precision,-1) as MaxSize,numeric_scale as Scale,
case a.attnotnull when 'true' then 0 else 1 end AS IsNullable,
case when position('nextval' in column_default)>0 then 1 else 0 end as IsAutoIncrement,
case when o.conname is null then 0 else 1 end as IsPrimaryKey,
d.description AS Description,
i.column_default as DefaultValue
from pg_class c
left join pg_attribute a on c.oid=a.attrelid
left join pg_description d on a.attrelid=d.objoid AND a.attnum = d.objsubid
left join pg_type t on a.atttypid = t.oid
left join information_schema.columns i on i.table_schema='public' and i.table_name=c.relname and i.column_name=a.attname
left join pg_constraint o on a.attnum = o.conkey[1] and o.contype='p'
where c.relname =:TableName
and a.attnum > 0 and a.atttypid>0
ORDER BY a.attnum";
}

5、处理关键字符号

由于PostgreSQL的大小写敏感,而且关键字加需要用双引号包含(这点和SQLite一致):

这里在原有的基础上加上case即可。

6、处理差异化的SQL语句:SqlCreate.cs

A、获取插入后的自增值,这里可以借用一下自增列产生的默认值:

这里用默认值,替换一下nextval序列为currval序列即可。

 else if (_action.dalHelper.dalType == DalType.PostgreSQL)
{
string key = Convert.ToString(primaryCell.Struct.DefaultValue);
if (!string.IsNullOrEmpty(key))
{
key = key.Replace("nextval", "currval");
sql = sql + "; select " + key + " as OutPutValue";
}
}

B、需要引用关键字的地方:

略。。。。

7、处理分页语句:SqlCreateForPager.cs

这里PostgreSQL和分页和sqlite及mysql是一致的,因此只要在相关的地方补上case即可:

public static string GetSql(DalType dalType, string version, int pageIndex, int pageSize, object objWhere, string tableName, int rowCount, string columns, string primaryKey, bool primaryKeyIsIdentity)
{
if (string.IsNullOrEmpty(columns))
{
columns = "*";
}
pageIndex = pageIndex == ? : pageIndex;
string where = SqlFormat.GetIFieldSql(objWhere);
if (string.IsNullOrEmpty(where))
{
where = "1=1";
}
if (pageSize == )
{
return string.Format(top1Pager, columns, tableName, where);
}
if (rowCount > )//分页查询。
{
where = SqlCreate.AddOrderBy(where, primaryKey);
}
int topN = pageIndex * pageSize;//Top N 最大数
int max = (pageIndex - ) * pageSize;
int rowStart = (pageIndex - ) * pageSize + ;
int rowEnd = rowStart + pageSize - ;
string orderBy = string.Empty;
if (pageIndex == && dalType != DalType.Oracle)//第一页(oracle时 rownum 在排序条件为非数字时,和row_number()的不一样,会导致结果差异,所以分页统一用row_number()。)
{
switch (dalType)
{
case DalType.Access:
case DalType.MsSql:
case DalType.Sybase:
return string.Format(top1Pager, "top " + pageSize + " " + columns, tableName, where);
//case DalType.Oracle:
// return string.Format(top1Pager, columns, tableName, "rownum<=" + pageSize + " and " + where);
case DalType.SQLite:
case DalType.MySql:
case DalType.PostgreSQL:
return string.Format(top1Pager, columns, tableName, where + " limit " + pageSize);
}
}
else
{ switch (dalType)
{
case DalType.Access:
case DalType.MsSql:
case DalType.Sybase:
int leftNum = rowCount % pageSize;
int pageCount = leftNum == ? rowCount / pageSize : rowCount / pageSize + ;//页数
if (pageIndex == pageCount && dalType != DalType.Sybase) // 最后一页Sybase 不支持双Top order by
{
return string.Format(top2Pager, pageSize+" "+columns, "top " + (leftNum == ? pageSize : leftNum) + " * ", tableName, ReverseOrderBy(where, primaryKey), GetOrderBy(where, false, primaryKey));//反序
}
if ((pageCount > || rowCount > ) && pageIndex > pageCount / ) // 页数过后半段,反转查询
{
orderBy = GetOrderBy(where, false, primaryKey);
where = ReverseOrderBy(where, primaryKey);//事先反转一次。
topN = rowCount - max;//取后面的
int rowStartTemp = rowCount - rowEnd;
rowEnd = rowCount - rowStart;
rowStart = rowStartTemp;
}
break; }
} switch (dalType)
{
case DalType.MsSql:
case DalType.Oracle:
if (version.StartsWith(""))
{
goto temtable;
// goto top3;//sql 2000
}
int index = tableName.LastIndexOf(')');
if (index > )
{
tableName = tableName.Substring(, index + );
}
string v = dalType == DalType.Oracle ? "" : " v";
string onlyWhere = "where " + SqlCreate.RemoveOrderBy(where);
onlyWhere = SqlFormat.RemoveWhereOneEqualsOne(onlyWhere);
return string.Format(rowNumberPager, GetOrderBy(where, false, primaryKey), (columns == "*" ? "t.*" : columns), tableName, onlyWhere, v, rowStart, rowEnd);
case DalType.Sybase:
temtable:
if (primaryKeyIsIdentity)
{
bool isOk = columns == "*";
if (!isOk)
{
string kv = SqlFormat.NotKeyword(primaryKey);
string[] items = columns.Split(',');
foreach (string item in items)
{
if (string.Compare(SqlFormat.NotKeyword(item), kv, StringComparison.OrdinalIgnoreCase) == )
{
isOk = true;
break;
}
}
}
else
{
columns = "t.*";
index = tableName.LastIndexOf(')');
if (index > )
{
tableName = tableName.Substring(, index + );
}
tableName += " t ";
}
if (isOk)
{ return string.Format(tempTablePagerWithIdentity, DateTime.Now.Millisecond, topN, primaryKey, tableName, where, pageSize, columns, rowStart, rowEnd, orderBy);
}
}
return string.Format(tempTablePager, DateTime.Now.Millisecond, pageIndex * pageSize + " " + columns, tableName, where, pageSize, rowStart, rowEnd, orderBy);
case DalType.Access:
top3:
if (!string.IsNullOrEmpty(orderBy)) // 反转查询
{
return string.Format(top4Pager,columns, (rowCount - max > pageSize ? pageSize : rowCount - max), topN, tableName, where, GetOrderBy(where, true, primaryKey), GetOrderBy(where, false, primaryKey), orderBy);
}
return string.Format(top3Pager, (rowCount - max > pageSize ? pageSize : rowCount - max),columns, topN, tableName, where, GetOrderBy(where, true, primaryKey), GetOrderBy(where, false, primaryKey));
case DalType.SQLite:
case DalType.MySql:
case DalType.PostgreSQL:
if (max > && primaryKeyIsIdentity && Convert.ToString(objWhere) == "" && !tableName.Contains(" "))//单表大数量时的优化成主键访问。
{
where = string.Format("{0}>=(select {0} from {1} limit {2}, 1) limit {3}", primaryKey, tableName, max, pageSize);
return string.Format(top1Pager, columns, tableName, where);
}
return string.Format(top1Pager, columns, tableName, where + " limit " + pageSize + " offset " + max);
}
return (string)Error.Throw("Pager::No Be Support:" + dalType.ToString());
}

总结:

一个数据库的基本支持、写到这里就完成了增删改查及分页。

当然,对于CYQ.Data而言,还差一些未处理:

1、多种数据库转换互通处理:DataType.cs。

2、对表的创建修改操作:SqlCreateForSchema.cs。

3、支持多数据库兼容性写法:SqlCompatible.cs。

4、其它细节。

CYQ.Data 支持 PostgreSQL 数据库的更多相关文章

  1. CYQ.Data 支持分布式数据库(主从备)高可用及负载调试

    前言: 继上一篇,介绍 CYQ.Data 在分布式缓存上支持高可用,详见:CYQ.Data 对于分布式缓存Redis.MemCache高可用的改进及性能测试 本篇介绍 CYQ.Data 在对数据库层面 ...

  2. CYQ.Data 支持WPF相关的数据控件绑定.Net获取iis版本

    CYQ.Data 支持WPF相关的数据控件绑定(2013-08-09) 事件的结果 经过多天的思考及忙碌的开发及测试,CYQ.Data 终于在UI上全面支持WPF,至此,CYQ.Data 已经可以方便 ...

  3. .net工作流引擎ccflow新增支持PostgreSQL数据库的功能的发布说明

    关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎  java工作流引擎. 各位驰骋工作流引擎爱好着,经过驰骋公司与正元公司的共同努力,ccflow支持Post ...

  4. CYQ.Data 支持WPF相关的数据控件绑定(2013-08-09)

    事件的结果 经过多天的思考及忙碌的开发及测试,CYQ.Data 终于在UI上全面支持WPF,至此,CYQ.Data 已经可以方便支持wpf的开发,同时,框架仍保留最低.net framework2.0 ...

  5. CYQ.Data 正式支持 DotNET Core 版本发布

    闲话几句: 自从上周开始,IOS人员逝去,就开始接手IOS的代码了. 并开始整理IOS的代码(包括当时一开始设计的开发框架). 在未来不远的日子里,设想是有一个系列详细的介绍I恋App和IT连App及 ...

  6. Java代码生成器加入postgresql数据库、HikariCP连接池、swagger2支持!

    目录 前言 PostgreSql VS MySql HikariCP VS Druid Swagger2 自定义参数配置一览 结语 前言   最近几天又抽时间给代码生成器增加了几个新功能(预计今晚发布 ...

  7. CYQ.Data 快速开发之UI(赋值、取值、绑定)原理

    昨夜园子猴子问了几个我CYQ.Data使用的小问题,经过简单解答后,他表示“妈妈再也不用担心我的学习",并于事后以资鼓励,希望这框架越走越好. 除了技术上的交流,双方在生活,S上面的问题上也 ...

  8. linux下PostgreSQL数据库的源码安装

    实验环境>>>>>>>>>>>>>>>>>>操作系统:CentOS release 6.3 ...

  9. 关于PDF.NET开发框架对Mysql Sqlite PostgreSQL数据库分页支持的个人看法

    关于PDF.NET开发框架的名字由来  在设计www.pwmis.com站点的时候,考虑到架构的兼容性和将来升级的可能性,最重要的是没有足够的时间去为网站添加和维护很多复杂的程序,所以在借鉴前人成功经 ...

随机推荐

  1. When to use next() and return next() in Node.js

    Some people always write return next() is to ensure that the execution stops after triggering the ca ...

  2. LeetCode Javascript实现 258. Add Digits 104. Maximum Depth of Binary Tree 226. Invert Binary Tree

    258. Add Digits Digit root 数根问题 /** * @param {number} num * @return {number} */ var addDigits = func ...

  3. Java之Collections.emptyList()、emptySet()、emptyMap()的作用和好处以及要注意的地方

    转自https://www.cnblogs.com/qiumingcheng/p/7126281.html 先说明一下好处有哪些:1,如果你想 new 一个空的 List ,而这个 List 以后也不 ...

  4. BZOJ_5055_膜法师_树状数组+离散化

    BZOJ_5055_膜法师_树状数组+离散化 Description 在经历过1e9次大型战争后的宇宙中现在还剩下n个完美维度, 现在来自多元宇宙的膜法师,想偷取其中的三个维度为伟大的长者续秒, 显然 ...

  5. LCA 各种神奇的LCA优化方法

    LCA(Least Common Ancestors) 树上问题的一种. 朴素lca很简单啦,我就不多说了,时间复杂度n^2 1.倍增LCA 时间复杂度 nlongn+klogn 其实是一种基于朴素l ...

  6. ArcGIS API for JavaScript 入门教程[1] 渊源

    ->对于萌新,你可能需要了解一下这个东西是什么 ->对于已经知道要用这个东西的开发者,你可能需要了解一下它的底层机制 不针对大牛.龟速更新ing. 转载注明出处.博客园&CSDN& ...

  7. vue不是内部或外部命令解决验证方案

    一.前提 1.该教程是在你已经安装配置好node.js和express情况下 2.你已经完成了vue和vue-cli的全局安装 3.完成以上2步后,使用vue指令,会显示"vue不是内部或外 ...

  8. 【爆料】-《阿伯泰大学毕业证书》Abertay一模一样原件

    ☞阿伯泰大学毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&a ...

  9. Gradle入门到实战(一) — 全面了解Gradle

    声明:本文来自汪磊的博客,转载请注明出处 可关注个人公众号,那里更新更及时,阅读体验更好:  友情提示由于文章是从个人公众号拷贝过来整理的,发现图片没有正常显示,没关注公众号的同学可通过如下链接查看: ...

  10. 有趣的8个IT冷知识

    阅读本文大概需要 2.8 分钟. 想要成为一名成功的程序员,我们除了了解不同编程语言的设计思路,也应当了解编程的发展历史,从而判断未来的编程技术将走向何方.接下就为大家普及下计算机发展历程中的8个冷门 ...