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
-
+
@@ -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