This commit is contained in:
zwl 2025-04-16 11:03:37 +08:00
commit 876a7f38e8
32 changed files with 339 additions and 159 deletions

View File

@ -38,9 +38,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="IBM.Data.DB2.Core" Version="3.1.0.600" /> <PackageReference Include="IBM.Data.DB2.Core" Version="3.1.0.600" />
<PackageReference Include="MySql.Data" Version="9.1.0" /> <PackageReference Include="MySql.Data" Version="9.1.0" />
<PackageReference Include="NewLife.Redis" Version="6.0.2025.101" /> <PackageReference Include="NewLife.Redis" Version="6.1.2025.301" />
<PackageReference Include="NewLife.Remoting" Version="3.2.2025.101" /> <PackageReference Include="NewLife.Remoting" Version="3.3.2025.301" />
<PackageReference Include="NewLife.Stardust" Version="3.2.2025.101" /> <PackageReference Include="NewLife.Stardust" Version="3.3.2025.301" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.7.0" /> <PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.7.0" />
<PackageReference Include="SapHana.DotNetCore.Data.Provider" Version="2.11.14" /> <PackageReference Include="SapHana.DotNetCore.Data.Provider" Version="2.11.14" />
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" /> <PackageReference Include="System.Data.SqlClient" Version="4.9.0" />

View File

@ -253,7 +253,7 @@ public class TableItem
} }
// 检查索引重复,最左原则 // 检查索引重复,最左原则
for (var i = 0; i < table.Indexes.Count; i++) for (var i = 0; i < table.Indexes.Count && XCodeSetting.Current.CheckDuplicateIndex; i++)
{ {
var di = table.Indexes[i]; var di = table.Indexes[i];
for (var j = i + 1; j < table.Indexes.Count; j++) for (var j = i + 1; j < table.Indexes.Count; j++)

View File

@ -70,7 +70,9 @@ public partial class DAL
if (db is DbBase dbBase) dbBase.Tracer = Tracer; if (db is DbBase dbBase) dbBase.Tracer = Tracer;
// 设置连接字符串时,可能触发内部的一系列动作,因此放在最后 // 设置连接字符串时,可能触发内部的一系列动作,因此放在最后
if (!ConnStr.IsNullOrEmpty()) db.ConnectionString = DecodeConnStr(ConnStr); var connStr = ConnStr;
if (!_mapConnStr.IsNullOrEmpty()) connStr = _mapConnStr;
if (!connStr.IsNullOrEmpty()) db.ConnectionString = DecodeConnStr(connStr);
_Db = db; _Db = db;
@ -86,6 +88,7 @@ public partial class DAL
public IAsyncDbSession AsyncSession => (Db as DbBase)!.CreateSessionForAsync(); public IAsyncDbSession AsyncSession => (Db as DbBase)!.CreateSessionForAsync();
private String? _mapTo; private String? _mapTo;
private String? _mapConnStr;
private static ICache _cache = new MemoryCache(); private static ICache _cache = new MemoryCache();
#endregion #endregion
@ -122,12 +125,31 @@ public partial class DAL
AddConnStr(connName, connstr, null, "SQLite"); AddConnStr(connName, connstr, null, "SQLite");
} }
ConnStr = css[connName]; var connStr = ConnStr = css[connName];
if (ConnStr.IsNullOrEmpty()) throw new XCodeException("请在使用数据库前设置[" + connName + "]连接字符串"); if (connStr.IsNullOrEmpty()) throw new XCodeException("请在使用数据库前设置[" + connName + "]连接字符串");
// 连接映射 // 连接映射
var vs = ConnStr.SplitAsDictionary("=", ",", true); var vs = connStr.SplitAsDictionary("=", ";", true);
if (vs.TryGetValue("MapTo", out var map) && !map.IsNullOrEmpty()) _mapTo = map; if (vs.TryGetValue("MapTo", out var map) && !map.IsNullOrEmpty() && !map.EqualIgnoreCase(connName))
{
// 如果连接字符串除了MapTo还有其它设置则需要复制后覆盖
if (vs.Count <= 1)
_mapTo = map;
else
{
var dal = Create(map);
var builder = new ConnectionStringBuilder(dal.ConnStr);
foreach (var item in vs)
{
if (!item.Key.EqualIgnoreCase("MapTo"))
builder[item.Key] = item.Value;
}
_mapConnStr = builder.ToString();
ProviderType = dal.ProviderType;
DbType = dal.DbType;
}
}
if (_infos!.TryGetValue(connName, out var t) && t.Type != null) if (_infos!.TryGetValue(connName, out var t) && t.Type != null)
{ {
@ -620,7 +642,7 @@ public partial class DAL
//CheckDatabase(); //CheckDatabase();
var tables = Db.CreateMetaData().GetTables(); var tables = Db.CreateMetaData().GetTables();
if (span != null) span.Tag += ": " + tables.Join(","); span?.AppendTag(tables.Join(","), tables.Count);
return tables; return tables;
} }

View File

@ -4,6 +4,7 @@ using System.Net;
using NewLife.Collections; using NewLife.Collections;
using NewLife.Data; using NewLife.Data;
using NewLife.Log; using NewLife.Log;
using NewLife.Web;
namespace XCode.DataAccessLayer; namespace XCode.DataAccessLayer;
@ -18,8 +19,14 @@ internal class MySql : RemoteDb
/// <returns></returns> /// <returns></returns>
protected override DbProviderFactory? CreateFactory() protected override DbProviderFactory? CreateFactory()
{ {
//_Factory = GetProviderFactory("NewLife.MySql.dll", "NewLife.MySql.MySqlClientFactory") ?? var type = PluginHelper.LoadPlugin("NewLife.MySql.MySqlClientFactory", null, "NewLife.MySql.dll", null);
// GetProviderFactory("MySql.Data.dll", "MySql.Data.MySqlClient.MySqlClientFactory"); var factory = GetProviderFactory(type);
if (factory != null) return factory;
type = PluginHelper.LoadPlugin("MySql.Data.MySqlClient.MySqlClientFactory", null, "MySql.Data.dll", null);
factory = GetProviderFactory(type);
if (factory != null) return factory;
// MewLife.MySql 在开发过程中,数据驱动下载站点没有它的包,暂时不支持下载 // MewLife.MySql 在开发过程中,数据驱动下载站点没有它的包,暂时不支持下载
return GetProviderFactory("NewLife.MySql", "NewLife.MySql.dll", "NewLife.MySql.MySqlClientFactory", true, true) ?? return GetProviderFactory("NewLife.MySql", "NewLife.MySql.dll", "NewLife.MySql.MySqlClientFactory", true, true) ??
GetProviderFactory(null, "MySql.Data.dll", "MySql.Data.MySqlClient.MySqlClientFactory"); GetProviderFactory(null, "MySql.Data.dll", "MySql.Data.MySqlClient.MySqlClientFactory");

View File

@ -847,7 +847,7 @@ internal class SQLiteMetaData : FileDbMetaData
/// <returns></returns> /// <returns></returns>
public override String DropIndexSQL(IDataIndex index) => $"Drop Index {index.Name}"; public override String DropIndexSQL(IDataIndex index) => $"Drop Index {index.Name}";
protected override String CheckColumnsChange(IDataTable entitytable, IDataTable dbtable, Boolean onlySql, Boolean noDelete) protected override String CheckColumnsChange(IDataTable entitytable, IDataTable dbtable, Boolean onlySql, Boolean noDelete, XCodeSetting set)
{ {
foreach (var item in entitytable.Columns) foreach (var item in entitytable.Columns)
{ {
@ -864,7 +864,7 @@ internal class SQLiteMetaData : FileDbMetaData
} }
// 把onlySql设为true让基类只产生语句而不执行 // 把onlySql设为true让基类只产生语句而不执行
var sql = base.CheckColumnsChange(entitytable, dbtable, onlySql, true); var sql = base.CheckColumnsChange(entitytable, dbtable, onlySql, true, set);
if (sql.IsNullOrEmpty()) return sql; if (sql.IsNullOrEmpty()) return sql;
// SQLite 3.35.0 起支持 Drop Column // SQLite 3.35.0 起支持 Drop Column
@ -879,7 +879,7 @@ internal class SQLiteMetaData : FileDbMetaData
Database.CreateSession().Execute(sql); Database.CreateSession().Execute(sql);
return null; return "";
} }
if (sql.Contains("Drop Column") && v != null && v >= new Version(3, 35)) if (sql.Contains("Drop Column") && v != null && v >= new Version(3, 35))
{ {
@ -887,11 +887,11 @@ internal class SQLiteMetaData : FileDbMetaData
Database.CreateSession().Execute(sql); Database.CreateSession().Execute(sql);
return null; return "";
} }
var db = Database as DbBase; var db = Database as DbBase;
using var span = db.Tracer?.NewSpan($"db:{db.ConnName}:SetSchema:RebuildTable", sql); using var span = db!.Tracer?.NewSpan($"db:{db.ConnName}:SetSchema:RebuildTable", sql);
var sql2 = sql; var sql2 = sql;
@ -941,7 +941,7 @@ internal class SQLiteMetaData : FileDbMetaData
throw; throw;
} }
return null; return "";
} }
#endregion #endregion
@ -965,16 +965,17 @@ internal class SQLiteMetaData : FileDbMetaData
/// <param name="entitytable"></param> /// <param name="entitytable"></param>
/// <param name="dbtable"></param> /// <param name="dbtable"></param>
/// <param name="mode"></param> /// <param name="mode"></param>
protected override void CheckTable(IDataTable entitytable, IDataTable dbtable, Migration mode) /// <param name="set"></param>
protected override void CheckTable(IDataTable entitytable, IDataTable? dbtable, Migration mode, XCodeSetting set)
{ {
if (dbtable == null && (Database as SQLite).IsMemoryDatabase) if (dbtable == null && (Database as SQLite)!.IsMemoryDatabase)
{ {
if (memoryTables.Any(t => t.TableName.EqualIgnoreCase(entitytable.TableName))) return; if (memoryTables.Any(t => t.TableName.EqualIgnoreCase(entitytable.TableName))) return;
memoryTables.Add(entitytable); memoryTables.Add(entitytable);
} }
base.CheckTable(entitytable, dbtable, mode); base.CheckTable(entitytable, dbtable, mode, set);
} }
protected override Boolean PerformSchema(StringBuilder sb, Boolean onlySql, DDLSchema schema, params Object[] values) protected override Boolean PerformSchema(StringBuilder sb, Boolean onlySql, DDLSchema schema, params Object[] values)

View File

@ -69,14 +69,16 @@ internal partial class DbMetaData
{ {
if (mode == Migration.Off) return; if (mode == Migration.Off) return;
OnSetTables(tables, mode); var set = XCodeSetting.Current;
OnSetTables(tables, mode, set);
} }
protected virtual void OnSetTables(IDataTable[] tables, Migration mode) protected virtual void OnSetTables(IDataTable[] tables, Migration mode, XCodeSetting set)
{ {
var dbExist = CheckDatabase(mode); var dbExist = CheckDatabase(mode);
CheckAllTables(tables, mode, dbExist); CheckAllTables(tables, mode, dbExist, set);
} }
private Boolean? hasCheckedDatabase; private Boolean? hasCheckedDatabase;
@ -119,7 +121,7 @@ internal partial class DbMetaData
return dbExist; return dbExist;
} }
private void CheckAllTables(IDataTable[] tables, Migration mode, Boolean dbExit) private void CheckAllTables(IDataTable[] tables, Migration mode, Boolean dbExit, XCodeSetting set)
{ {
IList<IDataTable>? dbtables = null; IList<IDataTable>? dbtables = null;
if (dbExit) if (dbExit)
@ -141,9 +143,9 @@ internal partial class DbMetaData
// 判断指定表是否存在于数据库中,以决定是创建表还是修改表 // 判断指定表是否存在于数据库中,以决定是创建表还是修改表
if (dbtable != null) if (dbtable != null)
CheckTable(item, dbtable, mode); CheckTable(item, dbtable, mode, set);
else else
CheckTable(item, null, mode); CheckTable(item, null, mode, set);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -152,7 +154,7 @@ internal partial class DbMetaData
} }
} }
protected virtual void CheckTable(IDataTable entitytable, IDataTable? dbtable, Migration mode) protected virtual void CheckTable(IDataTable entitytable, IDataTable? dbtable, Migration mode, XCodeSetting set)
{ {
var @readonly = mode <= Migration.ReadOnly; var @readonly = mode <= Migration.ReadOnly;
if (dbtable == null) if (dbtable == null)
@ -174,19 +176,30 @@ internal partial class DbMetaData
var onlyCreate = mode < Migration.Full; var onlyCreate = mode < Migration.Full;
var sb = new StringBuilder(); var sb = new StringBuilder();
var sql = CheckTableDescription(entitytable, dbtable, mode); if (set.CheckComment)
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); {
var sql = CheckTableDescription(entitytable, dbtable, mode);
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql);
}
// 先删除索引,后面才有可能删除字段 if (set.CheckDeleteIndex)
sql = CheckDeleteIndex(entitytable, dbtable, mode); {
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); // 先删除索引,后面才有可能删除字段
var sql = CheckDeleteIndex(entitytable, dbtable, mode);
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql);
}
sql = CheckColumnsChange(entitytable, dbtable, @readonly, onlyCreate); {
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); var sql = CheckColumnsChange(entitytable, dbtable, @readonly, onlyCreate, set);
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql);
}
// 新增字段后,可能需要删除索引 if (set.CheckAddIndex)
sql = CheckAddIndex(entitytable, dbtable, mode); {
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); // 新增字段后,可能需要删除索引
var sql = CheckAddIndex(entitytable, dbtable, mode);
if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql);
}
if (sb.Length > 0) WriteLog($"DDL模式[{mode}],请手工修改表[{dbtable.TableName}]{Environment.NewLine}{sb}"); if (sb.Length > 0) WriteLog($"DDL模式[{mode}],请手工修改表[{dbtable.TableName}]{Environment.NewLine}{sb}");
} }
@ -197,8 +210,9 @@ internal partial class DbMetaData
/// <param name="dbtable"></param> /// <param name="dbtable"></param>
/// <param name="readonly"></param> /// <param name="readonly"></param>
/// <param name="onlyCreate"></param> /// <param name="onlyCreate"></param>
/// <param name="set"></param>
/// <returns>返回未执行语句</returns> /// <returns>返回未执行语句</returns>
protected virtual String CheckColumnsChange(IDataTable entitytable, IDataTable dbtable, Boolean @readonly, Boolean onlyCreate) protected virtual String CheckColumnsChange(IDataTable entitytable, IDataTable dbtable, Boolean @readonly, Boolean onlyCreate, XCodeSetting set)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
var etdic = entitytable.Columns.ToDictionary(e => FormatName(e), e => e, StringComparer.OrdinalIgnoreCase); var etdic = entitytable.Columns.ToDictionary(e => FormatName(e), e => e, StringComparer.OrdinalIgnoreCase);
@ -279,7 +293,7 @@ internal partial class DbMetaData
PerformSchema(sb, @readonly || onlyCreate, DDLSchema.AlterColumn, item, dbf); PerformSchema(sb, @readonly || onlyCreate, DDLSchema.AlterColumn, item, dbf);
//if (item.Description + "" != dbf.Description + "") //if (item.Description + "" != dbf.Description + "")
if (FormatDescription(item.Description) != FormatDescription(dbf.Description)) if (set.CheckComment && FormatDescription(item.Description) != FormatDescription(dbf.Description))
{ {
// 先删除旧注释 // 先删除旧注释
//if (dbf.Description != null) PerformSchema(sb, noDelete, DDLSchema.DropColumnDescription, dbf); //if (dbf.Description != null) PerformSchema(sb, noDelete, DDLSchema.DropColumnDescription, dbf);

View File

@ -6,9 +6,9 @@ using System.Runtime.Serialization;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using NewLife;
using NewLife.Log; using NewLife.Log;
using NewLife.Reflection; using NewLife.Reflection;
using XCode.Code;
namespace XCode.DataAccessLayer; namespace XCode.DataAccessLayer;
@ -231,14 +231,15 @@ public static class ModelHelper
writer.WriteEndElement(); writer.WriteEndElement();
} }
var nameFormat = option is EntityBuilderOption opt ? opt.NameFormat : NameFormats.Default;
{ {
writer.WriteStartElement("Tables"); writer.WriteStartElement("Tables");
// 回写xml模型,排除IsHistory=true的表单仅仅保留原始表单 // 回写xml模型,排除IsHistory=true的表单仅仅保留原始表单
foreach (var item in tables.Where(x => x is not XTable xt || !xt.IsHistory)) foreach (var table in tables.Where(x => x is not XTable xt || !xt.IsHistory))
{ {
writer.WriteStartElement("Table"); writer.WriteStartElement("Table");
(item as IXmlSerializable)!.WriteXml(writer); table.WriteXml(writer, nameFormat);
writer.WriteEndElement(); writer.WriteEndElement();
} }
@ -312,10 +313,10 @@ public static class ModelHelper
{ {
if (option != null) if (option != null)
{ {
if (option is IXmlSerializable xml2) //if (option is IXmlSerializable xml2)
xml2.ReadXml(reader); // xml2.ReadXml(reader);
else //else
ReadXml(reader, option); ReadXml(reader, option);
} }
else else
{ {
@ -345,7 +346,7 @@ public static class ModelHelper
static void ReadTable(XmlReader reader, Func<IDataTable> createTable, IList<IDataTable> list) static void ReadTable(XmlReader reader, Func<IDataTable> createTable, IList<IDataTable> list)
{ {
var table = createTable(); var table = createTable();
(table as IXmlSerializable)!.ReadXml(reader); table.ReadXml(reader);
// 判断是否存在属性NeedHistory设置且为true // 判断是否存在属性NeedHistory设置且为true
var needHistory = table.Properties.FirstOrDefault(x => x.Key.EqualIgnoreCase("NeedHistory")); var needHistory = table.Properties.FirstOrDefault(x => x.Key.EqualIgnoreCase("NeedHistory"));
@ -433,7 +434,9 @@ public static class ModelHelper
// 清空默认的原始类型让其从xml读取 // 清空默认的原始类型让其从xml读取
dc.RawType = null; dc.RawType = null;
} }
(dc as IXmlSerializable)!.ReadXml(reader); ReadXmlAttribute(reader, dc);
// 跳过当前节点
reader.Skip();
// 未指定DataType但指定了Type修正为枚举整型 // 未指定DataType但指定了Type修正为枚举整型
if (dc.DataType == null && dc.Properties.ContainsKey("Type")) dc.DataType = typeof(Int32); if (dc.DataType == null && dc.Properties.ContainsKey("Type")) dc.DataType = typeof(Int32);
@ -454,7 +457,10 @@ public static class ModelHelper
while (reader.IsStartElement()) while (reader.IsStartElement())
{ {
var di = table.CreateIndex(); var di = table.CreateIndex();
(di as IXmlSerializable)!.ReadXml(reader); ReadXmlAttribute(reader, di);
// 跳过当前节点
reader.Skip();
di.Fix(); di.Fix();
table.Indexes.Add(di); table.Indexes.Add(di);
} }
@ -483,18 +489,21 @@ public static class ModelHelper
/// <summary>写入</summary> /// <summary>写入</summary>
/// <param name="table"></param> /// <param name="table"></param>
/// <param name="writer"></param> /// <param name="writer"></param>
public static IDataTable WriteXml(this IDataTable table, XmlWriter writer) /// <param name="nameFormat"></param>
public static IDataTable WriteXml(this IDataTable table, XmlWriter writer, NameFormats nameFormat = NameFormats.Default)
{ {
WriteXml(writer, table); var ignoreNameCase = nameFormat <= NameFormats.Default;
WriteXml(writer, table, false, ignoreNameCase);
// 写字段 // 写字段
if (table.Columns.Count > 0) if (table.Columns.Count > 0)
{ {
writer.WriteStartElement("Columns"); writer.WriteStartElement("Columns");
foreach (IXmlSerializable item in table.Columns) foreach (var dc in table.Columns)
{ {
writer.WriteStartElement("Column"); writer.WriteStartElement("Column");
item.WriteXml(writer); WriteXml(writer, dc, false, ignoreNameCase);
writer.WriteEndElement(); writer.WriteEndElement();
} }
writer.WriteEndElement(); writer.WriteEndElement();
@ -502,10 +511,10 @@ public static class ModelHelper
if (table.Indexes.Count > 0) if (table.Indexes.Count > 0)
{ {
writer.WriteStartElement("Indexes"); writer.WriteStartElement("Indexes");
foreach (IXmlSerializable item in table.Indexes) foreach (var di in table.Indexes)
{ {
writer.WriteStartElement("Index"); writer.WriteStartElement("Index");
item.WriteXml(writer); WriteXml(writer, di, false, true);
writer.WriteEndElement(); writer.WriteEndElement();
} }
writer.WriteEndElement(); writer.WriteEndElement();
@ -634,7 +643,8 @@ public static class ModelHelper
/// <param name="writer"></param> /// <param name="writer"></param>
/// <param name="value">数值</param> /// <param name="value">数值</param>
/// <param name="writeDefaultValueMember">是否写数值为默认值的成员。为了节省空间,默认不写。</param> /// <param name="writeDefaultValueMember">是否写数值为默认值的成员。为了节省空间,默认不写。</param>
public static void WriteXml(XmlWriter writer, Object value, Boolean writeDefaultValueMember = false) /// <param name="ignoreNameCase">忽略名称大小写</param>
public static void WriteXml(XmlWriter writer, Object value, Boolean writeDefaultValueMember = false, Boolean ignoreNameCase = true)
{ {
var type = value.GetType(); var type = value.GetType();
var def = GetDefault(type); var def = GetDefault(type);
@ -681,16 +691,24 @@ public static class ModelHelper
if (code == TypeCode.String && "" + obj == "" + dobj) continue; if (code == TypeCode.String && "" + obj == "" + dobj) continue;
} }
if (code == TypeCode.String) if (code == TypeCode.String && obj is String str)
{ {
// 如果别名与名称相同,则跳过,不区分大小写 // 如果别名与名称相同,则跳过,不区分大小写
// 改为区分大小写避免linux环境下 mysql 数据库存在 // 改为区分大小写避免linux环境下 mysql 数据库存在
if (pi.Name == "Name") if (pi.Name == "Name")
name = (String)obj; name = str;
else if (pi.Name is "TableName" or "ColumnName") else if (pi.Name is "TableName" or "ColumnName")
{ {
if (name == (String)obj) continue; if (name == str) continue;
if (/*ignoreNameCase &&*/ name.EqualIgnoreCase((String)obj)) continue; if (ignoreNameCase)
{
if (name.EqualIgnoreCase(str)) continue;
}
else
{
// 如果全小写或者全大写,也不缺分大小写比较
if ((str == str.ToLower() || str == str.ToUpper()) && name.EqualIgnoreCase(str)) continue;
}
} }
} }
else if (code == TypeCode.Object) else if (code == TypeCode.Object)
@ -723,9 +741,8 @@ public static class ModelHelper
if (obj != null) writer.WriteAttributeString(pi.Name, obj + ""); if (obj != null) writer.WriteAttributeString(pi.Name, obj + "");
} }
if (value is IDataTable) if (value is IDataTable table)
{ {
var table = value as IDataTable;
// 写入扩展属性作为特性 // 写入扩展属性作为特性
if (table.Properties.Count > 0) if (table.Properties.Count > 0)
{ {
@ -735,9 +752,8 @@ public static class ModelHelper
} }
} }
} }
else if (value is IDataColumn) else if (value is IDataColumn column)
{ {
var column = value as IDataColumn;
// 写入扩展属性作为特性 // 写入扩展属性作为特性
if (column.Properties.Count > 0) if (column.Properties.Count > 0)
{ {

View File

@ -2,7 +2,6 @@
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using NewLife;
using NewLife.Collections; using NewLife.Collections;
namespace XCode.DataAccessLayer; namespace XCode.DataAccessLayer;
@ -12,7 +11,7 @@ namespace XCode.DataAccessLayer;
[DisplayName("字段模型")] [DisplayName("字段模型")]
[Description("字段模型")] [Description("字段模型")]
[XmlRoot("Column")] [XmlRoot("Column")]
class XField : SerializableDataMember, IDataColumn, ICloneable class XField : /*SerializableDataMember,*/ IDataColumn, ICloneable
{ {
#region #region
/// <summary>名称</summary> /// <summary>名称</summary>

View File

@ -1,8 +1,6 @@
using System; using System.ComponentModel;
using System.ComponentModel;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Xml.Serialization; using System.Xml.Serialization;
using NewLife;
namespace XCode.DataAccessLayer; namespace XCode.DataAccessLayer;
@ -11,7 +9,7 @@ namespace XCode.DataAccessLayer;
[DisplayName("索引模型")] [DisplayName("索引模型")]
[Description("索引模型")] [Description("索引模型")]
[XmlRoot("Index")] [XmlRoot("Index")]
class XIndex : SerializableDataMember, IDataIndex, ICloneable class XIndex : /*SerializableDataMember,*/ IDataIndex, ICloneable
{ {
#region #region
/// <summary>名称</summary> /// <summary>名称</summary>

View File

@ -16,7 +16,7 @@ namespace XCode.DataAccessLayer;
[DisplayName("表模型")] [DisplayName("表模型")]
[Description("表模型")] [Description("表模型")]
[XmlRoot("Table")] [XmlRoot("Table")]
class XTable : IDataTable, ICloneable, IXmlSerializable class XTable : IDataTable, ICloneable//, IXmlSerializable
{ {
#region #region
///// <summary>编号</summary> ///// <summary>编号</summary>
@ -243,16 +243,16 @@ class XTable : IDataTable, ICloneable, IXmlSerializable
#endregion #endregion
#region IXmlSerializable #region IXmlSerializable
/// <summary>获取架构</summary> ///// <summary>获取架构</summary>
/// <returns></returns> ///// <returns></returns>
XmlSchema IXmlSerializable.GetSchema() => null; //XmlSchema IXmlSerializable.GetSchema() => null;
/// <summary>读取</summary> ///// <summary>读取</summary>
/// <param name="reader"></param> ///// <param name="reader"></param>
void IXmlSerializable.ReadXml(XmlReader reader) => ModelHelper.ReadXml(this, reader); //void IXmlSerializable.ReadXml(XmlReader reader) => ModelHelper.ReadXml(this, reader);
/// <summary>写入</summary> ///// <summary>写入</summary>
/// <param name="writer"></param> ///// <param name="writer"></param>
void IXmlSerializable.WriteXml(XmlWriter writer) => ModelHelper.WriteXml(this, writer); //void IXmlSerializable.WriteXml(XmlWriter writer) => ModelHelper.WriteXml(this, writer);
#endregion #endregion
} }

View File

@ -71,7 +71,7 @@
<Column Name="Ex3" DataType="Double" Description="扩展3" Category="扩展" /> <Column Name="Ex3" DataType="Double" Description="扩展3" Category="扩展" />
<Column Name="Ex4" DataType="String" Description="扩展4" Category="扩展" /> <Column Name="Ex4" DataType="String" Description="扩展4" Category="扩展" />
<Column Name="Ex5" DataType="String" Description="扩展5" Category="扩展" /> <Column Name="Ex5" DataType="String" Description="扩展5" Category="扩展" />
<Column Name="Ex6" DataType="String" Description="扩展6" Attribute="XmlIgnore, ScriptIgnore, IgnoreDataMember" Category="扩展" /> <Column Name="Ex6" DataType="String" Description="扩展6" Attribute="XmlIgnore, IgnoreDataMember, ScriptIgnore" Category="扩展" />
<Column Name="UpdateUser" DataType="String" Nullable="False" DefaultValue="''" Description="更新者" Model="False" Category="扩展" /> <Column Name="UpdateUser" DataType="String" Nullable="False" DefaultValue="''" Description="更新者" Model="False" Category="扩展" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新用户" Model="False" Category="扩展" /> <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" Model="False" Category="扩展" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" /> <Column Name="UpdateIP" DataType="String" Description="更新地址" Model="False" Category="扩展" />

Binary file not shown.

View File

@ -1,6 +1,7 @@
using System.IO.Compression; using System.IO.Compression;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Runtime.Serialization;
using System.Text; using System.Text;
using System.Web.Script.Serialization; using System.Web.Script.Serialization;
using System.Xml.Serialization; using System.Xml.Serialization;
@ -105,16 +106,21 @@ public partial class Area : Entity<Area>
#region #region
/// <summary>顶级根。它的Childs就是各个省份</summary> /// <summary>顶级根。它的Childs就是各个省份</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public static Area Root { get; } = new Area(); public static Area Root { get; } = new Area();
/// <summary>父级</summary> /// <summary>父级</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Area Parent => Extends.Get(nameof(Parent), k => FindByID(ParentID) ?? Root)!; public Area Parent => Extends.Get(nameof(Parent), k => FindByID(ParentID) ?? Root)!;
///// <summary>所有父级,从高到底</summary>
//[XmlIgnore, IgnoreDataMember, ScriptIgnore]
//[Obsolete("=>GetAllParents")]
//public IList<Area> AllParents => Extends.Get(nameof(AllParents), k => GetAllParents())!;
/// <summary>所有父级,从高到底</summary> /// <summary>所有父级,从高到底</summary>
[XmlIgnore, ScriptIgnore] /// <returns></returns>
public IList<Area> AllParents => Extends.Get(nameof(AllParents), k => public IList<Area> GetAllParents()
{ {
var list = new List<Area>(); var list = new List<Area>();
var entity = Parent; var entity = Parent;
@ -131,14 +137,14 @@ public partial class Area : Entity<Area>
list.Reverse(); list.Reverse();
return list; return list;
})!; }
/// <summary>父级路径</summary> /// <summary>父级路径</summary>
public String ParentPath public String? ParentPath
{ {
get get
{ {
var list = AllParents; var list = GetAllParents();
if (list != null && list.Count > 0) return list.Where(r => !r.IsVirtual).Join("/", r => r.Name); if (list != null && list.Count > 0) return list.Where(r => !r.IsVirtual).Join("/", r => r.Name);
return Parent?.Name; return Parent?.Name;
@ -159,21 +165,25 @@ public partial class Area : Entity<Area>
} }
/// <summary>下级地区</summary> /// <summary>下级地区</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<Area> Childs => Extends.Get(nameof(Childs), k => FindAllByParentID(ID).Where(e => e.Enable).ToList())!; public IList<Area> Childs => Extends.Get(nameof(Childs), k => FindAllByParentID(ID).Where(e => e.Enable).ToList())!;
///// <summary>子孙级区域。支持省市区,不支持乡镇街道</summary>
//[XmlIgnore, IgnoreDataMember, ScriptIgnore]
//[Obsolete("=>GetAllChilds")]
//public IList<Area> AllChilds => Extends.Get(nameof(AllChilds), k => GetAllChilds())!;
/// <summary>子孙级区域。支持省市区,不支持乡镇街道</summary> /// <summary>子孙级区域。支持省市区,不支持乡镇街道</summary>
[XmlIgnore, ScriptIgnore] public IList<Area> GetAllChilds()
public IList<Area> AllChilds => Extends.Get(nameof(AllChilds), k =>
{ {
var list = new List<Area>(); var list = new List<Area>();
foreach (var item in Childs) foreach (var item in Childs)
{ {
list.Add(item); list.Add(item);
if (item.Level < 3) list.AddRange(item.AllChilds); if (item.Level < 3) list.AddRange(item.GetAllChilds());
} }
return list; return list;
}); }
/// <summary>是否虚拟地区</summary> /// <summary>是否虚拟地区</summary>
public Boolean IsVirtual => Name.EqualIgnoreCase("市辖区", "直辖县", "直辖镇"); public Boolean IsVirtual => Name.EqualIgnoreCase("市辖区", "直辖县", "直辖镇");
@ -507,7 +517,7 @@ public partial class Area : Entity<Area>
var r = FindByNames(ss); var r = FindByNames(ss);
if (r != null) if (r != null)
{ {
list.AddRange(r.AllParents); list.AddRange(r.GetAllParents());
list.Add(r); list.Add(r);
if (maxLevel > 0 && list.Count > maxLevel) list = list.Take(maxLevel).ToList(); if (maxLevel > 0 && list.Count > maxLevel) list = list.Take(maxLevel).ToList();
@ -538,7 +548,7 @@ public partial class Area : Entity<Area>
var r = FindAddress(Root, address, maxLevel); var r = FindAddress(Root, address, maxLevel);
if (r != null) if (r != null)
{ {
list.AddRange(r.AllParents); list.AddRange(r.GetAllParents());
list.Add(r); list.Add(r);
} }

View File

@ -122,10 +122,10 @@ public partial class User : LogEntity<User>, IUser, IAuthUser, IIdentity
#region #region
/// <summary>物理地址</summary> /// <summary>物理地址</summary>
[DisplayName("物理地址")] [DisplayName("物理地址")]
public String LastLoginAddress => LastLoginIP.IPToAddress(); public String? LastLoginAddress => LastLoginIP?.IPToAddress();
///// <summary>部门</summary> ///// <summary>部门</summary>
//[XmlIgnore, ScriptIgnore, IgnoreDataMember] //[XmlIgnore, IgnoreDataMember, ScriptIgnore]
//public Department Department => Extends.Get(nameof(Department), k => Department.FindByID(DepartmentID)); //public Department Department => Extends.Get(nameof(Department), k => Department.FindByID(DepartmentID));
///// <summary>部门</summary> ///// <summary>部门</summary>
@ -137,7 +137,7 @@ public partial class User : LogEntity<User>, IUser, IAuthUser, IIdentity
/// 地区名 /// 地区名
/// </summary> /// </summary>
[Map(nameof(AreaId))] [Map(nameof(AreaId))]
public String AreaName => Area.FindByID(AreaId)?.Path; public String? AreaName => Area.FindByID(AreaId)?.Path;
#endregion #endregion
#region #region
@ -602,11 +602,11 @@ public partial class User : LogEntity<User>, IUser, IAuthUser, IIdentity
#region #region
/// <summary>角色</summary> /// <summary>角色</summary>
/// <remarks>扩展属性不缓存空对象,一般来说,每个管理员都有对应的角色,如果没有,可能是在初始化</remarks> /// <remarks>扩展属性不缓存空对象,一般来说,每个管理员都有对应的角色,如果没有,可能是在初始化</remarks>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
IRole IUser.Role => Role; IRole IUser.Role => Role;
/// <summary>角色集合</summary> /// <summary>角色集合</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual IRole[] Roles => Extends.Get(nameof(Roles), k => GetRoleIDs().Select(e => ManageProvider.Get<IRole>()?.FindByID(e)).Where(e => e != null).ToArray()); public virtual IRole[] Roles => Extends.Get(nameof(Roles), k => GetRoleIDs().Select(e => ManageProvider.Get<IRole>()?.FindByID(e)).Where(e => e != null).ToArray());
/// <summary>获取角色列表。主角色在前,其它角色升序在后</summary> /// <summary>获取角色列表。主角色在前,其它角色升序在后</summary>
@ -693,7 +693,7 @@ public partial interface IUser
IRole[] Roles { get; } IRole[] Roles { get; }
/// <summary>角色名</summary> /// <summary>角色名</summary>
String RoleName { get; } String? RoleName { get; }
/// <summary>用户是否拥有当前菜单的指定权限</summary> /// <summary>用户是否拥有当前菜单的指定权限</summary>
/// <param name="menu">指定菜单</param> /// <param name="menu">指定菜单</param>

View File

@ -286,7 +286,7 @@ public partial class User : IUser, IEntity<IUser>
private String? _Ex6; private String? _Ex6;
/// <summary>扩展6</summary> /// <summary>扩展6</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
[Category("扩展")] [Category("扩展")]
[DisplayName("扩展6")] [DisplayName("扩展6")]
[Description("扩展6")] [Description("扩展6")]

View File

@ -50,7 +50,7 @@ public partial class TenantUser : Entity<TenantUser>
#region #region
///// <summary>租户</summary> ///// <summary>租户</summary>
//[XmlIgnore, IgnoreDataMember] //[XmlIgnore, IgnoreDataMember, ScriptIgnore]
////[ScriptIgnore] ////[ScriptIgnore]
//public Tenant Tenant => Extends.Get(nameof(Tenant), k => Tenant.FindById(TenantId)); //public Tenant Tenant => Extends.Get(nameof(Tenant), k => Tenant.FindById(TenantId));
@ -59,7 +59,7 @@ public partial class TenantUser : Entity<TenantUser>
//public String TenantName => Tenant?.Name; //public String TenantName => Tenant?.Name;
///// <summary>用户</summary> ///// <summary>用户</summary>
//[XmlIgnore, IgnoreDataMember] //[XmlIgnore, IgnoreDataMember, ScriptIgnore]
////[ScriptIgnore] ////[ScriptIgnore]
//public User User => Extends.Get(nameof(User), k => User.FindByID(UserId)); //public User User => Extends.Get(nameof(User), k => User.FindByID(UserId));
@ -68,7 +68,7 @@ public partial class TenantUser : Entity<TenantUser>
//public String UserName => User?.Name; //public String UserName => User?.Name;
/// <summary>角色</summary> /// <summary>角色</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
IRole ITenantUser.Role => Role; IRole ITenantUser.Role => Role;
///// <summary>角色</summary> ///// <summary>角色</summary>
@ -76,7 +76,7 @@ public partial class TenantUser : Entity<TenantUser>
//public String RoleName => Role?.Name; //public String RoleName => Role?.Name;
/// <summary>角色集合</summary> /// <summary>角色集合</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual IRole[] Roles => Extends.Get(nameof(Roles), k => GetRoleIDs().Select(e => ManageProvider.Get<IRole>()?.FindByID(e)).Where(e => e != null).ToArray()); public virtual IRole[] Roles => Extends.Get(nameof(Roles), k => GetRoleIDs().Select(e => ManageProvider.Get<IRole>()?.FindByID(e)).Where(e => e != null).ToArray());
/// <summary>获取角色列表。主角色在前,其它角色升序在后</summary> /// <summary>获取角色列表。主角色在前,其它角色升序在后</summary>

View File

@ -265,7 +265,7 @@ public partial class Menu : EntityTree<Menu>, IMenu
#region #region
/// <summary>可选权限子项</summary> /// <summary>可选权限子项</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Dictionary<Int32, String> Permissions { get; set; } = new Dictionary<Int32, String>(); public Dictionary<Int32, String> Permissions { get; set; } = new Dictionary<Int32, String>();
private void LoadPermission() private void LoadPermission()

View File

@ -231,7 +231,7 @@ public partial class Role : LogEntity<Role>, IRole, ITenantSource
private IDictionary<Int32, PermissionFlags> _Permissions; private IDictionary<Int32, PermissionFlags> _Permissions;
/// <summary>本角色权限集合</summary> /// <summary>本角色权限集合</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IDictionary<Int32, PermissionFlags> Permissions => _Permissions ??= new Dictionary<Int32, PermissionFlags>(); public IDictionary<Int32, PermissionFlags> Permissions => _Permissions ??= new Dictionary<Int32, PermissionFlags>();
/// <summary>是否拥有指定资源的指定权限</summary> /// <summary>是否拥有指定资源的指定权限</summary>
@ -353,7 +353,7 @@ public partial class Role : LogEntity<Role>, IRole, ITenantSource
} }
/// <summary>当前角色拥有的资源</summary> /// <summary>当前角色拥有的资源</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Int32[] Resources => Permissions.Keys.ToArray(); public Int32[] Resources => Permissions.Keys.ToArray();
#endregion #endregion

View File

@ -95,7 +95,7 @@ public partial class Department : Entity<Department>, ITenantSource
#region #region
///// <summary>租户</summary> ///// <summary>租户</summary>
//[XmlIgnore, IgnoreDataMember] //[XmlIgnore, IgnoreDataMember, ScriptIgnore]
//public Tenant Tenant => Extends.Get(nameof(Tenant), k => Tenant.FindById(TenantId)); //public Tenant Tenant => Extends.Get(nameof(Tenant), k => Tenant.FindById(TenantId));
///// <summary>租户</summary> ///// <summary>租户</summary>
@ -103,7 +103,7 @@ public partial class Department : Entity<Department>, ITenantSource
//public String TenantName => Tenant?.Name; //public String TenantName => Tenant?.Name;
///// <summary>管理者</summary> ///// <summary>管理者</summary>
//[XmlIgnore, ScriptIgnore, IgnoreDataMember] //[XmlIgnore, IgnoreDataMember, ScriptIgnore]
//public User Manager => Extends.Get(nameof(Manager), k => User.FindByID(ManagerId)); //public User Manager => Extends.Get(nameof(Manager), k => User.FindByID(ManagerId));
///// <summary>管理者</summary> ///// <summary>管理者</summary>
@ -111,7 +111,7 @@ public partial class Department : Entity<Department>, ITenantSource
//public String ManagerName => Manager?.ToString(); //public String ManagerName => Manager?.ToString();
/// <summary>父级</summary> /// <summary>父级</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Department? Parent => Extends.Get(nameof(Department), k => FindByID(ParentID)); public Department? Parent => Extends.Get(nameof(Department), k => FindByID(ParentID));
/// <summary>父级</summary> /// <summary>父级</summary>
@ -155,13 +155,13 @@ public partial class Department : Entity<Department>, ITenantSource
/// <summary> /// <summary>
/// 获取子集合 /// 获取子集合
/// </summary> /// </summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IEnumerable<Department>? ChildList => Extends.Get(nameof(ChildList), k => FindAllByParentId(ID).OrderBy(e => e.ID)); public IEnumerable<Department>? ChildList => Extends.Get(nameof(ChildList), k => FindAllByParentId(ID).OrderBy(e => e.ID));
/// <summary> /// <summary>
///是否存在子集 ///是否存在子集
/// </summary> /// </summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Boolean subset { get; set; } public Boolean subset { get; set; }
#endregion #endregion

View File

@ -1,5 +1,4 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using NewLife;
using XCode.Configuration; using XCode.Configuration;
using XCode.Membership; using XCode.Membership;
@ -53,6 +52,7 @@ public class IPModule : EntityModule
var fs = GetFields(entity.GetType()); var fs = GetFields(entity.GetType());
var ip = ManageProvider.UserHost; var ip = ManageProvider.UserHost;
ip ??= NetHelper.MyIP()?.ToString();
if (!ip.IsNullOrEmpty()) if (!ip.IsNullOrEmpty())
{ {
// 如果不是IPv6去掉后面端口 // 如果不是IPv6去掉后面端口

View File

@ -83,6 +83,7 @@ public class UserModule : EntityModule
// 当前登录用户 // 当前登录用户
var prv = Provider ?? ManageProvider.Provider; var prv = Provider ?? ManageProvider.Provider;
var user = prv?.Current; var user = prv?.Current;
user ??= new User { Name = Environment.UserName };
if (user != null) if (user != null)
{ {
switch (method) switch (method)

View File

@ -73,6 +73,22 @@ public class XCodeSetting : Config<XCodeSetting>
[Description("反向工程。Off 关闭ReadOnly 只读不执行On 打开仅新建Full 完全,修改删除")] [Description("反向工程。Off 关闭ReadOnly 只读不执行On 打开仅新建Full 完全,修改删除")]
public Migration Migration { get; set; } = Migration.On; public Migration Migration { get; set; } = Migration.On;
/// <summary>检查注释。表注释或字段注释,反向工程,默认打开</summary>
[Description("检查注释。表注释或字段注释,反向工程,默认打开")]
public Boolean CheckComment { get; set; } = true;
/// <summary>检查删除索引。反向工程,默认打开</summary>
[Description("检查删除索引。反向工程,默认打开")]
public Boolean CheckDeleteIndex { get; set; } = true;
/// <summary>检查添加索引。反向工程,默认打开</summary>
[Description("检查添加索引。反向工程,默认打开")]
public Boolean CheckAddIndex { get; set; } = true;
/// <summary>是否检查索引重复。默认打开</summary>
[Description("检查索引重复。默认打开")]
public Boolean CheckDuplicateIndex { get; set; } = true;
/// <summary></summary> /// <summary></summary>
[Description("表名称、字段名大小写格式。Default 默认(由数据库决定);None 与模型保持一致;Upper 全大写;Lower 全小写;Underline下划线")] [Description("表名称、字段名大小写格式。Default 默认(由数据库决定);None 与模型保持一致;Upper 全大写;Lower 全小写;Underline下划线")]
public NameFormats NameFormat { get; set; } = NameFormats.Default; public NameFormats NameFormat { get; set; } = NameFormats.Default;

View File

@ -1,4 +1,5 @@
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Web.Script.Serialization;
using System.Xml.Serialization; using System.Xml.Serialization;
namespace XCode.Transform; namespace XCode.Transform;
@ -33,27 +34,27 @@ public class ExtractSetting : IExtractSetting
{ {
#region #region
/// <summary>开始。大于等于</summary> /// <summary>开始。大于等于</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public DateTime Start { get; set; } public DateTime Start { get; set; }
/// <summary>结束。小于</summary> /// <summary>结束。小于</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public DateTime End { get; set; } public DateTime End { get; set; }
/// <summary>时间偏移。距离实时时间的秒数,部分业务不能跑到实时</summary> /// <summary>时间偏移。距离实时时间的秒数,部分业务不能跑到实时</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Int32 Offset { get; set; } public Int32 Offset { get; set; }
/// <summary>开始行。分页</summary> /// <summary>开始行。分页</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Int32 Row { get; set; } public Int32 Row { get; set; }
/// <summary>步进。最大区间大小,秒</summary> /// <summary>步进。最大区间大小,秒</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Int32 Step { get; set; } public Int32 Step { get; set; }
/// <summary>批大小</summary> /// <summary>批大小</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Int32 BatchSize { get; set; } = 5000; public Int32 BatchSize { get; set; } = 5000;
///// <summary>启用</summary> ///// <summary>启用</summary>

View File

@ -49,14 +49,14 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
} }
/// <summary>子节点</summary> /// <summary>子节点</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual IList<TEntity> Childs => Extends.Get(nameof(Childs), e => FindChilds())!; public virtual IList<TEntity> Childs => Extends.Get(nameof(Childs), e => FindChilds())!;
/// <summary>子节点</summary> /// <summary>子节点</summary>
protected virtual IList<TEntity> FindChilds() => FindAllByParent((TKey)this[Setting.Key]!); protected virtual IList<TEntity> FindChilds() => FindAllByParent((TKey)this[Setting.Key]!);
/// <summary>父节点</summary> /// <summary>父节点</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual TEntity? Parent => Extends.Get(nameof(Parent), e => FindParent()); public virtual TEntity? Parent => Extends.Get(nameof(Parent), e => FindParent());
/// <summary>父节点</summary> /// <summary>父节点</summary>
@ -66,19 +66,19 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
protected static TEntity FindByKeyWithCache(TKey key) => Meta.Session.Cache.Find(e => Equals(e[Setting.Key], key)); protected static TEntity FindByKeyWithCache(TKey key) => Meta.Session.Cache.Find(e => Equals(e[Setting.Key], key));
/// <summary>子孙节点</summary> /// <summary>子孙节点</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual IList<TEntity> AllChilds => Extends.Get(nameof(AllChilds), e => FindAllChilds(this))!; public virtual IList<TEntity> AllChilds => Extends.Get(nameof(AllChilds), e => FindAllChilds(this))!;
/// <summary>子孙节点,包含自己</summary> /// <summary>子孙节点,包含自己</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual IList<TEntity> MyAllChilds => Extends.Get(nameof(MyAllChilds), e => FindAllChilds(this, true))!; public virtual IList<TEntity> MyAllChilds => Extends.Get(nameof(MyAllChilds), e => FindAllChilds(this, true))!;
/// <summary>父节点集合</summary> /// <summary>父节点集合</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual IList<TEntity> AllParents => Extends.Get(nameof(AllParents), e => FindAllParents(this))!; public virtual IList<TEntity> AllParents => Extends.Get(nameof(AllParents), e => FindAllParents(this))!;
/// <summary>深度</summary> /// <summary>深度</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual Int32 Deepth public virtual Int32 Deepth
{ {
get get
@ -109,7 +109,7 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
} }
/// <summary>节点名</summary> /// <summary>节点名</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual String? NodeName public virtual String? NodeName
{ {
get get
@ -122,7 +122,7 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
} }
/// <summary>父级节点名</summary> /// <summary>父级节点名</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual String? ParentNodeName public virtual String? ParentNodeName
{ {
get get
@ -139,7 +139,7 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
/// <summary>树形节点名,根据深度带全角空格前缀</summary> /// <summary>树形节点名,根据深度带全角空格前缀</summary>
[DisplayName("节点名")] [DisplayName("节点名")]
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual String TreeNodeName public virtual String TreeNodeName
{ {
get get
@ -156,7 +156,7 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
} }
/// <summary>树形节点名,根据深度带全角空格前缀</summary> /// <summary>树形节点名,根据深度带全角空格前缀</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual String TreeNodeName2 public virtual String TreeNodeName2
{ {
get get
@ -173,7 +173,7 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
} }
/// <summary>树形节点名,根据深度带全角空格前缀</summary> /// <summary>树形节点名,根据深度带全角空格前缀</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public virtual String TreeNodeText public virtual String TreeNodeText
{ {
get get
@ -195,11 +195,11 @@ public abstract partial class EntityTree<TKey, TEntity> : Entity<TEntity>, IEnti
} }
/// <summary>斜杠分隔的全路径</summary> /// <summary>斜杠分隔的全路径</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public String FullPath => @"\" + GetFullPath(true); public String FullPath => @"\" + GetFullPath(true);
/// <summary>斜杠分隔的全父路径</summary> /// <summary>斜杠分隔的全父路径</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public String FullParentPath => @"\" + GetFullPath(false); public String FullParentPath => @"\" + GetFullPath(false);
#endregion #endregion

View File

@ -46,7 +46,7 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="NewLife.Core" Version="11.4.2025.201" /> <PackageReference Include="NewLife.Core" Version="11.4.2025.401" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Using Include="NewLife" /> <Using Include="NewLife" />
@ -81,6 +81,7 @@
<Compile Remove="DataAccessLayer\Common\ConnectionPool.cs" /> <Compile Remove="DataAccessLayer\Common\ConnectionPool.cs" />
<Compile Remove="DataAccessLayer\Database\Firebird.cs" /> <Compile Remove="DataAccessLayer\Database\Firebird.cs" />
<Compile Remove="DataAccessLayer\Database\Network.cs" /> <Compile Remove="DataAccessLayer\Database\Network.cs" />
<Compile Remove="DataAccessLayer\Model\SerializableDataMember.cs" />
<Compile Remove="DataAccessLayer\MSPageSplit.cs" /> <Compile Remove="DataAccessLayer\MSPageSplit.cs" />
<Compile Remove="Membership\IPasswordProvider.cs" /> <Compile Remove="Membership\IPasswordProvider.cs" />
<Compile Remove="Membership\MenuProvider.cs" /> <Compile Remove="Membership\MenuProvider.cs" />

View File

@ -146,15 +146,15 @@ public class MemberSectionTests
#region #region
/// <summary>顶级根。它的Childs就是各个省份</summary> /// <summary>顶级根。它的Childs就是各个省份</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public static Area Root { get; } = new Area(); public static Area Root { get; } = new Area();
/// <summary>父级</summary> /// <summary>父级</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Area Parent => Extends.Get(nameof(Parent), k => FindByID(ParentID) ?? Root); public Area Parent => Extends.Get(nameof(Parent), k => FindByID(ParentID) ?? Root);
/// <summary>所有父级</summary> /// <summary>所有父级</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<Area> AllParents => Extends.Get(nameof(AllParents), k => public IList<Area> AllParents => Extends.Get(nameof(AllParents), k =>
{ {
var list = new List<Area>(); var list = new List<Area>();
@ -200,11 +200,11 @@ public class MemberSectionTests
} }
/// <summary>下级地区</summary> /// <summary>下级地区</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<Area> Childs => Extends.Get(nameof(Childs), k => FindAllByParentID(ID).Where(e => e.Enable).ToList()); public IList<Area> Childs => Extends.Get(nameof(Childs), k => FindAllByParentID(ID).Where(e => e.Enable).ToList());
/// <summary>子孙级区域。支持省市区,不支持乡镇街道</summary> /// <summary>子孙级区域。支持省市区,不支持乡镇街道</summary>
[XmlIgnore, ScriptIgnore] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<Area> AllChilds => Extends.Get(nameof(AllChilds), k => public IList<Area> AllChilds => Extends.Get(nameof(AllChilds), k =>
{ {
var list = new List<Area>(); var list = new List<Area>();
@ -408,13 +408,13 @@ public class MemberSectionTests
var code = var code =
""" """
#region #region
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public Product Product => Extends.Get(nameof(Product), k => Product.FindById(ProductId)); public Product Product => Extends.Get(nameof(Product), k => Product.FindById(ProductId));
[Map(nameof(ProductId), typeof(Product), "Id")] [Map(nameof(ProductId), typeof(Product), "Id")]
public String ProductName => Product?.Name; public String ProductName => Product?.Name;
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public DeviceGroup Group => Extends.Get(nameof(Group), k => DeviceGroup.FindById(GroupId)); public DeviceGroup Group => Extends.Get(nameof(Group), k => DeviceGroup.FindById(GroupId));
[Map(nameof(GroupId), typeof(DeviceGroup), "Id")] [Map(nameof(GroupId), typeof(DeviceGroup), "Id")]
@ -425,7 +425,7 @@ public class MemberSectionTests
public String AreaName => Area.FindByIDs(CityId, ProvinceId)?.Path; public String AreaName => Area.FindByIDs(CityId, ProvinceId)?.Path;
/// <summary>父级设备</summary> /// <summary>父级设备</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
//[ScriptIgnore] //[ScriptIgnore]
public Device Parent => Extends.Get(nameof(Parent), k => Device.FindById(ParentId)); public Device Parent => Extends.Get(nameof(Parent), k => Device.FindById(ParentId));
@ -434,18 +434,18 @@ public class MemberSectionTests
public String ParentName => Parent?.Name; public String ParentName => Parent?.Name;
/// <summary>子设备。借助扩展属性缓存</summary> /// <summary>子设备。借助扩展属性缓存</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<Device> Childs => Extends.Get(nameof(Childs), k => FindAllByParent(Id)); public IList<Device> Childs => Extends.Get(nameof(Childs), k => FindAllByParent(Id));
/// <summary>设备属性。借助扩展属性缓存</summary> /// <summary>设备属性。借助扩展属性缓存</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<DeviceProperty> Properties => Extends.Get(nameof(Properties), k => DeviceProperty.FindAllByDeviceId(Id)); public IList<DeviceProperty> Properties => Extends.Get(nameof(Properties), k => DeviceProperty.FindAllByDeviceId(Id));
/// <summary>设备服务。借助扩展属性缓存</summary> /// <summary>设备服务。借助扩展属性缓存</summary>
[XmlIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
public IList<DeviceService> Services => Extends.Get(nameof(Services), k => DeviceService.FindAllByDeviceId(Id)); public IList<DeviceService> Services => Extends.Get(nameof(Services), k => DeviceService.FindAllByDeviceId(Id));
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
protected Boolean IgnoreVaild { get; set; } = false; protected Boolean IgnoreVaild { get; set; } = false;
#endregion #endregion
"""; """;

View File

@ -287,7 +287,7 @@ public partial class User
private String? _Ex6; private String? _Ex6;
/// <summary>扩展6</summary> /// <summary>扩展6</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
[Category("扩展")] [Category("扩展")]
[DisplayName("扩展6")] [DisplayName("扩展6")]
[Description("扩展6")] [Description("扩展6")]

View File

@ -152,6 +152,34 @@ public class MySqlTests
catch (Exception ex) { XTrace.WriteException(ex); } catch (Exception ex) { XTrace.WriteException(ex); }
} }
[Fact]
public void MembershipTest()
{
var connStr = _ConnStr.Replace("Database=sys;", "Database=Membership;");
DAL.AddConnStr("MySql_member", connStr, null, "MySql");
User.Meta.ConnName = "MySql_member";
Role.Meta.ConnName = "MySql_member";
User.Meta.Session.InitData();
Role.Meta.Session.InitData();
var count = User.Meta.Count;
Assert.True(count > 0);
count = Role.Meta.Count;
Assert.True(count > 0);
var list = Role.FindAll();
Assert.Equal(4, list.Count);
var list2 = Role.FindAll(Role._.Name == "管理员");
Assert.Single(list2);
var list3 = Role.Search("用户", null);
Assert.Equal(2, list3.Count);
}
[Fact] [Fact]
public void TablePrefixTest() public void TablePrefixTest()
{ {

View File

@ -95,6 +95,27 @@ public class SQLiteTests
Assert.Equal(dal1, dal2); Assert.Equal(dal1, dal2);
Assert.Equal("sysSQLite", dal2.ConnName); Assert.Equal("sysSQLite", dal2.ConnName);
Assert.Equal(DatabaseType.SQLite, dal2.DbType); Assert.Equal(DatabaseType.SQLite, dal2.DbType);
Assert.Equal($"Data Source={db}", dal2.ConnStr);
}
[Fact]
public void MapToTest2()
{
var db = "Data\\Membership.db";
var dbf = db.GetFullPath();
DAL.AddConnStr("sysSQLite", $"Data Source={db}", null, "SQLite");
DAL.AddConnStr("mapTest", "MapTo=sysSQLite;TablePrefix=xcwl_", null, null);
var dal1 = DAL.Create("sysSQLite");
var dal2 = DAL.Create("mapTest");
Assert.NotNull(dal2);
Assert.NotEqual(dal1, dal2);
Assert.Equal("mapTest", dal2.ConnName);
Assert.Equal(DatabaseType.SQLite, dal2.DbType);
Assert.NotEqual($"Data Source={db};TablePrefix=xcwl_", dal2.ConnStr);
//Assert.EndsWith(";TablePrefix=xcwl_", dal2.Db.ConnectionString);
Assert.Equal(dbf, (dal2.Db as FileDbBase).DatabaseName);
} }
[Fact] [Fact]

View File

@ -40,16 +40,16 @@ public class AreaTests
XTrace.WriteLine("北京"); XTrace.WriteLine("北京");
r = Area.FindByName(0, "北京"); r = Area.FindByName(0, "北京");
Assert.Equal(17, r.AllChilds.Count); Assert.Equal(17, r.GetAllChilds().Count);
Assert.Equal("北京/延庆", r.AllChilds[^1].Path); Assert.Equal("北京/延庆", r.GetAllChilds()[^1].Path);
XTrace.WriteLine("容县"); XTrace.WriteLine("容县");
r = Area.FindByID(450921); r = Area.FindByID(450921);
Assert.Equal("容县", r.Name); Assert.Equal("容县", r.Name);
Assert.Equal(2, r.AllParents.Count); Assert.Equal(2, r.GetAllParents().Count);
Assert.Equal("广西/玉林", r.ParentPath); Assert.Equal("广西/玉林", r.ParentPath);
Assert.Equal(15, r.AllChilds.Count); Assert.Equal(15, r.GetAllChilds().Count);
Assert.Equal("广西/玉林/容县/容州", r.AllChilds[0].Path); Assert.Equal("广西/玉林/容县/容州", r.GetAllChilds()[0].Path);
var r2 = Area.FindByIDs(450921102, 450921, 450900, 450000); var r2 = Area.FindByIDs(450921102, 450921, 450900, 450000);
Assert.Equal("杨梅", r2.Name); Assert.Equal("杨梅", r2.Name);
@ -153,6 +153,51 @@ public class AreaTests
} }
} }
[TestOrder(50)]
[Fact]
public void ParentTest()
{
var ar = Area.FindByID(450921102);
Assert.Equal("杨梅", ar.Name);
var ps = ar.GetAllParents();
Assert.Equal(3, ps.Count);
ar = ar.Parent;
Assert.Equal("容县", ar.Name);
ar = ar.Parent;
Assert.Equal("玉林", ar.Name);
ar = ar.Parent;
Assert.Equal("广西", ar.Name);
ar = ar.Parent;
Assert.Equal(0, ar.ID);
Assert.Equal(Area.Root, ar);
}
[TestOrder(50)]
[Fact]
public void ParentTest2()
{
var ar = Area.FindByID(310116);
Assert.Equal("金山", ar.Name);
var ps = ar.GetAllParents();
Assert.Equal(2, ps.Count);
ar = ar.Parent;
Assert.Equal("市辖区", ar.Name);
ar = ar.Parent;
Assert.Equal("上海", ar.Name);
ar = ar.Parent;
Assert.Equal(0, ar.ID);
Assert.Equal(Area.Root, ar);
}
[Fact] [Fact]
public async void Download() public async void Download()
{ {

View File

@ -217,7 +217,7 @@ public partial class User2
private String _Ex6; private String _Ex6;
/// <summary>扩展6</summary> /// <summary>扩展6</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember] [XmlIgnore, IgnoreDataMember, ScriptIgnore]
[DisplayName("扩展6")] [DisplayName("扩展6")]
[Description("扩展6")] [Description("扩展6")]
[DataObjectField(false, false, true, 50)] [DataObjectField(false, false, true, 50)]

View File

@ -84,13 +84,13 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NewLife.Core" Version="11.4.2025.201" /> <PackageReference Include="NewLife.Core" Version="11.4.2025.401" />
<PackageReference Include="NewLife.IP" Version="2.2.2025.101" /> <PackageReference Include="NewLife.IP" Version="2.3.2025.202" />
<PackageReference Include="NewLife.UnitTest" Version="1.0.2025.101" /> <PackageReference Include="NewLife.UnitTest" Version="1.0.2025.101" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="9.0.0" /> <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="9.0.0" />
<PackageReference Include="xunit" Version="2.9.3" /> <PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1"> <PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>