You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
10 KiB
339 lines
10 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Text;
|
|
|
|
namespace Apewer.Source
|
|
{
|
|
|
|
/// <summary></summary>
|
|
abstract class DbClient
|
|
{
|
|
|
|
/// <summary></summary>
|
|
public virtual Logger Logger { get; set; }
|
|
|
|
#region Connection
|
|
|
|
DbConnection _conn = null;
|
|
string _str = null;
|
|
|
|
/// <summary></summary>
|
|
public Timeout Timeout { get; set; }
|
|
|
|
/// <summary></summary>
|
|
public DbConnection Connection { get => _conn; }
|
|
|
|
/// <summary></summary>
|
|
public bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); }
|
|
|
|
/// <summary>连接字符串。</summary>
|
|
public string ConnectionString { get => _str; }
|
|
|
|
/// <summary></summary>
|
|
public virtual string Connect()
|
|
{
|
|
if (_conn == null)
|
|
{
|
|
_str = GetConnectionString();
|
|
_conn = NewConnection();
|
|
_conn.ConnectionString = _str;
|
|
}
|
|
else
|
|
{
|
|
if (_conn.State == ConnectionState.Open) return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
_conn.Open();
|
|
switch (_conn.State)
|
|
{
|
|
case ConnectionState.Open: return null;
|
|
default: return $"连接失败,当前处于 {_conn.State} 状态。";
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error(this, "Connect", ex, _conn.ConnectionString);
|
|
Close();
|
|
return ex.Message;
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public void Close()
|
|
{
|
|
if (_conn != null)
|
|
{
|
|
if (_transaction != null)
|
|
{
|
|
if (_autocommit) Commit();
|
|
else Rollback();
|
|
}
|
|
_conn.Close();
|
|
_conn.Dispose();
|
|
_conn = null;
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public void Dispose() { Close(); }
|
|
|
|
#endregion
|
|
|
|
#region Transaction
|
|
|
|
private DbTransaction _transaction = null;
|
|
private bool _autocommit = false;
|
|
|
|
/// <summary>启动事务。</summary>
|
|
public string Begin(bool commit = true) => Begin(commit, null);
|
|
|
|
/// <summary>启动事务。</summary>
|
|
public string Begin(bool commit, Class<System.Data.IsolationLevel> isolation)
|
|
{
|
|
if (Connect() != null) return "未连接。";
|
|
if (_transaction != null) return "存在已启动的事务,无法再次启动。";
|
|
try
|
|
{
|
|
_transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction();
|
|
_autocommit = commit;
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Error(this, "Commit", ex.Message());
|
|
return ex.Message();
|
|
}
|
|
}
|
|
|
|
/// <summary>提交事务。</summary>
|
|
public string Commit()
|
|
{
|
|
if (_transaction == null) return "事务不存在。";
|
|
try
|
|
{
|
|
_transaction.Commit();
|
|
RuntimeUtility.Dispose(_transaction);
|
|
_transaction = null;
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RuntimeUtility.Dispose(_transaction);
|
|
_transaction = null;
|
|
Logger.Error(this, "Commit", ex.Message());
|
|
return ex.Message();
|
|
}
|
|
}
|
|
|
|
/// <summary>从挂起状态回滚事务。</summary>
|
|
public string Rollback()
|
|
{
|
|
if (_transaction == null) return "事务不存在。";
|
|
try
|
|
{
|
|
_transaction.Rollback();
|
|
RuntimeUtility.Dispose(_transaction);
|
|
_transaction = null;
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RuntimeUtility.Dispose(_transaction);
|
|
_transaction = null;
|
|
Logger.Error(this, "Rollback", ex.Message());
|
|
return ex.Message();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ADO
|
|
|
|
/// <summary>查询。</summary>
|
|
public IQuery Query(string sql) => Query(sql, null);
|
|
|
|
/// <summary>查询。</summary>
|
|
public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
|
|
{
|
|
if (TextUtility.IsBlank(sql)) return new Query(false, "语句无效。");
|
|
var connected = Connect();
|
|
if (connected.NotEmpty()) return new Query(false, connected);
|
|
|
|
try
|
|
{
|
|
using (var command = NewCommand())
|
|
{
|
|
command.Connection = _conn;
|
|
if (Timeout != null) command.CommandTimeout = Timeout.Query;
|
|
command.CommandText = sql;
|
|
if (parameters != null)
|
|
{
|
|
foreach (var parameter in parameters)
|
|
{
|
|
if (parameter != null) command.Parameters.Add(parameter);
|
|
}
|
|
}
|
|
using (var ds = new DataSet())
|
|
{
|
|
using (var da = NewDataAdapter(sql))
|
|
{
|
|
var name = "table_" + Guid.NewGuid().ToString("\n");
|
|
da.Fill(ds, name);
|
|
var table = ds.Tables[name];
|
|
return new Query(table, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Logger.Error(this, "Query", exception, sql);
|
|
return new Query(exception);
|
|
}
|
|
}
|
|
|
|
/// <summary>执行。</summary>
|
|
public IExecute Execute(string sql) => Execute(sql, null);
|
|
|
|
/// <summary>执行单条 Transact-SQL 语句,并加入参数。</summary>
|
|
public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters)
|
|
{
|
|
if (TextUtility.IsBlank(sql)) return Example.InvalidExecuteStatement;
|
|
|
|
var connected = Connect();
|
|
if (connected.NotEmpty()) return new Execute(false, connected);
|
|
|
|
var inTransaction = _transaction != null;
|
|
if (!inTransaction) Begin();
|
|
try
|
|
{
|
|
using (var command = NewCommand())
|
|
{
|
|
command.Connection = _conn;
|
|
command.Transaction = (DbTransaction)_transaction;
|
|
if (Timeout != null) command.CommandTimeout = Timeout.Execute;
|
|
command.CommandText = sql;
|
|
if (parameters != null)
|
|
{
|
|
foreach (var parameter in parameters)
|
|
{
|
|
if (parameter != null) command.Parameters.Add(parameter);
|
|
}
|
|
}
|
|
var rows = command.ExecuteNonQuery();
|
|
if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
|
|
return new Execute(true, rows);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Logger.Error(this, "Execute", exception, sql);
|
|
if (!inTransaction) Rollback();
|
|
return new Execute(exception);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ORM - Query
|
|
|
|
/// <summary>查询记录。</summary>
|
|
/// <param name="model">记录模型。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
public Result<object[]> Query(Type model, string sql)
|
|
{
|
|
if (_conn == null) return new Result<object[]>("连接无效。");
|
|
if (model == null) return new Result<object[]>("数据模型类型无效。");
|
|
if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
|
|
|
|
var query = Query(sql);
|
|
var result = null as Result<object[]>;
|
|
|
|
if (query.Success)
|
|
{
|
|
try
|
|
{
|
|
var array = OrmHelper.Fill(query, model);
|
|
result = new Result<object[]>(array);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
result = new Result<object[]>(ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = new Result<object[]>(query.Message);
|
|
}
|
|
|
|
query.Dispose();
|
|
return result;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
public Result<T[]> Query<T>(string sql) where T : class, new()
|
|
{
|
|
var query = Query(sql);
|
|
if (!query.Success) return new Result<T[]>(query.Message);
|
|
var records = query.Fill<T>();
|
|
query.Dispose();
|
|
|
|
var result = new Result<T[]>(records);
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Static
|
|
|
|
/// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
|
|
protected static string Escape(string text, int bytes = 0)
|
|
{
|
|
if (text.IsEmpty()) return "";
|
|
|
|
var t = text ?? "";
|
|
t = t.Replace("\\", "\\\\");
|
|
t = t.Replace("'", "\\'");
|
|
t = t.Replace("\n", "\\n");
|
|
t = t.Replace("\r", "\\r");
|
|
t = t.Replace("\b", "\\b");
|
|
t = t.Replace("\t", "\\t");
|
|
t = t.Replace("\f", "\\f");
|
|
|
|
if (bytes > 5)
|
|
{
|
|
if (t.Bytes(Encoding.UTF8).Length > bytes)
|
|
{
|
|
while (true)
|
|
{
|
|
t = t.Substring(0, t.Length - 1);
|
|
if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break;
|
|
}
|
|
t = t + " ...";
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary></summary>
|
|
protected abstract string GetConnectionString();
|
|
|
|
/// <summary></summary>
|
|
protected abstract DbConnection NewConnection();
|
|
|
|
/// <summary></summary>
|
|
protected abstract DbDataAdapter NewDataAdapter(string sql);
|
|
|
|
/// <summary></summary>
|
|
protected abstract DbCommand NewCommand();
|
|
|
|
}
|
|
|
|
}
|
|
|