using System;
using System.Data;
using System.Data.Common;
using NewLife;
using NewLife.Log;
namespace XCode.DataAccessLayer
{
/// <summary>远程数据库。一般是分为客户端服务器的中大型数据库,该类数据库支持完整的SQL92</summary>
abstract class RemoteDb : DbBase
{
#region 属性
/// <summary>系统数据库名</summary>
public virtual String SystemDatabaseName => "master";
private String _User;
/// <summary>用户名UserID</summary>
public String User
{
get
{
if (_User != null) return _User;
var connStr = ConnectionString;
if (String.IsNullOrEmpty(connStr)) return null;
var ocsb = Factory.CreateConnectionStringBuilder();
ocsb.ConnectionString = connStr;
if (ocsb.ContainsKey("User ID"))
_User = (String)ocsb["User ID"];
else if (ocsb.ContainsKey("User"))
_User = (String)ocsb["User"];
else if (ocsb.ContainsKey("uid"))
_User = (String)ocsb["uid"];
else
_User = String.Empty;
return _User;
}
}
#endregion
#region 分页
/// <summary>已重写。获取分页</summary>
/// <param name="sql">SQL语句</param>
/// <param name="startRowIndex">开始行,0表示第一行</param>
/// <param name="maximumRows">最大返回行数,0表示所有行</param>
/// <param name="keyColumn">主键列。用于not in分页</param>
/// <returns></returns>
public override String PageSplit(String sql, Int64 startRowIndex, Int64 maximumRows, String keyColumn) => PageSplitByLimit(sql, startRowIndex, maximumRows);
/// <summary>构造分页SQL</summary>
/// <param name="builder">查询生成器</param>
/// <param name="startRowIndex">开始行,0表示第一行</param>
/// <param name="maximumRows">最大返回行数,0表示所有行</param>
/// <returns>分页SQL</returns>
public override SelectBuilder PageSplit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows) => PageSplitByLimit(builder, startRowIndex, maximumRows);
/// <summary>已重写。获取分页</summary>
/// <param name="sql">SQL语句</param>
/// <param name="startRowIndex">开始行,0表示第一行</param>
/// <param name="maximumRows">最大返回行数,0表示所有行</param>
/// <returns></returns>
public static String PageSplitByLimit(String sql, Int64 startRowIndex, Int64 maximumRows)
{
// 从第一行开始,不需要分页
if (startRowIndex <= 0)
{
if (maximumRows < 1) return sql;
return $"{sql} limit {maximumRows}";
}
if (maximumRows < 1) throw new NotSupportedException("不支持取第几条数据之后的所有数据!");
return $"{sql} limit {startRowIndex}, {maximumRows}";
}
/// <summary>构造分页SQL</summary>
/// <param name="builder">查询生成器</param>
/// <param name="startRowIndex">开始行,0表示第一行</param>
/// <param name="maximumRows">最大返回行数,0表示所有行</param>
/// <returns>分页SQL</returns>
public static SelectBuilder PageSplitByLimit(SelectBuilder builder, Int64 startRowIndex, Int64 maximumRows)
{
// 从第一行开始,不需要分页
if (startRowIndex <= 0)
{
if (maximumRows > 0) builder.Limit = $"limit {maximumRows}";
return builder;
}
if (maximumRows < 1) throw new NotSupportedException("不支持取第几条数据之后的所有数据!");
builder.Limit = $"limit {startRowIndex}, {maximumRows}";
return builder;
}
#endregion
}
/// <summary>远程数据库会话</summary>
abstract class RemoteDbSession : DbSession
{
#region 属性
/// <summary>系统数据库名</summary>
public String SystemDatabaseName => (Database as RemoteDb)?.SystemDatabaseName;
#endregion
#region 构造函数
public RemoteDbSession(IDatabase db) : base(db) { }
#endregion
#region 架构
public override DataTable GetSchema(DbConnection conn, String collectionName, String[] restrictionValues)
{
try
{
return base.GetSchema(conn, collectionName, restrictionValues);
}
catch (Exception ex)
{
DAL.WriteLog("[{2}]GetSchema({0})异常重试!{1}", collectionName, ex.Message, Database.ConnName);
// 如果没有数据库,登录会失败,需要切换到系统数据库再试试
return ProcessWithSystem((s, c) => base.GetSchema(c, collectionName, restrictionValues)) as DataTable;
}
}
#endregion
#region 系统权限处理
public Object ProcessWithSystem(Func<IDbSession, DbConnection, Object> callback)
{
var dbname = Database.DatabaseName;
var sysdbname = SystemDatabaseName;
// 如果指定了数据库名,并且不是master,则切换到master
if (!dbname.IsNullOrEmpty() && !dbname.EqualIgnoreCase(sysdbname))
{
if (DAL.Debug) WriteLog("切换到系统库[{0}]", sysdbname);
using var conn = Database.Factory.CreateConnection();
try
{
//conn.ConnectionString = Database.ConnectionString;
OpenDatabase(conn, Database.ConnectionString, sysdbname);
return callback(this, conn);
}
catch (Exception ex)
{
XTrace.WriteException(ex);
throw;
}
finally
{
if (DAL.Debug) WriteLog("退出系统库[{0}],回到[{1}]", sysdbname, dbname);
}
}
else
{
using var conn = Database.OpenConnection();
return callback(this, conn);
}
}
private static void OpenDatabase(IDbConnection conn, String connStr, String dbName)
{
// 如果没有打开,则改变链接字符串
var builder = new ConnectionStringBuilder(connStr);
var flag = false;
if (builder["Database"] != null)
{
builder["Database"] = dbName;
flag = true;
}
else if (builder["Initial Catalog"] != null)
{
builder["Initial Catalog"] = dbName;
flag = true;
}
if (flag)
{
connStr = builder.ToString();
//WriteLog("系统级:{0}", connStr);
}
conn.ConnectionString = connStr;
conn.Open();
}
#endregion
}
/// <summary>远程数据库元数据</summary>
abstract class RemoteDbMetaData : DbMetaData
{
#region 属性
#endregion
#region 架构定义
public override Object SetSchema(DDLSchema schema, params Object[] values)
{
var session = Database.CreateSession();
var databaseName = Database.DatabaseName;
// ahuang 2014.06.12 类型强制转string的bug
if (values != null && values.Length > 0 && values[0] is String str && !str.IsNullOrEmpty()) databaseName = str;
switch (schema)
{
//case DDLSchema.TableExist:
// return session.QueryCount(GetSchemaSQL(schema, values)) > 0;
case DDLSchema.DatabaseExist:
return DatabaseExist(databaseName);
case DDLSchema.CreateDatabase:
values = new Object[] { databaseName, values == null || values.Length < 2 ? null : values[1] };
var sql = base.GetSchemaSQL(schema, values);
if (sql.IsNullOrEmpty()) return null;
if (session is RemoteDbSession ss)
{
ss.WriteSQL(sql);
return ss.ProcessWithSystem((s, c) =>
{
using var cmd = Database.Factory.CreateCommand();
cmd.Connection = c;
cmd.CommandText = sql;
return cmd.ExecuteNonQuery();
});
}
return 0;
//case DDLSchema.DropDatabase:
// return DropDatabase(databaseName);
default:
break;
}
return base.SetSchema(schema, values);
}
protected virtual Boolean DatabaseExist(String databaseName)
{
var session = Database.CreateSession();
return session.QueryCount(GetSchemaSQL(DDLSchema.DatabaseExist, new Object[] { databaseName })) > 0;
}
//protected virtual Boolean DropDatabase(String databaseName)
//{
// var session = Database.CreateSession();
// var sql = DropDatabaseSQL(databaseName);
// if (sql.IsNullOrEmpty()) return session.Execute(sql) > 0;
// return true;
//}
//Object ProcessWithSystem(Func<IDbSession, Object> callback) => (Database.CreateSession() as RemoteDbSession).ProcessWithSystem((s, c) => callback(s));
#endregion
}
}
|