CYQ.Data 支持 PostgreSQL 数据库
前言:
很久之前,就有同学问我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 数据库的更多相关文章
- CYQ.Data 支持分布式数据库(主从备)高可用及负载调试
前言: 继上一篇,介绍 CYQ.Data 在分布式缓存上支持高可用,详见:CYQ.Data 对于分布式缓存Redis.MemCache高可用的改进及性能测试 本篇介绍 CYQ.Data 在对数据库层面 ...
- CYQ.Data 支持WPF相关的数据控件绑定.Net获取iis版本
CYQ.Data 支持WPF相关的数据控件绑定(2013-08-09) 事件的结果 经过多天的思考及忙碌的开发及测试,CYQ.Data 终于在UI上全面支持WPF,至此,CYQ.Data 已经可以方便 ...
- .net工作流引擎ccflow新增支持PostgreSQL数据库的功能的发布说明
关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 各位驰骋工作流引擎爱好着,经过驰骋公司与正元公司的共同努力,ccflow支持Post ...
- CYQ.Data 支持WPF相关的数据控件绑定(2013-08-09)
事件的结果 经过多天的思考及忙碌的开发及测试,CYQ.Data 终于在UI上全面支持WPF,至此,CYQ.Data 已经可以方便支持wpf的开发,同时,框架仍保留最低.net framework2.0 ...
- CYQ.Data 正式支持 DotNET Core 版本发布
闲话几句: 自从上周开始,IOS人员逝去,就开始接手IOS的代码了. 并开始整理IOS的代码(包括当时一开始设计的开发框架). 在未来不远的日子里,设想是有一个系列详细的介绍I恋App和IT连App及 ...
- Java代码生成器加入postgresql数据库、HikariCP连接池、swagger2支持!
目录 前言 PostgreSql VS MySql HikariCP VS Druid Swagger2 自定义参数配置一览 结语 前言 最近几天又抽时间给代码生成器增加了几个新功能(预计今晚发布 ...
- CYQ.Data 快速开发之UI(赋值、取值、绑定)原理
昨夜园子猴子问了几个我CYQ.Data使用的小问题,经过简单解答后,他表示“妈妈再也不用担心我的学习",并于事后以资鼓励,希望这框架越走越好. 除了技术上的交流,双方在生活,S上面的问题上也 ...
- linux下PostgreSQL数据库的源码安装
实验环境>>>>>>>>>>>>>>>>>>操作系统:CentOS release 6.3 ...
- 关于PDF.NET开发框架对Mysql Sqlite PostgreSQL数据库分页支持的个人看法
关于PDF.NET开发框架的名字由来 在设计www.pwmis.com站点的时候,考虑到架构的兼容性和将来升级的可能性,最重要的是没有足够的时间去为网站添加和维护很多复杂的程序,所以在借鉴前人成功经 ...
随机推荐
- 用R语言进行文本挖掘和主题建模
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 我们每天都会遇到各种各样的文本数据,但大部分是非结构化的,并不是全部都是有价值的. 据估计,全球约80%的数据是非结构化的.这包括音频,视频 ...
- SSM-SpringMVC-14:SpringMVC中大话注解式开发基础--呕心沥血版
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 注解的基础我不再多啰嗦,百度一搜很多,很详细啊,我就讲一下SpringMVC中的注解入门 通过注解的方式定义 ...
- 写个批处理脚本来帮忙干活--遍历文件夹&字符串处理
这次打算写几篇关于脚本方面的博客,主要是记录一下 Gradle 脚本和批处理脚本的一些写法,方便后续查阅. 前言 平常开发过程中,一些较为重复的手工性工作,如果能让脚本来帮忙处理,自然是最好的,刚好之 ...
- JavaScript-//FOR/IN循环。当使用for/in循环遍历关联数组时,就可以清晰地体会到for/in的强大之处。
<script> //FOR/IN循环.当使用for/in循环遍历关联数组时,就可以清晰地体会到for/in的强大之处. function getvalue(portfolio){ var ...
- centos6.9 升级内核版本
想在centos6.9上安装docket,不过因为内核版本是2.6的故而想升级到最新的内核版本 晚上有编译升级的比较麻烦,不过有助于理解内核升级,我使用的直接升级到最新版方法 1. 导入public ...
- 关于bootstrap两个模态框的问题
今天不知道为什么,其中一个模态框无法正确触发,但是将两个模态框在body里的顺序调一下就都可以正确触发.
- 玩转JPA(一)---异常:Repeated column in mapping for entity/should be mapped with insert="false" update="fal
最近用JPA遇到这样一个问题:Repeated column in mapping for entity: com.ketayao.security.entity.main.User column: ...
- IntelliJ IDEA(十) :常用操作
IDEA功能详细,快捷键繁多,但是实际开发时不是所有都能用上,如果我们熟悉一些常用的也足够满足我们日常开发了,多的也只是提高我们的B格. 1.自定义主题 IDEA默认的主题有三款,分别是Intelli ...
- 【bzoj 3601】一个人的数论 (莫比乌斯反演+伯努利数)
题解: (吐槽:网上题解那个不严谨猜测真是没谁了……关键是还猜得辣么准……) 直接化简到求和那一段: $f_{d}(n)=\sum_{t|n}\mu(t)t^{d}\sum_{i=1}^{\frac{ ...
- Keepalived + nginx实现高可用性和负载均衡
在前面的一篇中讲到了Heartbeat作为高可用服务架构的解决方案,今天有试验了一种全新的解决方案,即采用Keepalived来实现这个功能. Keepalived 是一种高性能的服务器高可用或热备解 ...