From a239e710463b73633eea1af4ecdbb816e1007efb Mon Sep 17 00:00:00 2001 From: Elivo Date: Fri, 3 Dec 2021 00:01:10 +0800 Subject: [PATCH] Apewer-6.5.9 --- Apewer.Source/Source/SqlClient.cs | 24 ++++--- Apewer.Windows/_Extensions.cs | 2 +- Apewer/Apewer.props | 2 +- Apewer/ObjectSet.cs | 60 ++++++---------- Apewer/RuntimeUtility.cs | 2 +- Apewer/Source/IQuery.cs | 11 ++- Apewer/Source/Query.cs | 55 +++++++++++++- Apewer/Source/SourceUtility.cs | 115 +++++++++++++++++------------- Apewer/Source/TableStructure.cs | 13 ++-- Apewer/Web/ApiOptions.cs | 5 +- Apewer/_Extensions.cs | 3 + ChangeLog.md | 4 ++ 12 files changed, 185 insertions(+), 111 deletions(-) diff --git a/Apewer.Source/Source/SqlClient.cs b/Apewer.Source/Source/SqlClient.cs index 5c51830..24e0190 100644 --- a/Apewer.Source/Source/SqlClient.cs +++ b/Apewer.Source/Source/SqlClient.cs @@ -378,11 +378,12 @@ namespace Apewer.Source } /// 创建数据库,返回错误信息。 - /// 数据库名称。 + /// 数据库名称。 + /// 日志文件的最大 MB 数,指定非正数将不限制增长。 /// 成功时候返回 NULL 值,失败时返回错误信息。 - public string CreateStore(string storeName) + public string CreateStore(string name, int logMaxSizeMB = 1024) { - var store = storeName.Escape().ToTrim(); + var store = name.Escape().ToTrim(); if (store.IsEmpty()) return "创建失败:未指定数据库名称。"; if (ConnectionString.IsEmpty()) return "创建失败:未指定数据库连接方式。"; @@ -402,13 +403,14 @@ namespace Apewer.Source var ldfPath = Path.Combine(dir, store) + "_log.ldf"; // 创建库。 + var maxLog = logMaxSizeMB > 0 ? $"{logMaxSizeMB}MB" : "UNLIMITED"; var sql1 = $@" CREATE DATABASE [{store}] ON PRIMARY ( NAME = N'{store}', FILENAME = N'{mdfPath}', - SIZE = 8192KB, + SIZE = 4MB, MAXSIZE = UNLIMITED, FILEGROWTH = 4MB ) @@ -416,19 +418,23 @@ LOG ON ( NAME = N'{store}_log', FILENAME = N'{ldfPath}', - SIZE = 8MB, - MAXSIZE = 1024MB, - FILEGROWTH = 4MB + SIZE = 1MB, + MAXSIZE = {maxLog}, + FILEGROWTH = 1MB ) 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"; + // 设置兼容性级别。 + var sql2 = $"alter database [{store}] set compatibility_level = 0"; source.Execute(sql2, null, false); + // 设置恢复默认为“简单” + var sql3 = $"alter database [{store}] set recovery simple"; + source.Execute(sql3, null, false); + return null; } } diff --git a/Apewer.Windows/_Extensions.cs b/Apewer.Windows/_Extensions.cs index 51e5eed..4da20c0 100644 --- a/Apewer.Windows/_Extensions.cs +++ b/Apewer.Windows/_Extensions.cs @@ -4,7 +4,7 @@ using System; using System.Windows.Forms; /// 扩展方法。 -public static class Extensions +public static class Extensions_Apewer_Windows { #region Surface diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props index fed2cd8..3c33bdc 100644 --- a/Apewer/Apewer.props +++ b/Apewer/Apewer.props @@ -9,7 +9,7 @@ Apewer Apewer Libraries - 6.5.8 + 6.5.9 diff --git a/Apewer/ObjectSet.cs b/Apewer/ObjectSet.cs index fdd6809..a3fad56 100644 --- a/Apewer/ObjectSet.cs +++ b/Apewer/ObjectSet.cs @@ -16,54 +16,35 @@ namespace Apewer { /// - public ObjectSet(bool autoTrim) - { - AutoTrim = autoTrim; - } - - /// - public ObjectSet() { } + public ObjectSet(bool trimKey = false) : base(trimKey) { } } /// 字典模型。 [Serializable] - public partial class ObjectSet // where T : class + public partial class ObjectSet : IToJson { private Dictionary _origin; - private bool _autotrim = false; + private bool _trimkey = false; private bool _locked = false; /// 获取或设置字典内容。 - public T this[string key] - { - get { return Get(key); } - set { Set(key, value); } - } + public T this[string key] { get { return Get(key); } set { Set(key, value); } } /// 获取字典。 - internal Dictionary Origin - { - get { return _origin; } - } + internal Dictionary Origin { get { return _origin; } } internal bool Locked { get { return _locked; } set { _locked = value; } } - internal bool AutoTrim { get { return _autotrim; } set { _autotrim = value; } } + internal bool TrimKey { get { return _trimkey; } set { _trimkey = value; } } /// 构造函数。 - public ObjectSet() + public ObjectSet(bool trimKey = false) { - _origin = new Dictionary(); - } - - /// 构造函数。 - public ObjectSet(bool autoTrim) - { - _autotrim = autoTrim; + _trimkey = trimKey; _origin = new Dictionary(); } @@ -78,7 +59,7 @@ namespace Apewer var value = default(T); if (key != null) { - var k = _autotrim ? Trim(key) : key; + var k = _trimkey ? Trim(key) : key; lock (_origin) { contains = _origin.ContainsKey(k); @@ -94,23 +75,28 @@ namespace Apewer var success = false; if (key != null) { - var k = _autotrim ? Trim(key) : key; + var k = _trimkey ? Trim(key) : key; lock (_origin) { - if (_origin.ContainsKey(k)) - { - _origin[k] = value; - } - else - { - _origin.Add(k, value); - } + if (_origin.ContainsKey(k)) _origin[k] = value; + else _origin.Add(k, value); success = true; } } return success; } + /// 转换为 对象。 + public Json ToJson() => Json.From(_origin); + + /// 转换为 字符串。 + public override string ToString() + { + var json = ToJson(); + if (json == null) return null; + return json.ToString(true); + } + } #if !NET20 diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs index 892bddf..59688b1 100644 --- a/Apewer/RuntimeUtility.cs +++ b/Apewer/RuntimeUtility.cs @@ -415,7 +415,7 @@ namespace Apewer /// 判断指定对象是否为默认值。 public static bool IsDefault(object value) { - if (value == null) return true; + if (value.IsNull()) return true; var type = value.GetType(); if (type.IsValueType) return value.Equals(Activator.CreateInstance(type)); return false; diff --git a/Apewer/Source/IQuery.cs b/Apewer/Source/IQuery.cs index 6cb0978..85c4198 100644 --- a/Apewer/Source/IQuery.cs +++ b/Apewer/Source/IQuery.cs @@ -7,7 +7,7 @@ namespace Apewer.Source { /// 查询数据表。 - public interface IQuery : IDisposable + public interface IQuery : IDisposable, IToJson { #region 结果集的属性。 @@ -29,7 +29,7 @@ namespace Apewer.Source #endregion - #region 以对象获取结果集中的内容。 + #region 读取 DateTime 对象。 /// 获取默认表中第 0 行、第 0 列的单元格内容。 object Value(); @@ -54,6 +54,13 @@ namespace Apewer.Source #endregion + #region 模型化。 + + /// 转换为模型数组。 + ObjectSet[] ToArray(); + + #endregion + } } diff --git a/Apewer/Source/Query.cs b/Apewer/Source/Query.cs index ba37ecf..9e0df36 100644 --- a/Apewer/Source/Query.cs +++ b/Apewer/Source/Query.cs @@ -240,7 +240,7 @@ namespace Apewer.Source #endregion - #region IToJson + #region 模型化、IToJson /// 转换为 Json 对象。 public Json ToJson() @@ -251,7 +251,7 @@ namespace Apewer.Source var table = _table; if (!_disposed && table != null) { - var columnsCount = _table.Columns.Count; + var columnsCount = table.Columns.Count; for (var c = 0; c < columnsCount; c++) { var dc = table.Columns[c]; @@ -262,7 +262,7 @@ namespace Apewer.Source } var rowsCount = table.Rows.Count; - for (var r = 0; r < _table.Rows.Count; r++) + for (var r = 0; r < rowsCount; r++) { var row = Json.NewArray(); for (var c = 0; c < columnsCount; c++) @@ -294,6 +294,54 @@ namespace Apewer.Source return jsonObject; } + /// 转换为模型数组。 + public ObjectSet[] ToArray() + { + var oss = null as ObjectSet[]; + var table = _table; + if (!_disposed && table != null) + { + var width = table.Columns.Count; + var names = new string[width]; + for (var c = 0; c < width; c++) + { + var name = table.Columns[c].ColumnName; + if (name.IsEmpty()) continue; + if (names.Contains(name)) continue; + names[c] = name; + } + + var unnamed = 1; + for (var c = 0; c < width; c++) + { + if (names[c] != null) continue; + while (true) + { + var name = "unnamed_" + unnamed.ToString(); + unnamed++; + if (names.Contains(name)) continue; + names[c] = name; + break; + } + } + + var height = table.Rows.Count; + oss = new ObjectSet[height]; + for (var r = 0; r < height; r++) + { + var os = new ObjectSet(); + var dict = os.Origin; + for (var c = 0; c < width; c++) + { + var v = Value(r, c); + dict.Add(names[c], v.IsNull() ? null : v); + } + oss[r] = os; + } + } + return oss; + } + #endregion #region Static @@ -303,6 +351,7 @@ 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); } } + #endregion #region Extension diff --git a/Apewer/Source/SourceUtility.cs b/Apewer/Source/SourceUtility.cs index 29ec5bf..f4b52af 100644 --- a/Apewer/Source/SourceUtility.cs +++ b/Apewer/Source/SourceUtility.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Reflection; using System.Text; using static Apewer.NumberUtility; @@ -123,71 +124,83 @@ namespace Apewer.Source var properties = model.GetProperties(); foreach (var property in properties) { - // 必须有 setter 访问器。 - var setter = property.GetSetMethod(); - if (setter == null) continue; - // 在表结构中检查,是否包含此属性,并获取 ColumnAttribute。 - var attribute = null as ColumnAttribute; + // 在表结构中检查,是否包含此属性,并获取 ColumnAttribute 中的 Field。 + var field = null as string; for (var j = 0; j < columns.Length; j++) { if (columns[j].PropertyName == property.Name) { - attribute = columns[j]; + field = columns[j].Field; break; } } - if (attribute == null) continue; - - // 读取值。 - var value = query.Value(r, attribute.Field); - if (value == null) continue; - if (value.Equals(DBNull.Value)) continue; - - // 根据属性类型设置值。 - var pt = property.PropertyType; - if (pt.Equals(typeof(object))) setter.Invoke(record, new object[] { value }); - else if (pt.Equals(typeof(byte[]))) setter.Invoke(record, new object[] { (byte[])value }); - else if (pt.Equals(typeof(string))) setter.Invoke(record, new object[] { value.ToString() }); - - else if (pt.Equals(typeof(DateTime))) setter.Invoke(record, new object[] { value }); - else if (pt.Equals(typeof(byte))) setter.Invoke(record, new object[] { Byte(value) }); - else if (pt.Equals(typeof(sbyte))) setter.Invoke(record, new object[] { SByte(value) }); - else if (pt.Equals(typeof(short))) setter.Invoke(record, new object[] { Int16(value) }); - else if (pt.Equals(typeof(ushort))) setter.Invoke(record, new object[] { UInt16(value) }); - else if (pt.Equals(typeof(int))) setter.Invoke(record, new object[] { Int32(value) }); - else if (pt.Equals(typeof(uint))) setter.Invoke(record, new object[] { UInt32(value) }); - else if (pt.Equals(typeof(long))) setter.Invoke(record, new object[] { Int64(value) }); - else if (pt.Equals(typeof(ulong))) setter.Invoke(record, new object[] { UInt64(value) }); - else if (pt.Equals(typeof(float))) setter.Invoke(record, new object[] { Single(value) }); - else if (pt.Equals(typeof(double))) setter.Invoke(record, new object[] { Double(value) }); - else if (pt.Equals(typeof(decimal))) setter.Invoke(record, new object[] { Decimal(value) }); - - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable((DateTime)value) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Byte(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(SByte(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int16(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt16(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int32(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt32(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int64(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt64(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Single(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Double(value)) }); - else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Decimal(value)) }); - - else + if (field == null) { - try - { - setter.Invoke(record, new object[] { query.Value(r, attribute.Field) }); - } - catch { } + if (ts.Attribute != null && ts.AllProperties) continue; + field = property.Name; } + + var value = query.Value(r, field); + var setted = Set(record, property, value); } return record; } + static bool Set(object record, PropertyInfo property, object value) + { + // 读取值。 + if (value == null) return false; + if (value.Equals(DBNull.Value)) return false; + + // 必须有 setter 访问器。 + var setter = property.GetSetMethod(); + if (setter == null) return false; + + // 根据属性类型设置值。 + var pt = property.PropertyType; + if (pt.Equals(typeof(object))) setter.Invoke(record, new object[] { value }); + else if (pt.Equals(typeof(byte[]))) setter.Invoke(record, new object[] { (byte[])value }); + else if (pt.Equals(typeof(string))) setter.Invoke(record, new object[] { value.ToString() }); + + else if (pt.Equals(typeof(DateTime))) setter.Invoke(record, new object[] { value }); + else if (pt.Equals(typeof(byte))) setter.Invoke(record, new object[] { Byte(value) }); + else if (pt.Equals(typeof(sbyte))) setter.Invoke(record, new object[] { SByte(value) }); + else if (pt.Equals(typeof(short))) setter.Invoke(record, new object[] { Int16(value) }); + else if (pt.Equals(typeof(ushort))) setter.Invoke(record, new object[] { UInt16(value) }); + else if (pt.Equals(typeof(int))) setter.Invoke(record, new object[] { Int32(value) }); + else if (pt.Equals(typeof(uint))) setter.Invoke(record, new object[] { UInt32(value) }); + else if (pt.Equals(typeof(long))) setter.Invoke(record, new object[] { Int64(value) }); + else if (pt.Equals(typeof(ulong))) setter.Invoke(record, new object[] { UInt64(value) }); + else if (pt.Equals(typeof(float))) setter.Invoke(record, new object[] { Single(value) }); + else if (pt.Equals(typeof(double))) setter.Invoke(record, new object[] { Double(value) }); + else if (pt.Equals(typeof(decimal))) setter.Invoke(record, new object[] { Decimal(value) }); + + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable((DateTime)value) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Byte(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(SByte(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int16(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt16(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int32(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt32(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int64(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt64(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Single(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Double(value)) }); + else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Decimal(value)) }); + + else + { + try + { + setter.Invoke(record, new object[] { value }); + return true; + } + catch { } + } + return false; + } + #endregion #region IOrm diff --git a/Apewer/Source/TableStructure.cs b/Apewer/Source/TableStructure.cs index 388766d..afa03e2 100644 --- a/Apewer/Source/TableStructure.cs +++ b/Apewer/Source/TableStructure.cs @@ -120,12 +120,15 @@ namespace Apewer.Source ts._attribute = ta; ts._key = key; ts._flag = flag; - ts._name = ta.Name; - ts._description = ta.Description; - ts._allprops = ta.AllProperties; - ts._independent = ta.Independent; ts._columns = columns; ts._model = model; + if (ta != null) + { + ts._name = ta.Name; + ts._description = ta.Description; + ts._allprops = ta.AllProperties; + ts._independent = ta.Independent; + } // 加入缓存。 if (useCache) @@ -168,7 +171,7 @@ namespace Apewer.Source { if (!str.StartsWith("_")) str = TextUtility.Merge("_", str); } - + return str; } diff --git a/Apewer/Web/ApiOptions.cs b/Apewer/Web/ApiOptions.cs index ac9c255..5bd93b0 100644 --- a/Apewer/Web/ApiOptions.cs +++ b/Apewer/Web/ApiOptions.cs @@ -105,7 +105,10 @@ namespace Apewer.Web ///
WithDuration = TRUE ///
WithParameters = TRUE /// - public ApiOptions() => Debug(); + public ApiOptions() + { + Debug(); + } [Conditional("DEBUG")] void Debug() diff --git a/Apewer/_Extensions.cs b/Apewer/_Extensions.cs index e870010..93762ef 100644 --- a/Apewer/_Extensions.cs +++ b/Apewer/_Extensions.cs @@ -21,6 +21,9 @@ public static class Extensions_Apewer /// 不是 NULL 值。 public static bool NotNull(this object @this) => @this != null && !@this.Equals(DBNull.Value); + /// 是默认值。 + public static bool IsDefault(this object @this) => RuntimeUtility.IsDefault(@this); + #region Class Utility /// 克隆对象,创建新对象。可指定包含对象的属性和字段。 diff --git a/ChangeLog.md b/ChangeLog.md index d6d2d7a..af7bc37 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,10 @@  ### 最新提交 +### 6.5.9 +- Source:修正 Query 填充无特性类型时的报错; +- Source:新增 Query.ToArray 方法,支持 dynamic。 + ### 6.5.8 - Source:SqlClient 新增 CreateStore 方法,支持创建数据库。