diff --git a/Apewer.Source/Source/DbClient.cs b/Apewer.Source/Source/DbClient.cs index ef95aa4..e54d61f 100644 --- a/Apewer.Source/Source/DbClient.cs +++ b/Apewer.Source/Source/DbClient.cs @@ -338,36 +338,6 @@ namespace Apewer.Source #region static - /// 对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。 - public 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; - } - /// 获取表名。 protected static string Table() => Table(typeof(T)); diff --git a/Apewer.Source/Source/SqlClient.cs b/Apewer.Source/Source/SqlClient.cs index 264560c..5c51830 100644 --- a/Apewer.Source/Source/SqlClient.cs +++ b/Apewer.Source/Source/SqlClient.cs @@ -1,4 +1,4 @@ -/* 2021.11.07 */ +/* 2021.12.01 */ using Apewer; using System; @@ -9,6 +9,7 @@ using System.Text; using static Apewer.Source.SourceUtility; using System.Data.SqlClient; +using System.IO; #if NETFRAMEWORK using System.Data.Sql; @@ -113,6 +114,24 @@ namespace Apewer.Source } } + /// 改变 + /// + /// + public string Change(string store) + { + if (store.IsEmpty()) return "未指定数据名称。"; + + var connect = Connect(); + if (connect.NotEmpty()) return connect; + + try + { + _db.ChangeDatabase(store); + return null; + } + catch (Exception ex) { return ex.Message(); } + } + /// 关闭连接,并释放对象所占用的系统资源。 public void Close() { @@ -249,10 +268,13 @@ namespace Apewer.Source } /// 执行。 - public IExecute Execute(string sql) => Execute(sql, null); + public IExecute Execute(string sql) => Execute(sql, null, true); + + /// 执行单条 Transact-SQL 语句,并加入参数。 + public IExecute Execute(string sql, IEnumerable parameters) => Execute(sql, parameters, true); /// 执行单条 Transact-SQL 语句,并加入参数。 - public IExecute Execute(string sql, IEnumerable parameters) + IExecute Execute(string sql, IEnumerable parameters, bool requireTransaction) { if (TextUtility.IsBlank(sql)) return Example.InvalidExecuteStatement; @@ -260,13 +282,13 @@ namespace Apewer.Source if (connected.NotEmpty()) return new Execute(false, connected); var inTransaction = _transaction != null; - if (!inTransaction) Begin(); + if (requireTransaction && !inTransaction) Begin(); try { using (var command = new SqlCommand()) { command.Connection = _db; - command.Transaction = (SqlTransaction)_transaction; + if (requireTransaction) command.Transaction = (SqlTransaction)_transaction; command.CommandTimeout = _timeout.Execute; command.CommandText = sql; if (parameters != null) @@ -277,14 +299,14 @@ namespace Apewer.Source } } var rows = command.ExecuteNonQuery(); - if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。 + if (requireTransaction && !inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。 return new Execute(true, rows); } } catch (Exception exception) { Logger.Error(nameof(SqlClient), "Execute", exception, sql); - if (!inTransaction) Rollback(); + if (requireTransaction && !inTransaction) Rollback(); return new Execute(exception); } } @@ -355,6 +377,62 @@ namespace Apewer.Source return list.ToArray(); } + /// 创建数据库,返回错误信息。 + /// 数据库名称。 + /// 成功时候返回 NULL 值,失败时返回错误信息。 + public string CreateStore(string storeName) + { + var store = storeName.Escape().ToTrim(); + if (store.IsEmpty()) return "创建失败:未指定数据库名称。"; + if (ConnectionString.IsEmpty()) return "创建失败:未指定数据库连接方式。"; + + using (var source = new SqlClient(ConnectionString)) + { + var connect = source.Connect(); + if (connect.NotEmpty()) return "创建失败:" + connect; + + var schema = source.SimpleCell("select default_schema_name from sys.database_principals where type = 'S' and name=user_name()"); + if (schema.IsEmpty()) return "创建失败:无法获取默认模式名称。"; + + var refPath = source.SimpleCell(@"select f.physical_name path from sys.filegroups g left join sys.database_files f on f.data_space_id = g.data_space_id where g.name = 'PRIMARY' and g.type = 'FG' and g.is_default = 1 and g.filegroup_guid is null"); + if (refPath.IsEmpty()) return "创建失败:无法获取文件路径。"; + + var dir = Path.GetDirectoryName(refPath); + var mdfPath = Path.Combine(dir, store) + ".mdf"; + var ldfPath = Path.Combine(dir, store) + "_log.ldf"; + + // 创建库。 + var sql1 = $@" +CREATE DATABASE [{store}] +ON PRIMARY +( + NAME = N'{store}', + FILENAME = N'{mdfPath}', + SIZE = 8192KB, + MAXSIZE = UNLIMITED, + FILEGROWTH = 4MB +) +LOG ON +( + NAME = N'{store}_log', + FILENAME = N'{ldfPath}', + SIZE = 8MB, + MAXSIZE = 1024MB, + FILEGROWTH = 4MB +) +COLLATE Chinese_PRC_CI_AS +"; + var create = source.Execute(sql1, null, false); + if (!create.Success) return TextUtility.Merge("创建失败:", create.Message); + + // 设计兼容性级别。 + var sql2 = $"ALTER DATABASE [{store}] SET COMPATIBILITY_LEVEL = 0"; + source.Execute(sql2, null, false); + + return null; + } + } + static string XType(int xtype) { switch (xtype) diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props index 898879a..fed2cd8 100644 --- a/Apewer/Apewer.props +++ b/Apewer/Apewer.props @@ -9,7 +9,7 @@ Apewer Apewer Libraries - 6.5.7 + 6.5.8 diff --git a/Apewer/NetworkUtility.cs b/Apewer/NetworkUtility.cs index 196926d..cfa990e 100644 --- a/Apewer/NetworkUtility.cs +++ b/Apewer/NetworkUtility.cs @@ -383,7 +383,7 @@ namespace Apewer #region Port - private static int[] ListActivePort(IPEndPoint[] endpoints) + private static int[] ActivePorts(IPEndPoint[] endpoints) { var list = new List(endpoints.Length); foreach (var endpoint in endpoints) @@ -398,10 +398,10 @@ namespace Apewer } /// 列出活动的 TCP 端口。 - public static int[] ListActiveTcpPort() => ListActivePort(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); + public static int[] ActiveTcpPorts() => ActivePorts(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); /// 列出活动的 UDP 端口。 - public static int[] ListActiveUdpPort() => ListActivePort(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); + public static int[] ActiveUdpPorts() => ActivePorts(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); #endregion diff --git a/Apewer/Source/Query.cs b/Apewer/Source/Query.cs index 6a35933..ba37ecf 100644 --- a/Apewer/Source/Query.cs +++ b/Apewer/Source/Query.cs @@ -303,42 +303,6 @@ namespace Apewer.Source private static T TextFormatter(object input) => (T)(Text(input) as object); private static ObjectDisposedException DisposedException { get { return new ObjectDisposedException(typeof(Query).FullName); } } - - /// 简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。 - public static string[] SimpleColumn(IDbAdo database, string statement, bool dispose = false) - { - if (database == null) return new string[0]; - var ab = new ArrayBuilder(); - using (var query = database.Query(statement)) - { - var rows = query.Rows; - if (rows > 0) - { - var added = 0; - for (int i = 0; i < rows; i++) - { - var cell = Trim(query.Text(i)); - if (string.IsNullOrEmpty(cell)) continue; - ab.Add(cell); - added++; - } - } - } - if (dispose) RuntimeUtility.Dispose(database); - return ab.Export(); - } - - /// 简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。 - public static string SimpleCell(IDbAdo database, string statement, bool dispose = false) - { - if (database == null) return ""; - var query = database.Query(statement); - var cell = Trim(Query.Text(query.Value())); - query.Dispose(); - if (dispose) RuntimeUtility.Dispose(database); - return cell; - } - #endregion #region Extension diff --git a/Apewer/Source/SourceUtility.cs b/Apewer/Source/SourceUtility.cs index dcbb987..29ec5bf 100644 --- a/Apewer/Source/SourceUtility.cs +++ b/Apewer/Source/SourceUtility.cs @@ -436,6 +436,84 @@ namespace Apewer.Source #endregion + #region DbClient + + /// 简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。 + /// 数据库客户端。 + /// 用于查询的 SQL 语句。 + /// 查询后,关闭数据库链接。 + public static string[] SimpleColumn(this IDbAdo source, string sql, bool close = false) + { + if (source == null) return new string[0]; + var ab = new ArrayBuilder(); + using (var query = source.Query(sql)) + { + var rows = query.Rows; + if (rows > 0) + { + var added = 0; + for (int i = 0; i < rows; i++) + { + var cell = TextUtility.Trim(query.Text(i)); + if (string.IsNullOrEmpty(cell)) continue; + ab.Add(cell); + added++; + } + } + } + if (close) RuntimeUtility.Dispose(source); + return ab.Export(); + } + + /// 简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。 + /// 数据库客户端。 + /// 用于查询的 SQL 语句。 + /// 查询后,关闭数据库链接。 + public static string SimpleCell(this IDbAdo source, string sql, bool close = false) + { + if (source == null) return null; + var value = null as string; + using (var query = source.Query(sql)) value = TextUtility.Trim(query.Text()); + if (close) RuntimeUtility.Dispose(source); + return value; + } + + #endregion + + #region SQL + + /// 对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。 + public static string Escape(this 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 + } } diff --git a/ChangeLog.md b/ChangeLog.md index f507f94..d6d2d7a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,15 @@  ### 最新提交 +### 6.5.8 +- Source:SqlClient 新增 CreateStore 方法,支持创建数据库。 + +### 6.5.7 +- ArrayBuilder:增加 Contains 方法和 IndexOf 方法; +- NetworkUtility:ListActivePort 返回值改为数组; +- RuntimeUtility:StartThread 默认使用前台线程; +- Source:OrmHelper 重命名为 SourceUtility。 + ### 6.5.6 - Global:增加 IsNull 和 NotNull 扩展方法; - ArrayBuilder:添加了 Add 重载方法;