Browse Source

Apewer-6.5.10

dev
王厅 4 years ago
parent
commit
00344ebfd0
  1. 42
      Apewer.Source/Source/MySql.cs
  2. 69
      Apewer.Source/Source/SqlClient.cs
  3. 4
      Apewer.Source/Source/SqlClientThin.cs
  4. 27
      Apewer.Source/Source/Sqlite.cs
  5. 2
      Apewer/Apewer.props
  6. 8
      Apewer/NumberUtility.cs
  7. 5
      Apewer/ObjectSet.cs
  8. 10
      Apewer/RuntimeUtility.cs
  9. 40
      Apewer/Source/ColumnAttribute.cs
  10. 7
      Apewer/Source/ColumnType.cs
  11. 12
      Apewer/Source/IncrementalAttribute.cs
  12. 12
      Apewer/Source/PrimaryKeyAttribute.cs
  13. 59
      Apewer/Source/SourceUtility.cs
  14. 2
      Apewer/Source/TableStructure.cs
  15. 8
      ChangeLog.md

42
Apewer.Source/Source/MySql.cs

@ -400,7 +400,7 @@ namespace Apewer.Source
var columns = new List<string>(structure.Columns.Length);
var columnsAdded = 0;
var primarykey = null as string;
var primarykeys = new List<string>();
foreach (var column in structure.Columns)
{
// 检查 Independent 特性。
@ -409,11 +409,14 @@ namespace Apewer.Source
// 字段。
var type = Declaration(column);
if (type.IsEmpty()) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
if (!column.Independent)
{
if (column.PrimaryKey) primarykeys.Add(column.Field);
if (column.Incremental) type += " auto_increment";
}
columns.Add(type);
columnsAdded++;
// 主键。
if (column.Property.Name == "Key") primarykey = column.Field;
}
columns.Capacity = columnsAdded;
var table = structure.Name;
@ -421,14 +424,8 @@ namespace Apewer.Source
// 设置主键。
string sql;
if (!structure.Independent && !string.IsNullOrEmpty(primarykey))
{
sql = TextUtility.Merge("create table `", table, "`(", joined, ", primary key (", primarykey, ") ) engine=innodb default charset=utf8mb4; ");
}
else
{
sql = TextUtility.Merge("create table `", table, "`(", joined, ") engine=innodb default charset=utf8mb4; ");
}
var sqlPrimaryKey = primarykeys.Count < 1 ? "" : (", primary key (" + string.Join(", ", primarykeys.ToArray()) + ")");
sql = TextUtility.Merge("create table `", table, "`(", joined, sqlPrimaryKey, ") engine=innodb default charset=utf8mb4; ");
return sql;
}
@ -487,7 +484,15 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter);
// 排除字段。
var excluded = new List<string>();
foreach (var ca in structure.Columns)
{
// 排除不使用 ORM 的属性。
if (ca.Independent || ca.Incremental) excluded.Add(ca.Field);
}
var ps = structure.CreateParameters(record, Parameter, excluded);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
@ -522,7 +527,16 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter, "_key");
// 排除字段。
var excluded = new List<string>();
if (structure.Key != null) excluded.Add(structure.Key.Field);
foreach (var ca in structure.Columns)
{
// 排除不使用 ORM 的属性、自增属性和主键属性。
if (ca.Independent || ca.Incremental || ca.PrimaryKey) excluded.Add(ca.Field);
}
var ps = structure.CreateParameters(record, Parameter, excluded);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";

69
Apewer.Source/Source/SqlClient.cs

@ -1,4 +1,4 @@
/* 2021.12.01 */
/* 2021.12.07 */
using Apewer;
using System;
@ -311,6 +311,39 @@ namespace Apewer.Source
}
}
/// <summary>批量插入,必须在 DataTable 中指定表名,或指定 tableName 参数。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public void BulkCopy(DataTable table, string tableName = null)
{
// 检查 table 参数。
if (table == null) throw new ArgumentNullException(nameof(table));
if (table.Rows.Count < 1) return;
// 检查 tableName 参数。
if (tableName.IsEmpty()) tableName = table.TableName;
if (tableName.IsEmpty()) throw new ArgumentException("未指定表名。");
// 连接数据库。
var connect = Connect();
if (connect.NotEmpty()) throw new Exception(connect);
// 批量插入。
var bc = null as SqlBulkCopy;
try
{
bc = new SqlBulkCopy(_db);
bc.DestinationTableName = tableName;
bc.BatchSize = table.Rows.Count;
bc.WriteToServer(table);
}
catch (Exception ex)
{
try { bc.Close(); } catch { }
throw ex;
}
}
#endregion
#region ORM
@ -566,11 +599,16 @@ COLLATE Chinese_PRC_CI_AS
if (structure.Independent && column.Independent) continue;
var type = Declaration(column);
if (!column.Independent && column.Property.Name == "Key") type = type + " primary key";
if (!column.Independent)
{
if (column.PrimaryKey) type = type + " primary key";
if (column.Incremental) type += " identity";
}
if (string.IsNullOrEmpty(type)) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
sqlcolumns.Add(type);
}
if (sqlcolumns.Count < 1) return $"无法对类型 {model.FullName} 创建表:没有定义任何字段。";
var sql = TextUtility.Merge("create table [", structure.Name, "](", string.Join(", ", sqlcolumns.ToArray()), "); ");
var execute = Execute(sql);
if (execute.Success) return TextUtility.Empty;
@ -589,7 +627,15 @@ COLLATE Chinese_PRC_CI_AS
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter);
// 排除字段。
var excluded = new List<string>();
foreach (var ca in structure.Columns)
{
// 排除不使用 ORM 的属性。
if (ca.Independent || ca.Incremental) excluded.Add(ca.Field);
}
var ps = structure.CreateParameters(record, Parameter, excluded);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
@ -625,7 +671,16 @@ COLLATE Chinese_PRC_CI_AS
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter, "_key");
// 排除字段。
var excluded = new List<string>();
if (structure.Key != null) excluded.Add(structure.Key.Field);
foreach (var ca in structure.Columns)
{
// 排除不使用 ORM 的属性、自增属性和主键属性。
if (ca.Independent || ca.Incremental || ca.PrimaryKey) excluded.Add(ca.Field);
}
var ps = structure.CreateParameters(record, Parameter, excluded);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
@ -756,6 +811,9 @@ COLLATE Chinese_PRC_CI_AS
var vtype = SqlDbType.BigInt;
switch (type)
{
case ColumnType.Boolean:
vtype = SqlDbType.Bit;
break;
case ColumnType.Bytes:
vtype = SqlDbType.Image;
break;
@ -853,6 +911,9 @@ COLLATE Chinese_PRC_CI_AS
var length = Math.Max(0, vcolumn.Length);
switch (vcolumn.Type)
{
case ColumnType.Boolean:
type = "bit";
break;
case ColumnType.Integer:
type = "bigint";
break;

4
Apewer.Source/Source/SqlClientThin.cs

@ -194,7 +194,7 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter);
var ps = structure.CreateParameters(record, Parameter, null);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
@ -230,7 +230,7 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter, "_key");
var ps = structure.CreateParameters(record, Parameter, null);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";

27
Apewer.Source/Source/Sqlite.cs

@ -409,6 +409,12 @@ namespace Apewer.Source
{
return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
}
if (!column.Independent)
{
if (column.PrimaryKey) type = type + " primary key";
}
sqlcolumns.Add(type);
}
var sql = TextUtility.Merge("create table [", structure.Name, "](", TextUtility.Join(", ", sqlcolumns), "); ");
@ -430,8 +436,16 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
// 排除字段。
var excluded = new List<string>();
foreach (var ca in structure.Columns)
{
// 排除不使用 ORM 的属性。
if (ca.Independent || ca.Incremental) excluded.Add(ca.Field);
}
// 准备参数。
var ps = structure.CreateParameters(record, Parameter);
var ps = structure.CreateParameters(record, Parameter, excluded);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
@ -476,8 +490,17 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
// 排除字段。
var excluded = new List<string>();
if (structure.Key != null) excluded.Add(structure.Key.Field);
foreach (var ca in structure.Columns)
{
// 排除不使用 ORM 的属性、自增属性和主键属性。
if (ca.Independent || ca.Incremental || ca.PrimaryKey) excluded.Add(ca.Field);
}
// 准备参数。
var ps = structure.CreateParameters(record, Parameter, "_key");
var ps = structure.CreateParameters(record, Parameter, excluded);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";

2
Apewer/Apewer.props

@ -9,7 +9,7 @@
<Description></Description>
<RootNamespace>Apewer</RootNamespace>
<Product>Apewer Libraries</Product>
<Version>6.5.9</Version>
<Version>6.5.10</Version>
</PropertyGroup>
<!-- 生成 -->

8
Apewer/NumberUtility.cs

@ -297,6 +297,7 @@ namespace Apewer
/// <summary>获取布尔对象。</summary>
public static bool Boolean(object any)
{
if (any.IsNull()) return false;
if (any is bool _bool) return _bool;
if (any is byte _byte) return _byte == 1;
if (any is sbyte _sbyte) return _sbyte == 1;
@ -306,10 +307,13 @@ namespace Apewer
if (any is uint _uint) return _uint == 1;
if (any is long _long) return _long == 1;
if (any is ulong _ulong) return _ulong == 1;
if (any is float _float) return _float == 1;
if (any is double _double) return _double == 1;
if (any is decimal _decimal) return _decimal == 1;
if (any is string _string)
{
_string = TextUtility.Lower(_string);
if (_string == "true" || _string == "yes" || _string == "y") return true;
var lower = TextUtility.Lower(_string);
if (lower == "true" || lower == "yes" || lower == "y" || lower == "1") return true;
}
return false;
}

5
Apewer/ObjectSet.cs

@ -105,6 +105,11 @@ namespace Apewer
public partial class ObjectSet<T> : DynamicObject
{
public override IEnumerable<string> GetDynamicMemberNames()
{
return new List<string>(_origin.Keys).ToArray();
}
/// <summary></summary>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{

10
Apewer/RuntimeUtility.cs

@ -332,14 +332,20 @@ namespace Apewer
{
if (attributes == null) return false;
if (attributes.Length < 1) return false;
return true;
var type = typeof(T);
foreach (var attribute in attributes)
{
if (type.Equals(attribute.GetType())) return true;
}
return false;
}
/// <summary>判断指定类型具有特性。</summary>
public static bool Contains<T>(ICustomAttributeProvider model, bool inherit = false) where T : Attribute
{
if (model == null) return false;
return Contains<T>(model.GetCustomAttributes(typeof(T), inherit));
var attributes = model.GetCustomAttributes(inherit);
return Contains<T>(attributes);
}
/// <summary>判断基类。</summary>

40
Apewer/Source/ColumnAttribute.cs

@ -20,9 +20,12 @@ namespace Apewer.Source
private string _field = null;
private int _length = 0;
private ColumnType _type;
private bool _incremental = false;
private bool _primarykey = false;
private bool _independent = false;
private bool _valid = true;
private bool _nullable = false;
private void Init(string field, ColumnType type, int length)
{
@ -66,12 +69,21 @@ namespace Apewer.Source
/// <summary>此特性有效。</summary>
public bool Valid { get => _valid; }
/// <summary>Independent 特性。</summary>
/// <summary>此列不被管理,Insert、Update 和 Create Table 将忽略此列。</summary>
public bool Independent { get => _independent; }
/// <summary>使用此特性的属性。</summary>
public PropertyInfo Property { get => _property; }
/// <summary>属性对应的字段是主键。</summary>
public bool PrimaryKey { get => _primarykey; }
/// <summary>属性对应的字段是自动增长的整数。</summary>
public bool Incremental { get => _incremental; }
/// <summary>属性可为 NULL 值。</summary>
public bool Nullale { get => _nullable; }
#endregion
/// <summary>解析列特性。</summary>
@ -101,12 +113,20 @@ namespace Apewer.Source
if (getter == null || getter.IsStatic) return null;
if (setter == null || setter.IsStatic) return null;
// 检查主键特性和自增特性。
var pt = property.PropertyType;
if (!ca._incremental && RuntimeUtility.Contains<IncrementalAttribute>(property))
{
if (pt.Equals(typeof(int)) || pt.Equals(typeof(long))) ca._incremental = true;
}
if (!ca._primarykey && RuntimeUtility.Contains<PrimaryKeyAttribute>(property)) ca._primarykey = true;
// 检查列名称。
if (TextUtility.IsBlank(ca.Field)) ca._field = property.Name;
// 类型兼容。
var pt = property.PropertyType;
if (pt.Equals(typeof(byte[]).FullName)) ca._type = ColumnType.Bytes;
if (pt.Equals(typeof(byte[]))) ca._type = ColumnType.Bytes;
else if (pt.Equals(typeof(bool))) ca._type = ColumnType.Boolean;
else if (pt.Equals(typeof(Byte))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(SByte))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(Int16))) ca._type = ColumnType.Integer;
@ -132,6 +152,20 @@ namespace Apewer.Source
break;
}
}
#if !NET20
else if (pt.Equals(typeof(Nullable<Boolean>))) { ca._type = ColumnType.Boolean; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Byte>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<SByte>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Int16>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<UInt16>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Int32>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<UInt32>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Int64>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Single>))) { ca._type = ColumnType.Float; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Double>))) { ca._type = ColumnType.Float; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<Decimal>))) { ca._type = ColumnType.Float; ca._nullable = true; }
else if (pt.Equals(typeof(Nullable<DateTime>))) { ca._type = ColumnType.DateTime; ca._nullable = true; }
#endif
else
{
ca._type = ColumnType.NVarChar191;

7
Apewer/Source/ColumnType.cs

@ -20,7 +20,7 @@ namespace Apewer.Source
Bytes,
/// <summary>日期和时间类型(System.DateTime)。</summary>
DateTime,
DateTime,
/// <summary>可变长度的字符串(System.String),必须指定长度。</summary>
VarChar,
@ -44,7 +44,10 @@ namespace Apewer.Source
NVarCharMax,
/// <summary>可变长度的字符串(System.String)。</summary>
NText
NText,
/// <summary>布尔类型(System.Boolean)。</summary>
Boolean
}

12
Apewer/Source/IncrementalAttribute.cs

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>表示此属性在支持的数据库中是自增字段。</summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class IncrementalAttribute : Attribute { }
}

12
Apewer/Source/PrimaryKeyAttribute.cs

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>表示此属性对应的字段是主键。</summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class PrimaryKeyAttribute : Attribute { }
}

59
Apewer/Source/SourceUtility.cs

@ -164,6 +164,7 @@ namespace Apewer.Source
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(bool))) setter.Invoke(record, new object[] { Boolean(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) });
@ -176,7 +177,9 @@ namespace Apewer.Source
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) });
#if !NET20
else if (pt.Equals(typeof(Nullable<DateTime>))) setter.Invoke(record, new object[] { new Nullable<DateTime>((DateTime)value) });
else if (pt.Equals(typeof(Nullable<bool>))) setter.Invoke(record, new object[] { new Nullable<bool>(Boolean(value)) });
else if (pt.Equals(typeof(Nullable<byte>))) setter.Invoke(record, new object[] { new Nullable<byte>(Byte(value)) });
else if (pt.Equals(typeof(Nullable<sbyte>))) setter.Invoke(record, new object[] { new Nullable<sbyte>(SByte(value)) });
else if (pt.Equals(typeof(Nullable<short>))) setter.Invoke(record, new object[] { new Nullable<short>(Int16(value)) });
@ -188,6 +191,7 @@ namespace Apewer.Source
else if (pt.Equals(typeof(Nullable<float>))) setter.Invoke(record, new object[] { new Nullable<float>(Single(value)) });
else if (pt.Equals(typeof(Nullable<double>))) setter.Invoke(record, new object[] { new Nullable<double>(Double(value)) });
else if (pt.Equals(typeof(Nullable<decimal>))) setter.Invoke(record, new object[] { new Nullable<decimal>(Decimal(value)) });
#endif
else
{
@ -527,6 +531,61 @@ namespace Apewer.Source
#endregion
#region DataTable
/// <summary>将多个实体元素转换为 DataTable。</summary>
/// <typeparam name="T">实体元素的类型。</typeparam>
/// <param name="items">实体元素。</param>
/// <exception cref="ArgumentNullException"></exception>
public static DataTable DataTable<T>(this IEnumerable<T> items, string tableName = null)
{
if (items == null) throw new ArgumentNullException(nameof(items));
// 解析表结构。
var it = typeof(T);
var ts = TableStructure.Parse(it);
var cas = ts.Columns;
var width = cas.Length;
// 初始化列。
var table = new DataTable();
var gs = new MethodInfo[width];
var pts = new Type[width];
for (var i = 0; i < width; i++)
{
var ca = cas[i];
var p = ca.Property;
var pt = p.PropertyType;
var g = p.GetGetMethod();
if (g != null)
{
gs[i] = g;
pts[i] = pt;
table.Columns.Add(ca.Field, pt);
}
}
// 添加行。
foreach (var item in items)
{
if (item == null) continue;
var values = new ArrayBuilder<object>(width);
for (var i = 0; i < width; i++)
{
if (gs[i] == null) continue;
var v = gs[i].Invoke(item, null);
values.Add(v);
}
table.Rows.Add(values.Export());
}
if (tableName.NotEmpty()) table.TableName = tableName;
return table;
}
#endregion
}
}

2
Apewer/Source/TableStructure.cs

@ -221,7 +221,7 @@ namespace Apewer.Source
}
/// <summary>生成 IDataParameter 列表,用于 Insert 和 Update 方法。</summary>
public IDataParameter[] CreateParameters(object record, Func<Parameter, IDataParameter> callback, params string[] excludeds)
public IDataParameter[] CreateParameters(object record, Func<Parameter, IDataParameter> callback, IEnumerable<string> excludeds)
{
if (record == null || callback == null) return null;

8
ChangeLog.md

@ -1,6 +1,14 @@

### 最新提交
### 6.5.10
- Source:新增 Incremental 特性,用于标记自动增长字段;
- Source:新增 PrimaryKey 特性,用于标记主键字段;
- Source:添加 IEnumerable<T>.DataTable() 扩展方法;
- Source:SqlClient 支持 Bit 类型;
- Source:SqlClient 新增 BulkCopy 方法;
- ObjectSet:实现 GetDynamicMemberNames 的重写。
### 6.5.9
- Source:修正 Query<T> 填充无特性类型时的报错;
- Source:新增 Query.ToArray 方法,支持 dynamic。

Loading…
Cancel
Save