using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using NewLife.Exceptions;
using NewLife.Reflection;
#if NET4
using System.Linq;
#else
using NewLife.Linq;
#endif
namespace XCode
{
/// <summary>主键为整型的实体树基类</summary>
/// <typeparam name="TEntity"></typeparam>
public class EntityTree<TEntity> : EntityTree<Int32, TEntity> where TEntity : EntityTree<TEntity>, new()
{ }
/// <summary>实体树基类,具有树形结构的实体继承该类即可得到各种树操作功能</summary>
/// <typeparam name="TKey">主键类型</typeparam>
/// <typeparam name="TEntity">实体类型</typeparam>
public abstract class EntityTree<TKey, TEntity> : Entity<TEntity>, IEntityTree where TEntity : EntityTree<TKey, TEntity>, new()
{
#region 构造
//static EntityTree()
//{
// Meta.OnDataChange += delegate { Root = null; };
//}
#endregion
#region 设置型属性
/// <summary>关联键名称,一般是主键,如ID</summary>
protected virtual String KeyName { get { return Meta.Unique.Name; } }
/// <summary>关联父键名,一般是Parent加主键,如ParentID</summary>
protected virtual String ParentKeyName
{
get
{
String name = "Parent" + KeyName;
//if (Meta.FieldNames.Contains(name)) return name;
// 不区分大小写的比较
if (Meta.FieldNames.Contains(name, StringComparer.OrdinalIgnoreCase)) return name;
return null;
}
}
private static String _SortingKeyName;
/// <summary>排序字段,默认是"Sorting", "Sort", "Rank"之一</summary>
protected virtual String SortingKeyName
{
get
{
if (_SortingKeyName == null)
{
// Empty与null不同,可用于区分是否已计算
_SortingKeyName = String.Empty;
String[] names = new String[] { "Sorting", "Sort", "Rank" };
IList<String> fs = Meta.FieldNames;
foreach (String name in names)
{
//if (fs.Contains(name))
// 不区分大小写的比较
if (fs.Contains(name, StringComparer.OrdinalIgnoreCase))
{
_SortingKeyName = name;
break;
}
}
}
return _SortingKeyName;
}
}
/// <summary>名称键名,如Name,否则使用第二个字段</summary>
/// <remarks>影响NodeName、TreeNodeName、TreeNodeName2、FindByPath、GetFullPath、GetFullPath2等</remarks>
protected virtual String NameKeyName { get { return Meta.FieldNames.Contains("Name", StringComparer.OrdinalIgnoreCase) ? "Name" : Meta.Fields.Where(f => !f.IsIdentity).FirstOrDefault().Name; } }
/// <summary>是否缓存Childs、AllChilds、Parent等</summary>
protected virtual Boolean EnableCaching { get { return true; } }
#endregion
#region 扩展属性
/// <summary>排序值</summary>
private Int32 Sort
{
get { return String.IsNullOrEmpty(SortingKeyName) ? 0 : (Int32)this[SortingKeyName]; }
set { if (!String.IsNullOrEmpty(SortingKeyName) && (Int32)this[SortingKeyName] != value) SetItem(SortingKeyName, value); }
}
/// <summary>子节点</summary>
public virtual EntityList<TEntity> Childs
{
get { return EnableCaching ? GetExtend<EntityList<TEntity>>("Childs", e => FindChilds(), !IsNull((TKey)this[KeyName])) : FindChilds(); }
set { SetExtend("Childs", value); }
}
/// <summary>子节点</summary>
protected virtual EntityList<TEntity> FindChilds() { return FindAllByParent((TKey)this[KeyName]); }
/// <summary>父节点</summary>
[XmlIgnore]
public virtual TEntity Parent
{
get { return EnableCaching ? GetExtend<TEntity>("Parent", e => FindParent()) : FindParent(); }
set { SetExtend("Parent", value); }
}
/// <summary>父节点</summary>
protected virtual TEntity FindParent() { return Meta.Cache.Entities.Find(KeyName, this[ParentKeyName]); }
/// <summary>父节点</summary>
protected static TEntity FindByKeyWithCache(TKey key)
{
return Meta.Cache.Entities.Find((Meta.Factory.Default as TEntity).KeyName, key);
}
/// <summary>子孙节点</summary>
[XmlIgnore]
public virtual EntityList<TEntity> AllChilds
{
get { return EnableCaching ? GetExtend<EntityList<TEntity>>("AllChilds", e => FindAllChilds(this), !IsNull((TKey)this[KeyName])) : FindAllChilds(this); }
set { SetExtend("AllChilds", value); }
}
/// <summary>父节点集合</summary>
[XmlIgnore]
public virtual EntityList<TEntity> AllParents
{
get { return EnableCaching ? GetExtend<EntityList<TEntity>>("AllParents", e => FindAllParents(this)) : FindAllParents(this); }
set { SetExtend("AllParents", value); }
}
/// <summary>深度</summary>
[XmlIgnore]
public virtual Int32 Deepth
{
get
{
if (IsNull((TKey)this[KeyName])) return 0;
Int32 _Deepth = 1;
var list = AllParents;
if (list != null && list.Count > 0) _Deepth += list.Count;
return _Deepth;
}
}
private static TEntity _Root;
/// <summary>根</summary>
public static TEntity Root
{
get
{
if (_Root == null)
{
_Root = new TEntity();
Meta.OnDataChange += delegate { _Root = null; };
}
return _Root;
}
set { _Root = null; }
}
/// <summary>节点名</summary>
[XmlIgnore]
public virtual String NodeName
{
get
{
String key = NameKeyName;
if (String.IsNullOrEmpty(key)) return String.Empty;
return (String)this[key];
}
}
/// <summary>树形节点名,根据深度带全角空格前缀</summary>
[XmlIgnore]
public virtual String TreeNodeName
{
get
{
String key = NameKeyName;
if (String.IsNullOrEmpty(key)) return String.Empty;
Int32 d = Deepth;
if (d <= 0) return String.Empty;
return new String(' ', (d - 1) * 2) + this[key];
}
}
/// <summary>树形节点名,根据深度带全角空格前缀</summary>
[XmlIgnore]
public virtual String TreeNodeName2
{
get
{
String key = NameKeyName;
if (String.IsNullOrEmpty(key)) return String.Empty;
Int32 d = Deepth;
if (d <= 0) return "|- 根";
return new String(' ', d) + "|- " + this[key];
}
}
/// <summary>斜杠分隔的全路径</summary>
[XmlIgnore]
public String FullPath { get { return GetFullPath2(true); } }
/// <summary>斜杠分隔的全父路径</summary>
[XmlIgnore]
public String FullParentPath { get { return GetFullPath2(false); } }
#endregion
#region 查询
/// <summary>根据父级查找所有子级,带排序功能</summary>
/// <param name="parentKey"></param>
/// <returns></returns>
[DataObjectMethod(DataObjectMethodType.Select)]
public static EntityList<TEntity> FindAllByParent(TKey parentKey)
{
TEntity entity = Meta.Factory.Default as TEntity;
EntityList<TEntity> list = Meta.Cache.Entities.FindAll(entity.ParentKeyName, parentKey);
// 如果是顶级,那么包含所有无头节点
if (IsNull(parentKey))
{
EntityList<TEntity> noParents = FindAllNoParent();
if (noParents != null && noParents.Count > 0)
{
if (list == null || list.Count < 1)
list = noParents;
else
list.AddRange(noParents);
}
}
if (list == null) return new EntityList<TEntity>();
if (list.Count < 1) return list;
if (!String.IsNullOrEmpty(entity.SortingKeyName))
{
list.Sort(delegate(TEntity item1, TEntity item2)
{
if (item1.Sort != item2.Sort)
return -1 * item1.Sort.CompareTo(item2.Sort);
else
return (item1[entity.KeyName] as IComparable).CompareTo(item2[entity.KeyName]);
});
}
return list;
}
/// <summary>查找所有没有父节点的节点集合(其实就是父节点已经被删掉了的非法节点)</summary>
/// <returns></returns>
public static EntityList<TEntity> FindAllNoParent()
{
TEntity entity = Meta.Factory.Default as TEntity;
EntityList<TEntity> list = new EntityList<TEntity>();
foreach (TEntity item in Meta.Cache.Entities)
{
// 有父节点的跳过
if (item.Parent != null) continue;
// 父节点为空的跳过
if (IsNull((TKey)item[entity.ParentKeyName])) continue;
list.Add(item);
}
return list;
}
/// <summary>查找指定键的所有子节点,以深度层次树结构输出,包括当前节点,并作为根节点</summary>
/// <param name="parentKey"></param>
/// <returns></returns>
[DataObjectMethod(DataObjectMethodType.Select)]
public static EntityList<TEntity> FindAllChildsByParent(TKey parentKey)
{
TEntity entity = FindByKeyWithCache(parentKey);
if (entity == null) entity = Root;
EntityList<TEntity> list = FindAllChilds(entity);
list.Insert(0, entity);
return list;
}
/// <summary>查找指定键的所有子节点,以深度层次树结构输出</summary>
/// <param name="parentKey"></param>
/// <returns></returns>
[DataObjectMethod(DataObjectMethodType.Select)]
public static EntityList<TEntity> FindAllChildsNoParent(TKey parentKey)
{
TEntity entity = FindByKeyWithCache(parentKey);
if (entity == null) entity = Root;
return FindAllChilds(entity);
}
/// <summary>查找指定键的所有父节点,从高到底以深度层次树结构输出</summary>
/// <param name="key"></param>
/// <returns></returns>
[DataObjectMethod(DataObjectMethodType.Select)]
public static EntityList<TEntity> FindAllParentsByKey(TKey key)
{
TEntity entity = FindByKeyWithCache(key);
if (entity == null) entity = Root;
return FindAllParents(entity);
}
#endregion
#region 树形计算
/// <summary>查找指定节点的所有子节点,以深度层次树结构输出</summary>
/// <param name="entity"></param>
/// <returns></returns>
protected static EntityList<TEntity> FindAllChilds(IEntityTree entity)
{
if (entity == null) return new EntityList<TEntity>();
IEntityList childlist = entity.Childs;
if (childlist == null) return new EntityList<TEntity>();
EntityList<TEntity> list = new EntityList<TEntity>();
// 使用队列而不使用递归,避免死循环
// 使用队列而不使用堆栈,因为树的构造一般是深度搜索而不是广度搜索
Stack<TEntity> stack = new Stack<TEntity>();
stack.Push(entity as TEntity);
while (stack.Count > 0)
{
TEntity item = stack.Pop();
if (list.Contains(item)) continue;
list.Add(item);
EntityList<TEntity> childs = item.Childs;
if (childs == null || childs.Count < 1) continue;
// 反向入队
for (int i = childs.Count - 1; i >= 0; i--)
{
// 已计算到结果的,不再处理
if (list.Contains(childs[i])) continue;
// 已进入待处理队列的,不再处理
if (stack.Contains(childs[i])) continue;
stack.Push(childs[i]);
}
}
// 去掉第一个,那是自身
list.RemoveAt(0);
return list;
}
/// <summary>查找指定节点的所有父节点,从高到底以深度层次树结构输出</summary>
/// <param name="entity"></param>
/// <returns></returns>
protected static EntityList<TEntity> FindAllParents(IEntityTree entity)
{
if (entity == null || entity.Parent == null) return new EntityList<TEntity>();
EntityList<TEntity> list = new EntityList<TEntity>();
TEntity item = entity as TEntity;
while (item != null)
{
// 形成了死循环,就此中断
if (list.Contains(item)) break;
list.Add(item);
item = item.Parent;
}
// 去掉第一个自己
list.RemoveAt(0);
// 反转
if (list.Count > 0) list.Reverse();
return list;
}
/// <summary>根据层次路径查找</summary>
/// <param name="path">层次路径</param>
/// <param name="keys">用于在每一层匹配实体的键值,默认是NameKeyName</param>
/// <returns></returns>
public TEntity FindByPath(String path, params String[] keys)
{
if (String.IsNullOrEmpty(path)) return null;
if (keys == null || keys.Length < 1)
{
if (String.IsNullOrEmpty(NameKeyName)) return null;
keys = new String[] { NameKeyName };
}
EntityList<TEntity> list = Childs;
if (list == null || list.Count < 1) return null;
//// 尝试一次性查找
//TEntity entity = list.Find(name, path);
//if (entity != null) return entity;
String[] ss = path.Split(new Char[] { '.', '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
if (ss == null || ss.Length < 1) return null;
// 找第一级
TEntity entity = null;
foreach (String item in keys)
{
entity = list.Find(item, ss[0]);
if (entity != null) break;
}
if (entity == null) return null;
// 是否还有下级
if (ss.Length == 1) return entity;
// 递归找下级
return entity.FindByPath(String.Join("\\", ss, 1, ss.Length - 1), keys);
}
#endregion
#region 集合运算
/// <summary>是否包含子节点</summary>
/// <param name="key">子节点键值</param>
/// <returns></returns>
public Boolean Contains(TKey key)
{
// 判断空
if (IsNull(key)) return false;
// 自身
if (Object.Equals((TKey)this[KeyName], key)) return true;
// 子级
var list = Childs;
if (list != null && list.Exists(KeyName, key)) return true;
// 子孙
list = AllChilds;
if (list != null && list.Exists(KeyName, key)) return true;
return false;
}
/// <summary>子级键值集合</summary>
[XmlIgnore]
public List<TKey> ChildKeys
{
get
{
var list = Childs;
if (list == null || list.Count < 1) return new List<TKey>();
return list.GetItem<TKey>(KeyName);
}
}
/// <summary>逗号分隔的子级键值字符串,一般可用于SQL语句中</summary>
[XmlIgnore]
public String ChildKeyString
{
get
{
List<TKey> list = ChildKeys;
if (list == null || list.Count < 1) return null;
StringBuilder sb = new StringBuilder();
foreach (TKey item in list)
{
if (sb.Length > 0) sb.Append(",");
sb.Append(item.ToString());
}
return sb.ToString();
}
}
/// <summary>子孙键值集合</summary>
[XmlIgnore]
public List<TKey> AllChildKeys
{
get
{
var list = AllChilds;
if (list == null || list.Count < 1) return new List<TKey>();
return list.GetItem<TKey>(KeyName);
}
}
/// <summary>逗号分隔的子孙键值字符串,一般可用于SQL语句中</summary>
[XmlIgnore]
public String AllChildKeyString
{
get
{
List<TKey> list = AllChildKeys;
if (list == null || list.Count < 1) return null;
StringBuilder sb = new StringBuilder();
foreach (TKey item in list)
{
if (sb.Length > 0) sb.Append(",");
sb.Append(item.ToString());
}
return sb.ToString();
}
}
#endregion
#region 业务
/// <summary>创建菜单树</summary>
/// <param name="nodes">父集合</param>
/// <param name="list">菜单列表</param>
/// <param name="url">格式化地址,可以使用{ID}和{Name}</param>
/// <param name="func">由菜单项创建树节点的委托</param>
public static void MakeTree(TreeNodeCollection nodes, EntityList<TEntity> list, String url, Func<TEntity, TreeNode> func)
{
if (list == null || list.Count < 1) return;
// 使用内层递归,避免死循环
MakeTree(nodes, list, url, func, new EntityList<TEntity>());
}
private static void MakeTree(TreeNodeCollection nodes, EntityList<TEntity> list, String url, Func<TEntity, TreeNode> func, EntityList<TEntity> parents)
{
String id = Meta.FieldNames[0];
String name = Meta.FieldNames[1];
if (Meta.FieldNames.Contains("Name")) name = "Name";
if (Meta.Unique != null)
id = Meta.Unique.Name;
else if (Meta.FieldNames.Contains("ID"))
id = "ID";
foreach (TEntity item in list)
{
if (parents.Contains(item) || parents.Exists(id, item[id])) continue;
parents.Add(item);
TreeNode node = null;
if (func == null)
{
node = new TreeNode((String)item[name]);
node.Value = "" + item[id];
if (!String.IsNullOrEmpty(url))
{
foreach (String elm in Meta.FieldNames)
{
url = url.Replace("{" + elm + "}", "" + item[elm]);
}
node.NavigateUrl = url;
}
}
else
{
node = func(item);
}
var list2 = item.Childs;
if (list2 != null && list2.Count > 0) MakeTree(node.ChildNodes, list2, url, func, parents);
if (node != null) nodes.Add(node);
}
}
/// <summary>取得全路径的实体,由上向下排序</summary>
/// <param name="includeSelf"></param>
/// <returns></returns>
public EntityList<TEntity> GetFullPath(Boolean includeSelf)
{
EntityList<TEntity> list = AllParents;
if (!includeSelf) return list;
// 绝对不能让list直接等于AllParents,否则后面会加一项进去,导致改变它的值
EntityList<TEntity> list2 = new EntityList<TEntity>();
if (list != null && list.Count > 0) list2.AddRange(list);
list2.Add(this as TEntity);
return list2;
}
/// <summary>取得全路径的实体,由上向下排序</summary>
/// <param name="includeSelf">是否包含自己</param>
/// <param name="separator">分隔符</param>
/// <param name="func">回调</param>
/// <returns></returns>
public String GetFullPath(Boolean includeSelf = true, String separator = @"\", Func<TEntity, String> func = null)
{
EntityList<TEntity> list = GetFullPath(includeSelf);
if (list == null || list.Count < 1) return null;
String namekey = NameKeyName;
StringBuilder sb = new StringBuilder();
foreach (TEntity item in list)
{
if (sb.Length > 0 && !String.IsNullOrEmpty(separator)) sb.Append(separator);
if (func != null)
sb.Append(func(item));
else
{
if (String.IsNullOrEmpty(namekey))
sb.Append(item.ToString());
else
sb.Append(item[namekey]);
}
}
return sb.ToString();
}
String GetFullPath2(Boolean includeSelf = true, String separator = @"\", Func<TEntity, String> func = null)
{
EntityList<TEntity> list = GetFullPath(includeSelf);
if (list == null || list.Count < 1) return separator;
String namekey = NameKeyName;
StringBuilder sb = new StringBuilder();
foreach (TEntity item in list)
{
if (!String.IsNullOrEmpty(separator)) sb.Append(separator);
if (func != null)
sb.Append(func(item));
else
{
if (String.IsNullOrEmpty(namekey))
sb.Append(item.ToString());
else
sb.Append(item[namekey]);
}
}
return sb.ToString();
}
/// <summary>删除子级到本级的关系。导出数据前可以先删除关系,以减少导出的大小</summary>
public virtual void ClearRelation()
{
var list = Childs;
if (list == null || list.Count < 1) return;
foreach (TEntity item in list)
{
item[KeyName] = default(TKey);
item[ParentKeyName] = default(TKey);
item.ClearRelation();
}
}
/// <summary>批量保存,保存整棵树</summary>
/// <param name="saveSelf">是否保存自己</param>
/// <returns></returns>
public virtual Int32 BatchSave(Boolean saveSelf)
{
Int32 count = 0;
Meta.BeginTrans();
try
{
EntityList<TEntity> list = Childs;
if (saveSelf) count += Save();
// 上面保存数据后,可能会引起扩展属性抖动(不断更新)
if (list != null && list.Count > 0)
{
foreach (TEntity item in list)
{
item[ParentKeyName] = this[KeyName];
count += item.BatchSave(true);
}
}
Meta.Commit();
return count;
}
catch
{
Meta.Rollback();
throw;
}
}
/// <summary>排序上升</summary>
public void Up()
{
EntityList<TEntity> list = FindAllByParent((TKey)this[ParentKeyName]);
if (list == null || list.Count < 1) return;
for (int i = 0; i < list.Count; i++)
{
Int32 s = list.Count - i;
// 当前项,排序增加。原来比较实体相等有问题,也许新旧实体类不对应,现在改为比较主键值
if (this.EqualTo(list[i])) s++;
// 下一项是当前项,排序减少
if (i < list.Count - 1 && this.EqualTo(list[i + 1])) s--;
list[i].Sort = s;
}
list.Save();
}
/// <summary>排序下降</summary>
public void Down()
{
EntityList<TEntity> list = FindAllByParent((TKey)this[ParentKeyName]);
if (list == null || list.Count < 1) return;
for (int i = 0; i < list.Count; i++)
{
Int32 s = list.Count - i;
// 当前项,排序减少
if (this.EqualTo(list[i])) s--;
// 上一项是当前项,排序增加
if (i >= 1 && this.EqualTo(list[i - 1])) s++;
list[i].Sort = s;
}
list.Save();
}
Boolean EqualTo(TEntity entity)
{
if (entity == null) return false;
Object v1 = this[KeyName];
Object v2 = entity[KeyName];
if (typeof(TKey) == typeof(String)) return "" + v1 == "" + v2;
return Object.Equals(v1, v2);
}
#endregion
#region 数据检查
/// <summary>验证树形数据是否有效</summary>
/// <param name="isNew">是否新数据</param>
public override void Valid(Boolean isNew)
{
base.Valid(isNew);
TKey key = (TKey)this[KeyName];
TKey pkey = (TKey)this[ParentKeyName];
Boolean isnull = IsNull(key);
Boolean pisnull = IsNull(pkey);
// 无主检查
//if (!pisnull && !Meta.Cache.Entities.Exists(KeyName, pkey)) throw new Exception("无效上级[" + pkey + "]!");
// 先检查实体缓存,可以命中绝大部分,因为绝大多数时候父级都存在
if (!pisnull && !Meta.Cache.Entities.Exists(KeyName, pkey) && FindCount(KeyName, pkey) <= 0) throw new XException("无效上级[" + pkey + "]!");
// 死循环检查
if (isnull)
{
// 插入状态,key为空,pkey可以是任何值
}
else
{
// 更新状态,且pkey不为空时,判断两者是否相等
if (!pisnull && Object.Equals(pkey, key)) throw new XException("上级不能是当前节点!");
}
// 编辑状态且设置了父节点时才处理
if (!isnull && !pisnull)
{
EntityList<TEntity> list = this.AllChilds;
if (list != null && list.Exists(KeyName, pkey))
throw new XException("上级[" + pkey + "]是当前节点的子孙节点!");
}
}
private static Boolean IsNull(TKey value)
{
// 为空或者默认值,返回空
if (value == null || Object.Equals(value, default(TKey))) return true;
// 字符串的空
if (typeof(TKey) == typeof(String) && String.IsNullOrEmpty(value.ToString())) return true;
return false;
}
#endregion
#region IEntityTree 成员
/// <summary>父实体</summary>
IEntity IEntityTree.Parent { get { return Parent; } }
/// <summary>子实体集合</summary>
IEntityList IEntityTree.Childs { get { return Childs; } }
/// <summary>子孙实体集合。以深度层次树结构输出</summary>
IEntityList IEntityTree.AllChilds { get { return AllChilds; } }
/// <summary>父亲实体集合。以深度层次树结构输出</summary>
IEntityList IEntityTree.AllParents { get { return AllParents; } }
#endregion
}
}
|