diff --git a/Test/Test.csproj b/Test/Test.csproj index 0dfb014eb..4b04d3f41 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -38,9 +38,9 @@ - - - + + + diff --git a/XCode/Configuration/TableItem.cs b/XCode/Configuration/TableItem.cs index 946cd38f9..ebfdf6d5f 100644 --- a/XCode/Configuration/TableItem.cs +++ b/XCode/Configuration/TableItem.cs @@ -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]; for (var j = i + 1; j < table.Indexes.Count; j++) diff --git a/XCode/DataAccessLayer/DAL.cs b/XCode/DataAccessLayer/DAL.cs index 2c71e700d..ea3e15874 100644 --- a/XCode/DataAccessLayer/DAL.cs +++ b/XCode/DataAccessLayer/DAL.cs @@ -70,7 +70,9 @@ public partial class DAL 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; @@ -86,6 +88,7 @@ public partial class DAL public IAsyncDbSession AsyncSession => (Db as DbBase)!.CreateSessionForAsync(); private String? _mapTo; + private String? _mapConnStr; private static ICache _cache = new MemoryCache(); #endregion @@ -122,12 +125,31 @@ public partial class DAL AddConnStr(connName, connstr, null, "SQLite"); } - ConnStr = css[connName]; - if (ConnStr.IsNullOrEmpty()) throw new XCodeException("请在使用数据库前设置[" + connName + "]连接字符串"); + var connStr = ConnStr = css[connName]; + if (connStr.IsNullOrEmpty()) throw new XCodeException("请在使用数据库前设置[" + connName + "]连接字符串"); // 连接映射 - var vs = ConnStr.SplitAsDictionary("=", ",", true); - if (vs.TryGetValue("MapTo", out var map) && !map.IsNullOrEmpty()) _mapTo = map; + var vs = connStr.SplitAsDictionary("=", ";", true); + 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) { @@ -620,7 +642,7 @@ public partial class DAL //CheckDatabase(); var tables = Db.CreateMetaData().GetTables(); - if (span != null) span.Tag += ": " + tables.Join(","); + span?.AppendTag(tables.Join(","), tables.Count); return tables; } diff --git a/XCode/DataAccessLayer/Database/MySql.cs b/XCode/DataAccessLayer/Database/MySql.cs index 7c913640b..9058288dd 100755 --- a/XCode/DataAccessLayer/Database/MySql.cs +++ b/XCode/DataAccessLayer/Database/MySql.cs @@ -4,6 +4,7 @@ using System.Net; using NewLife.Collections; using NewLife.Data; using NewLife.Log; +using NewLife.Web; namespace XCode.DataAccessLayer; @@ -18,8 +19,14 @@ internal class MySql : RemoteDb /// protected override DbProviderFactory? CreateFactory() { - //_Factory = GetProviderFactory("NewLife.MySql.dll", "NewLife.MySql.MySqlClientFactory") ?? - // GetProviderFactory("MySql.Data.dll", "MySql.Data.MySqlClient.MySqlClientFactory"); + var type = PluginHelper.LoadPlugin("NewLife.MySql.MySqlClientFactory", null, "NewLife.MySql.dll", null); + 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 在开发过程中,数据驱动下载站点没有它的包,暂时不支持下载 return GetProviderFactory("NewLife.MySql", "NewLife.MySql.dll", "NewLife.MySql.MySqlClientFactory", true, true) ?? GetProviderFactory(null, "MySql.Data.dll", "MySql.Data.MySqlClient.MySqlClientFactory"); diff --git a/XCode/DataAccessLayer/Database/SQLite.cs b/XCode/DataAccessLayer/Database/SQLite.cs index 6aa1e9969..655b7d895 100755 --- a/XCode/DataAccessLayer/Database/SQLite.cs +++ b/XCode/DataAccessLayer/Database/SQLite.cs @@ -847,7 +847,7 @@ internal class SQLiteMetaData : FileDbMetaData /// 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) { @@ -864,7 +864,7 @@ internal class SQLiteMetaData : FileDbMetaData } // 把onlySql设为true,让基类只产生语句而不执行 - var sql = base.CheckColumnsChange(entitytable, dbtable, onlySql, true); + var sql = base.CheckColumnsChange(entitytable, dbtable, onlySql, true, set); if (sql.IsNullOrEmpty()) return sql; // SQLite 3.35.0 起支持 Drop Column @@ -879,7 +879,7 @@ internal class SQLiteMetaData : FileDbMetaData Database.CreateSession().Execute(sql); - return null; + return ""; } if (sql.Contains("Drop Column") && v != null && v >= new Version(3, 35)) { @@ -887,11 +887,11 @@ internal class SQLiteMetaData : FileDbMetaData Database.CreateSession().Execute(sql); - return null; + return ""; } 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; @@ -941,7 +941,7 @@ internal class SQLiteMetaData : FileDbMetaData throw; } - return null; + return ""; } #endregion @@ -965,16 +965,17 @@ internal class SQLiteMetaData : FileDbMetaData /// /// /// - protected override void CheckTable(IDataTable entitytable, IDataTable dbtable, Migration mode) + /// + 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; 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) diff --git a/XCode/DataAccessLayer/MetaData/DbMetaData_Negative.cs b/XCode/DataAccessLayer/MetaData/DbMetaData_Negative.cs index c64f94137..6cd067b74 100644 --- a/XCode/DataAccessLayer/MetaData/DbMetaData_Negative.cs +++ b/XCode/DataAccessLayer/MetaData/DbMetaData_Negative.cs @@ -69,14 +69,16 @@ internal partial class DbMetaData { 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); - CheckAllTables(tables, mode, dbExist); + CheckAllTables(tables, mode, dbExist, set); } private Boolean? hasCheckedDatabase; @@ -119,7 +121,7 @@ internal partial class DbMetaData return dbExist; } - private void CheckAllTables(IDataTable[] tables, Migration mode, Boolean dbExit) + private void CheckAllTables(IDataTable[] tables, Migration mode, Boolean dbExit, XCodeSetting set) { IList? dbtables = null; if (dbExit) @@ -141,9 +143,9 @@ internal partial class DbMetaData // 判断指定表是否存在于数据库中,以决定是创建表还是修改表 if (dbtable != null) - CheckTable(item, dbtable, mode); + CheckTable(item, dbtable, mode, set); else - CheckTable(item, null, mode); + CheckTable(item, null, mode, set); } 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; if (dbtable == null) @@ -174,19 +176,30 @@ internal partial class DbMetaData var onlyCreate = mode < Migration.Full; var sb = new StringBuilder(); - var sql = CheckTableDescription(entitytable, dbtable, mode); - if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); + if (set.CheckComment) + { + var sql = CheckTableDescription(entitytable, dbtable, mode); + if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); + } - // 先删除索引,后面才有可能删除字段 - sql = CheckDeleteIndex(entitytable, dbtable, mode); - if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); + if (set.CheckDeleteIndex) + { + // 先删除索引,后面才有可能删除字段 + 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); + } - // 新增字段后,可能需要删除索引 - sql = CheckAddIndex(entitytable, dbtable, mode); - if (!sql.IsNullOrEmpty()) Append(sb, ";" + Environment.NewLine, sql); + if (set.CheckAddIndex) + { + // 新增字段后,可能需要删除索引 + 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}"); } @@ -197,8 +210,9 @@ internal partial class DbMetaData /// /// /// + /// /// 返回未执行语句 - 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 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); //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); diff --git a/XCode/DataAccessLayer/Model/ModelHelper.cs b/XCode/DataAccessLayer/Model/ModelHelper.cs index 1f4a2d85f..6500c07ec 100644 --- a/XCode/DataAccessLayer/Model/ModelHelper.cs +++ b/XCode/DataAccessLayer/Model/ModelHelper.cs @@ -6,9 +6,9 @@ using System.Runtime.Serialization; using System.Text; using System.Xml; using System.Xml.Serialization; -using NewLife; using NewLife.Log; using NewLife.Reflection; +using XCode.Code; namespace XCode.DataAccessLayer; @@ -231,14 +231,15 @@ public static class ModelHelper writer.WriteEndElement(); } + var nameFormat = option is EntityBuilderOption opt ? opt.NameFormat : NameFormats.Default; { writer.WriteStartElement("Tables"); // 回写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"); - (item as IXmlSerializable)!.WriteXml(writer); + table.WriteXml(writer, nameFormat); writer.WriteEndElement(); } @@ -312,10 +313,10 @@ public static class ModelHelper { if (option != null) { - if (option is IXmlSerializable xml2) - xml2.ReadXml(reader); - else - ReadXml(reader, option); + //if (option is IXmlSerializable xml2) + // xml2.ReadXml(reader); + //else + ReadXml(reader, option); } else { @@ -345,7 +346,7 @@ public static class ModelHelper static void ReadTable(XmlReader reader, Func createTable, IList list) { var table = createTable(); - (table as IXmlSerializable)!.ReadXml(reader); + table.ReadXml(reader); // 判断是否存在属性NeedHistory设置且为true var needHistory = table.Properties.FirstOrDefault(x => x.Key.EqualIgnoreCase("NeedHistory")); @@ -433,7 +434,9 @@ public static class ModelHelper // 清空默认的原始类型,让其从xml读取 dc.RawType = null; } - (dc as IXmlSerializable)!.ReadXml(reader); + ReadXmlAttribute(reader, dc); + // 跳过当前节点 + reader.Skip(); // 未指定DataType,但指定了Type,修正为枚举整型 if (dc.DataType == null && dc.Properties.ContainsKey("Type")) dc.DataType = typeof(Int32); @@ -454,7 +457,10 @@ public static class ModelHelper while (reader.IsStartElement()) { var di = table.CreateIndex(); - (di as IXmlSerializable)!.ReadXml(reader); + ReadXmlAttribute(reader, di); + // 跳过当前节点 + reader.Skip(); + di.Fix(); table.Indexes.Add(di); } @@ -483,18 +489,21 @@ public static class ModelHelper /// 写入 /// /// - public static IDataTable WriteXml(this IDataTable table, XmlWriter writer) + /// + 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) { writer.WriteStartElement("Columns"); - foreach (IXmlSerializable item in table.Columns) + foreach (var dc in table.Columns) { writer.WriteStartElement("Column"); - item.WriteXml(writer); + WriteXml(writer, dc, false, ignoreNameCase); writer.WriteEndElement(); } writer.WriteEndElement(); @@ -502,10 +511,10 @@ public static class ModelHelper if (table.Indexes.Count > 0) { writer.WriteStartElement("Indexes"); - foreach (IXmlSerializable item in table.Indexes) + foreach (var di in table.Indexes) { writer.WriteStartElement("Index"); - item.WriteXml(writer); + WriteXml(writer, di, false, true); writer.WriteEndElement(); } writer.WriteEndElement(); @@ -634,7 +643,8 @@ public static class ModelHelper /// /// 数值 /// 是否写数值为默认值的成员。为了节省空间,默认不写。 - public static void WriteXml(XmlWriter writer, Object value, Boolean writeDefaultValueMember = false) + /// 忽略名称大小写 + public static void WriteXml(XmlWriter writer, Object value, Boolean writeDefaultValueMember = false, Boolean ignoreNameCase = true) { var type = value.GetType(); var def = GetDefault(type); @@ -681,16 +691,24 @@ public static class ModelHelper if (code == TypeCode.String && "" + obj == "" + dobj) continue; } - if (code == TypeCode.String) + if (code == TypeCode.String && obj is String str) { // 如果别名与名称相同,则跳过,不区分大小写 // 改为区分大小写,避免linux环境下 mysql 数据库存在 if (pi.Name == "Name") - name = (String)obj; + name = str; else if (pi.Name is "TableName" or "ColumnName") { - if (name == (String)obj) continue; - if (/*ignoreNameCase &&*/ name.EqualIgnoreCase((String)obj)) continue; + if (name == str) 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) @@ -723,9 +741,8 @@ public static class ModelHelper 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) { @@ -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) { diff --git a/XCode/DataAccessLayer/Model/XField.cs b/XCode/DataAccessLayer/Model/XField.cs index a7998a88d..965f9cba0 100644 --- a/XCode/DataAccessLayer/Model/XField.cs +++ b/XCode/DataAccessLayer/Model/XField.cs @@ -2,7 +2,6 @@ using System.Runtime.Serialization; using System.Xml; using System.Xml.Serialization; -using NewLife; using NewLife.Collections; namespace XCode.DataAccessLayer; @@ -12,7 +11,7 @@ namespace XCode.DataAccessLayer; [DisplayName("字段模型")] [Description("字段模型")] [XmlRoot("Column")] -class XField : SerializableDataMember, IDataColumn, ICloneable +class XField : /*SerializableDataMember,*/ IDataColumn, ICloneable { #region 属性 /// 名称 diff --git a/XCode/DataAccessLayer/Model/XIndex.cs b/XCode/DataAccessLayer/Model/XIndex.cs index 69dc48897..ee0df7c3d 100644 --- a/XCode/DataAccessLayer/Model/XIndex.cs +++ b/XCode/DataAccessLayer/Model/XIndex.cs @@ -1,8 +1,6 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Runtime.Serialization; using System.Xml.Serialization; -using NewLife; namespace XCode.DataAccessLayer; @@ -11,7 +9,7 @@ namespace XCode.DataAccessLayer; [DisplayName("索引模型")] [Description("索引模型")] [XmlRoot("Index")] -class XIndex : SerializableDataMember, IDataIndex, ICloneable +class XIndex : /*SerializableDataMember,*/ IDataIndex, ICloneable { #region 属性 /// 名称 diff --git a/XCode/DataAccessLayer/Model/XTable.cs b/XCode/DataAccessLayer/Model/XTable.cs index 69f273bf5..0d8fa7848 100644 --- a/XCode/DataAccessLayer/Model/XTable.cs +++ b/XCode/DataAccessLayer/Model/XTable.cs @@ -16,7 +16,7 @@ namespace XCode.DataAccessLayer; [DisplayName("表模型")] [Description("表模型")] [XmlRoot("Table")] -class XTable : IDataTable, ICloneable, IXmlSerializable +class XTable : IDataTable, ICloneable//, IXmlSerializable { #region 基本属性 ///// 编号 @@ -243,16 +243,16 @@ class XTable : IDataTable, ICloneable, IXmlSerializable #endregion #region IXmlSerializable 成员 - /// 获取架构 - /// - XmlSchema IXmlSerializable.GetSchema() => null; + ///// 获取架构 + ///// + //XmlSchema IXmlSerializable.GetSchema() => null; - /// 读取 - /// - void IXmlSerializable.ReadXml(XmlReader reader) => ModelHelper.ReadXml(this, reader); + ///// 读取 + ///// + //void IXmlSerializable.ReadXml(XmlReader reader) => ModelHelper.ReadXml(this, reader); - /// 写入 - /// - void IXmlSerializable.WriteXml(XmlWriter writer) => ModelHelper.WriteXml(this, writer); + ///// 写入 + ///// + //void IXmlSerializable.WriteXml(XmlWriter writer) => ModelHelper.WriteXml(this, writer); #endregion } \ No newline at end of file diff --git a/XCode/Membership/Member.xml b/XCode/Membership/Member.xml index 5b0a91e28..b81a52d04 100644 --- a/XCode/Membership/Member.xml +++ b/XCode/Membership/Member.xml @@ -71,7 +71,7 @@ - + diff --git a/XCode/Membership/xcodetool.exe b/XCode/Membership/xcodetool.exe index 48abd79b2..765ae324e 100644 Binary files a/XCode/Membership/xcodetool.exe and b/XCode/Membership/xcodetool.exe differ diff --git a/XCode/Membership/地区.Biz.cs b/XCode/Membership/地区.Biz.cs index 5b38940a2..04ae91beb 100644 --- a/XCode/Membership/地区.Biz.cs +++ b/XCode/Membership/地区.Biz.cs @@ -1,6 +1,7 @@ using System.IO.Compression; using System.Net; using System.Net.Http; +using System.Runtime.Serialization; using System.Text; using System.Web.Script.Serialization; using System.Xml.Serialization; @@ -105,16 +106,21 @@ public partial class Area : Entity #region 扩展属性 /// 顶级根。它的Childs就是各个省份 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public static Area Root { get; } = new Area(); /// 父级 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Area Parent => Extends.Get(nameof(Parent), k => FindByID(ParentID) ?? Root)!; + ///// 所有父级,从高到底 + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] + //[Obsolete("=>GetAllParents")] + //public IList AllParents => Extends.Get(nameof(AllParents), k => GetAllParents())!; + /// 所有父级,从高到底 - [XmlIgnore, ScriptIgnore] - public IList AllParents => Extends.Get(nameof(AllParents), k => + /// + public IList GetAllParents() { var list = new List(); var entity = Parent; @@ -131,14 +137,14 @@ public partial class Area : Entity list.Reverse(); return list; - })!; + } /// 父级路径 - public String ParentPath + public String? ParentPath { get { - var list = AllParents; + var list = GetAllParents(); if (list != null && list.Count > 0) return list.Where(r => !r.IsVirtual).Join("/", r => r.Name); return Parent?.Name; @@ -159,21 +165,25 @@ public partial class Area : Entity } /// 下级地区 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList Childs => Extends.Get(nameof(Childs), k => FindAllByParentID(ID).Where(e => e.Enable).ToList())!; + ///// 子孙级区域。支持省市区,不支持乡镇街道 + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] + //[Obsolete("=>GetAllChilds")] + //public IList AllChilds => Extends.Get(nameof(AllChilds), k => GetAllChilds())!; + /// 子孙级区域。支持省市区,不支持乡镇街道 - [XmlIgnore, ScriptIgnore] - public IList AllChilds => Extends.Get(nameof(AllChilds), k => + public IList GetAllChilds() { var list = new List(); foreach (var item in Childs) { list.Add(item); - if (item.Level < 3) list.AddRange(item.AllChilds); + if (item.Level < 3) list.AddRange(item.GetAllChilds()); } return list; - }); + } /// 是否虚拟地区 public Boolean IsVirtual => Name.EqualIgnoreCase("市辖区", "直辖县", "直辖镇"); @@ -507,7 +517,7 @@ public partial class Area : Entity var r = FindByNames(ss); if (r != null) { - list.AddRange(r.AllParents); + list.AddRange(r.GetAllParents()); list.Add(r); if (maxLevel > 0 && list.Count > maxLevel) list = list.Take(maxLevel).ToList(); @@ -538,7 +548,7 @@ public partial class Area : Entity var r = FindAddress(Root, address, maxLevel); if (r != null) { - list.AddRange(r.AllParents); + list.AddRange(r.GetAllParents()); list.Add(r); } diff --git a/XCode/Membership/用户.Biz.cs b/XCode/Membership/用户.Biz.cs index 7112ad9bb..e7ad8370b 100644 --- a/XCode/Membership/用户.Biz.cs +++ b/XCode/Membership/用户.Biz.cs @@ -122,10 +122,10 @@ public partial class User : LogEntity, IUser, IAuthUser, IIdentity #region 扩展属性 /// 物理地址 [DisplayName("物理地址")] - public String LastLoginAddress => LastLoginIP.IPToAddress(); + public String? LastLoginAddress => LastLoginIP?.IPToAddress(); ///// 部门 - //[XmlIgnore, ScriptIgnore, IgnoreDataMember] + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] //public Department Department => Extends.Get(nameof(Department), k => Department.FindByID(DepartmentID)); ///// 部门 @@ -137,7 +137,7 @@ public partial class User : LogEntity, IUser, IAuthUser, IIdentity /// 地区名 /// [Map(nameof(AreaId))] - public String AreaName => Area.FindByID(AreaId)?.Path; + public String? AreaName => Area.FindByID(AreaId)?.Path; #endregion #region 扩展查询 @@ -602,11 +602,11 @@ public partial class User : LogEntity, IUser, IAuthUser, IIdentity #region 权限 /// 角色 /// 扩展属性不缓存空对象,一般来说,每个管理员都有对应的角色,如果没有,可能是在初始化 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] IRole IUser.Role => Role; /// 角色集合 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual IRole[] Roles => Extends.Get(nameof(Roles), k => GetRoleIDs().Select(e => ManageProvider.Get()?.FindByID(e)).Where(e => e != null).ToArray()); /// 获取角色列表。主角色在前,其它角色升序在后 @@ -693,7 +693,7 @@ public partial interface IUser IRole[] Roles { get; } /// 角色名 - String RoleName { get; } + String? RoleName { get; } /// 用户是否拥有当前菜单的指定权限 /// 指定菜单 diff --git a/XCode/Membership/用户.cs b/XCode/Membership/用户.cs index f81e16bbf..e733d90d4 100644 --- a/XCode/Membership/用户.cs +++ b/XCode/Membership/用户.cs @@ -286,7 +286,7 @@ public partial class User : IUser, IEntity private String? _Ex6; /// 扩展6 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] [Category("扩展")] [DisplayName("扩展6")] [Description("扩展6")] diff --git a/XCode/Membership/租户关系.Biz.cs b/XCode/Membership/租户关系.Biz.cs index 17f9de9a6..547f2bb9e 100644 --- a/XCode/Membership/租户关系.Biz.cs +++ b/XCode/Membership/租户关系.Biz.cs @@ -50,7 +50,7 @@ public partial class TenantUser : Entity #region 扩展属性 ///// 租户 - //[XmlIgnore, IgnoreDataMember] + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] ////[ScriptIgnore] //public Tenant Tenant => Extends.Get(nameof(Tenant), k => Tenant.FindById(TenantId)); @@ -59,7 +59,7 @@ public partial class TenantUser : Entity //public String TenantName => Tenant?.Name; ///// 用户 - //[XmlIgnore, IgnoreDataMember] + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] ////[ScriptIgnore] //public User User => Extends.Get(nameof(User), k => User.FindByID(UserId)); @@ -68,7 +68,7 @@ public partial class TenantUser : Entity //public String UserName => User?.Name; /// 角色 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] IRole ITenantUser.Role => Role; ///// 角色 @@ -76,7 +76,7 @@ public partial class TenantUser : Entity //public String RoleName => Role?.Name; /// 角色集合 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual IRole[] Roles => Extends.Get(nameof(Roles), k => GetRoleIDs().Select(e => ManageProvider.Get()?.FindByID(e)).Where(e => e != null).ToArray()); /// 获取角色列表。主角色在前,其它角色升序在后 diff --git a/XCode/Membership/菜单.Biz.cs b/XCode/Membership/菜单.Biz.cs index fc70bc76a..2ea8da9a6 100644 --- a/XCode/Membership/菜单.Biz.cs +++ b/XCode/Membership/菜单.Biz.cs @@ -265,7 +265,7 @@ public partial class Menu : EntityTree, IMenu #region 扩展权限 /// 可选权限子项 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Dictionary Permissions { get; set; } = new Dictionary(); private void LoadPermission() diff --git a/XCode/Membership/角色.Biz.cs b/XCode/Membership/角色.Biz.cs index ff02901d6..d8851f7e8 100644 --- a/XCode/Membership/角色.Biz.cs +++ b/XCode/Membership/角色.Biz.cs @@ -231,7 +231,7 @@ public partial class Role : LogEntity, IRole, ITenantSource private IDictionary _Permissions; /// 本角色权限集合 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IDictionary Permissions => _Permissions ??= new Dictionary(); /// 是否拥有指定资源的指定权限 @@ -353,7 +353,7 @@ public partial class Role : LogEntity, IRole, ITenantSource } /// 当前角色拥有的资源 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Int32[] Resources => Permissions.Keys.ToArray(); #endregion 扩展权限 diff --git a/XCode/Membership/部门.Biz.cs b/XCode/Membership/部门.Biz.cs index 180e75a41..674538e26 100644 --- a/XCode/Membership/部门.Biz.cs +++ b/XCode/Membership/部门.Biz.cs @@ -95,7 +95,7 @@ public partial class Department : Entity, ITenantSource #region 扩展属性 ///// 租户 - //[XmlIgnore, IgnoreDataMember] + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] //public Tenant Tenant => Extends.Get(nameof(Tenant), k => Tenant.FindById(TenantId)); ///// 租户 @@ -103,7 +103,7 @@ public partial class Department : Entity, ITenantSource //public String TenantName => Tenant?.Name; ///// 管理者 - //[XmlIgnore, ScriptIgnore, IgnoreDataMember] + //[XmlIgnore, IgnoreDataMember, ScriptIgnore] //public User Manager => Extends.Get(nameof(Manager), k => User.FindByID(ManagerId)); ///// 管理者 @@ -111,7 +111,7 @@ public partial class Department : Entity, ITenantSource //public String ManagerName => Manager?.ToString(); /// 父级 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Department? Parent => Extends.Get(nameof(Department), k => FindByID(ParentID)); /// 父级 @@ -155,13 +155,13 @@ public partial class Department : Entity, ITenantSource /// /// 获取子集合 /// - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IEnumerable? ChildList => Extends.Get(nameof(ChildList), k => FindAllByParentId(ID).OrderBy(e => e.ID)); /// ///是否存在子集 /// - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Boolean subset { get; set; } #endregion diff --git a/XCode/Model/IPModule.cs b/XCode/Model/IPModule.cs index 7ea50f172..0156ae7c3 100644 --- a/XCode/Model/IPModule.cs +++ b/XCode/Model/IPModule.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using NewLife; using XCode.Configuration; using XCode.Membership; @@ -53,6 +52,7 @@ public class IPModule : EntityModule var fs = GetFields(entity.GetType()); var ip = ManageProvider.UserHost; + ip ??= NetHelper.MyIP()?.ToString(); if (!ip.IsNullOrEmpty()) { // 如果不是IPv6,去掉后面端口 diff --git a/XCode/Model/UserModule.cs b/XCode/Model/UserModule.cs index 5417e1b02..27a127375 100644 --- a/XCode/Model/UserModule.cs +++ b/XCode/Model/UserModule.cs @@ -83,6 +83,7 @@ public class UserModule : EntityModule // 当前登录用户 var prv = Provider ?? ManageProvider.Provider; var user = prv?.Current; + user ??= new User { Name = Environment.UserName }; if (user != null) { switch (method) diff --git a/XCode/Setting.cs b/XCode/Setting.cs index d62aacf8c..323b25164 100644 --- a/XCode/Setting.cs +++ b/XCode/Setting.cs @@ -73,6 +73,22 @@ public class XCodeSetting : Config [Description("反向工程。Off 关闭;ReadOnly 只读不执行;On 打开,仅新建;Full 完全,修改删除")] public Migration Migration { get; set; } = Migration.On; + /// 检查注释。表注释或字段注释,反向工程,默认打开 + [Description("检查注释。表注释或字段注释,反向工程,默认打开")] + public Boolean CheckComment { get; set; } = true; + + /// 检查删除索引。反向工程,默认打开 + [Description("检查删除索引。反向工程,默认打开")] + public Boolean CheckDeleteIndex { get; set; } = true; + + /// 检查添加索引。反向工程,默认打开 + [Description("检查添加索引。反向工程,默认打开")] + public Boolean CheckAddIndex { get; set; } = true; + + /// 是否检查索引重复。默认打开 + [Description("检查索引重复。默认打开")] + public Boolean CheckDuplicateIndex { get; set; } = true; + /// [Description("表名称、字段名大小写格式。Default 默认(由数据库决定);None 与模型保持一致;Upper 全大写;Lower 全小写;Underline下划线")] public NameFormats NameFormat { get; set; } = NameFormats.Default; diff --git a/XCode/Transform/ExtractSetting.cs b/XCode/Transform/ExtractSetting.cs index 922c28efd..9b8f89bf7 100644 --- a/XCode/Transform/ExtractSetting.cs +++ b/XCode/Transform/ExtractSetting.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using System.Web.Script.Serialization; using System.Xml.Serialization; namespace XCode.Transform; @@ -33,27 +34,27 @@ public class ExtractSetting : IExtractSetting { #region 属性 /// 开始。大于等于 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public DateTime Start { get; set; } /// 结束。小于 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public DateTime End { get; set; } /// 时间偏移。距离实时时间的秒数,部分业务不能跑到实时 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Int32 Offset { get; set; } /// 开始行。分页 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Int32 Row { get; set; } /// 步进。最大区间大小,秒 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Int32 Step { get; set; } /// 批大小 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Int32 BatchSize { get; set; } = 5000; ///// 启用 diff --git a/XCode/Tree/EntityTree.cs b/XCode/Tree/EntityTree.cs index afb107355..d476e9f7c 100644 --- a/XCode/Tree/EntityTree.cs +++ b/XCode/Tree/EntityTree.cs @@ -49,14 +49,14 @@ public abstract partial class EntityTree : Entity, IEnti } /// 子节点 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual IList Childs => Extends.Get(nameof(Childs), e => FindChilds())!; /// 子节点 protected virtual IList FindChilds() => FindAllByParent((TKey)this[Setting.Key]!); /// 父节点 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual TEntity? Parent => Extends.Get(nameof(Parent), e => FindParent()); /// 父节点 @@ -66,19 +66,19 @@ public abstract partial class EntityTree : Entity, IEnti protected static TEntity FindByKeyWithCache(TKey key) => Meta.Session.Cache.Find(e => Equals(e[Setting.Key], key)); /// 子孙节点 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual IList AllChilds => Extends.Get(nameof(AllChilds), e => FindAllChilds(this))!; /// 子孙节点,包含自己 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual IList MyAllChilds => Extends.Get(nameof(MyAllChilds), e => FindAllChilds(this, true))!; /// 父节点集合 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual IList AllParents => Extends.Get(nameof(AllParents), e => FindAllParents(this))!; /// 深度 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual Int32 Deepth { get @@ -109,7 +109,7 @@ public abstract partial class EntityTree : Entity, IEnti } /// 节点名 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual String? NodeName { get @@ -122,7 +122,7 @@ public abstract partial class EntityTree : Entity, IEnti } /// 父级节点名 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual String? ParentNodeName { get @@ -139,7 +139,7 @@ public abstract partial class EntityTree : Entity, IEnti /// 树形节点名,根据深度带全角空格前缀 [DisplayName("节点名")] - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual String TreeNodeName { get @@ -156,7 +156,7 @@ public abstract partial class EntityTree : Entity, IEnti } /// 树形节点名,根据深度带全角空格前缀 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual String TreeNodeName2 { get @@ -173,7 +173,7 @@ public abstract partial class EntityTree : Entity, IEnti } /// 树形节点名,根据深度带全角空格前缀 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public virtual String TreeNodeText { get @@ -195,11 +195,11 @@ public abstract partial class EntityTree : Entity, IEnti } /// 斜杠分隔的全路径 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public String FullPath => @"\" + GetFullPath(true); /// 斜杠分隔的全父路径 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public String FullParentPath => @"\" + GetFullPath(false); #endregion diff --git a/XCode/XCode.csproj b/XCode/XCode.csproj index 038b42034..66b2e60e3 100644 --- a/XCode/XCode.csproj +++ b/XCode/XCode.csproj @@ -46,7 +46,7 @@ - + @@ -81,6 +81,7 @@ + diff --git a/XUnitTest.XCode/Code/MemberSectionTests.cs b/XUnitTest.XCode/Code/MemberSectionTests.cs index 0dea80db6..293f5f4d5 100644 --- a/XUnitTest.XCode/Code/MemberSectionTests.cs +++ b/XUnitTest.XCode/Code/MemberSectionTests.cs @@ -146,15 +146,15 @@ public class MemberSectionTests #region 扩展属性 /// 顶级根。它的Childs就是各个省份 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public static Area Root { get; } = new Area(); /// 父级 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Area Parent => Extends.Get(nameof(Parent), k => FindByID(ParentID) ?? Root); /// 所有父级 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList AllParents => Extends.Get(nameof(AllParents), k => { var list = new List(); @@ -200,11 +200,11 @@ public class MemberSectionTests } /// 下级地区 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList Childs => Extends.Get(nameof(Childs), k => FindAllByParentID(ID).Where(e => e.Enable).ToList()); /// 子孙级区域。支持省市区,不支持乡镇街道 - [XmlIgnore, ScriptIgnore] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList AllChilds => Extends.Get(nameof(AllChilds), k => { var list = new List(); @@ -408,13 +408,13 @@ public class MemberSectionTests var code = """ #region 扩展属性 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public Product Product => Extends.Get(nameof(Product), k => Product.FindById(ProductId)); [Map(nameof(ProductId), typeof(Product), "Id")] public String ProductName => Product?.Name; - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public DeviceGroup Group => Extends.Get(nameof(Group), k => DeviceGroup.FindById(GroupId)); [Map(nameof(GroupId), typeof(DeviceGroup), "Id")] @@ -425,7 +425,7 @@ public class MemberSectionTests public String AreaName => Area.FindByIDs(CityId, ProvinceId)?.Path; /// 父级设备 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] //[ScriptIgnore] public Device Parent => Extends.Get(nameof(Parent), k => Device.FindById(ParentId)); @@ -434,18 +434,18 @@ public class MemberSectionTests public String ParentName => Parent?.Name; /// 子设备。借助扩展属性缓存 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList Childs => Extends.Get(nameof(Childs), k => FindAllByParent(Id)); /// 设备属性。借助扩展属性缓存 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList Properties => Extends.Get(nameof(Properties), k => DeviceProperty.FindAllByDeviceId(Id)); /// 设备服务。借助扩展属性缓存 - [XmlIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] public IList Services => Extends.Get(nameof(Services), k => DeviceService.FindAllByDeviceId(Id)); - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] protected Boolean IgnoreVaild { get; set; } = false; #endregion """; diff --git a/XUnitTest.XCode/Code/entity_user_normal.cs b/XUnitTest.XCode/Code/entity_user_normal.cs index 001860233..2c0dbd470 100644 --- a/XUnitTest.XCode/Code/entity_user_normal.cs +++ b/XUnitTest.XCode/Code/entity_user_normal.cs @@ -287,7 +287,7 @@ public partial class User private String? _Ex6; /// 扩展6 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] [Category("扩展")] [DisplayName("扩展6")] [Description("扩展6")] diff --git a/XUnitTest.XCode/DataAccessLayer/MySqlTests.cs b/XUnitTest.XCode/DataAccessLayer/MySqlTests.cs index 006d60811..0a1af88b8 100644 --- a/XUnitTest.XCode/DataAccessLayer/MySqlTests.cs +++ b/XUnitTest.XCode/DataAccessLayer/MySqlTests.cs @@ -152,6 +152,34 @@ public class MySqlTests 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] public void TablePrefixTest() { diff --git a/XUnitTest.XCode/DataAccessLayer/SQLiteTests.cs b/XUnitTest.XCode/DataAccessLayer/SQLiteTests.cs index 80effd84e..c1309530f 100644 --- a/XUnitTest.XCode/DataAccessLayer/SQLiteTests.cs +++ b/XUnitTest.XCode/DataAccessLayer/SQLiteTests.cs @@ -95,6 +95,27 @@ public class SQLiteTests Assert.Equal(dal1, dal2); Assert.Equal("sysSQLite", dal2.ConnName); 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] diff --git a/XUnitTest.XCode/Membership/AreaTests.cs b/XUnitTest.XCode/Membership/AreaTests.cs index 9940819d3..8ad9f6350 100644 --- a/XUnitTest.XCode/Membership/AreaTests.cs +++ b/XUnitTest.XCode/Membership/AreaTests.cs @@ -40,16 +40,16 @@ public class AreaTests XTrace.WriteLine("北京"); r = Area.FindByName(0, "北京"); - Assert.Equal(17, r.AllChilds.Count); - Assert.Equal("北京/延庆", r.AllChilds[^1].Path); + Assert.Equal(17, r.GetAllChilds().Count); + Assert.Equal("北京/延庆", r.GetAllChilds()[^1].Path); XTrace.WriteLine("容县"); r = Area.FindByID(450921); Assert.Equal("容县", r.Name); - Assert.Equal(2, r.AllParents.Count); + Assert.Equal(2, r.GetAllParents().Count); Assert.Equal("广西/玉林", r.ParentPath); - Assert.Equal(15, r.AllChilds.Count); - Assert.Equal("广西/玉林/容县/容州", r.AllChilds[0].Path); + Assert.Equal(15, r.GetAllChilds().Count); + Assert.Equal("广西/玉林/容县/容州", r.GetAllChilds()[0].Path); var r2 = Area.FindByIDs(450921102, 450921, 450900, 450000); 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] public async void Download() { diff --git a/XUnitTest.XCode/TestEntity/用户.cs b/XUnitTest.XCode/TestEntity/用户.cs index 6d0d996c0..c0264e79f 100644 --- a/XUnitTest.XCode/TestEntity/用户.cs +++ b/XUnitTest.XCode/TestEntity/用户.cs @@ -217,7 +217,7 @@ public partial class User2 private String _Ex6; /// 扩展6 - [XmlIgnore, ScriptIgnore, IgnoreDataMember] + [XmlIgnore, IgnoreDataMember, ScriptIgnore] [DisplayName("扩展6")] [Description("扩展6")] [DataObjectField(false, false, true, 50)] diff --git a/XUnitTest.XCode/XUnitTest.XCode.csproj b/XUnitTest.XCode/XUnitTest.XCode.csproj index 929df4b18..0d75f7c7c 100644 --- a/XUnitTest.XCode/XUnitTest.XCode.csproj +++ b/XUnitTest.XCode/XUnitTest.XCode.csproj @@ -84,13 +84,13 @@ - - - + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive