构造AD组织结构(Organizational Unit)的树形结构的另一个方法

前端之家收集整理的这篇文章主要介绍了构造AD组织结构(Organizational Unit)的树形结构的另一个方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

这是构造AD组织结构(Organizational Unit,以下称为OU)的树形结构的另一个方法。之前的方法请参见:递归构造AD组织结构(Organizational Unit)的树形结构
需要引用两个dll:

using System.DirectoryServices;  
using System.DirectoryServices.ActiveDirectory; 

定义一个树节点类:

    public class OuTreeNode
    {
        public string Name { get; set; }
        public string Id { get; set; }
        private List<OuTreeNode> _children = new List<OuTreeNode>();

        public List<OuTreeNode> Children
        {
            get { return _children; }
            set { _children = value; }
        }
    }
这个类的名字是OuTreeNode,其中保存OU节点的基本信息,这里定义了OU的name,id,和子节点的列表。

与之前递归查找OU的方法不同,这个方法使用查询,一次查询出所有的OU,然后遍历这些OU就可以构造出一棵组织结构树。基本的思路是,利用OU对象的distinguishedName构造出一个OU相对于根节点的相对路径,这个路径正好可以作为一个xml表的xpath,根据这个xpath,就可以构造一个xml,最后通过xml表递归构造OU树。这是对xml查询的反向应用,一般都是给定一个xml表,使用xpath查询其中的节点,这次是使用xpath,构造一个xml表。

首先我们需要一个方法,将一个OU的distinguishedName转换成相对路径:

        private static string FormatDNToRelativePath(string rawDn)
        {
            StringBuilder path = new StringBuilder();
            rawDn = rawDn.Replace(" ","_").Replace("&","_");
            string[] dnParts = rawDn.Split(new[] { ',' });
            foreach (string part in dnParts)
            {
                if (part.StartsWith("OU",StringComparison.OrdinalIgnoreCase))
                {
                    string name = XmlConvert.EncodeName((part.Split(new char[] {'='})[1]));
                    path.Insert(0,string.Format("/{0}",name));
                }
            }
            string domainName = string.Empty;
            using (Domain root = Domain.GetCurrentDomain())
            {
                domainName = root.Name;
            }
            path.Insert(0,string.Format("{0}",domainName));
            return path.ToString();
        }
这个方法可以将形如“OU=Office1,OU=Dev Department,DC=Test,DC=com”的转换为Test.Com\Dev Department\Office1这样的相对路径,借助于xml表来构造OU树,会有字符转义的问题,在AD中OU的名字可以使用任意的字符,但是xml不行,因此要对OU的名字进行转义,这里使用XmlConvert.EncodeName方法来做到这一点,之后在构造树的时候,再转义回来。

我们需要获取OU的信息,来展示给最终用户,这里作为示例,只获取OU的name和id,其中name也需要转义,因为name会作为xml的一个属性保存:

        private static string GetName(DirectoryEntry entry)
        {
            return XmlConvert.EncodeLocalName(entry.Properties["name"].Value.ToString());
        }

        private static string GetId(DirectoryEntry entry)
        {
            byte[] bytes = entry.Properties["ObjectGuid"].Value as byte[];
            Guid id = new Guid(bytes);
            return id.ToString();
        }
然后就是获得每个OU的相对路径,以及这个OU的父节点的相对路径,这个路径可以看成是xpath,用来将OU节点添加到xml中的正确位置:
        private static string GetPath(DirectoryEntry entry)
        {
            string xPath = string.Format("/{0}",FormatDNToRelativePath(entry.Properties["distinguishedName"].Value.ToString()));
            return xPath;

        }

        private static string GetParentPath(DirectoryEntry entry,string name)
        {
            string path = FormatDNToRelativePath(entry.Properties["distinguishedName"].Value.ToString());
            if (path.LastIndexOf(name) != -1)
            {
                path = path.Remove(path.LastIndexOf(name));
            }

            string parentPath = string.Format("/{0}",path);
            parentPath = parentPath.Substring(0,parentPath.LastIndexOf('/'));

            return parentPath;
        }
有个这些方法,就可以开始构造OU树了,下面是代码,具体的步骤详见注释:
        public static OuTreeNode GetOuTree()
        {
            XmlDocument xmlDoc = new XmlDocument(); //声明一个XmlDocument
            XmlElement rootElement = null;
            string domainName = string.Empty;
            using (Domain domain = Domain.GetCurrentDomain())
            {
                rootElement = xmlDoc.CreateElement(domain.Name);
                xmlDoc.AppendChild(rootElement); //将domain节点作为根节点

                using (DirectorySearcher ds = new DirectorySearcher(domain.GetDirectoryEntry(),"(objectClass=organizationalUnit)",null,SearchScope.Subtree))
                {
                    SearchResultCollection src = ds.FindAll(); //使用查询,一次性获取所有的OU
                    
                    foreach (SearchResult result in src)  //遍历OU
                    {
                        using (DirectoryEntry ouEntry = result.GetDirectoryEntry())
                        {
                            try
                            {
                                string nodeName = GetName(ouEntry); //获得OU的name
                                string nodeId = GetId(ouEntry);  //获得OU的guid
                                string xPathParent = GetParentPath(ouEntry,nodeName); //获取OU的父节点的相对路径
                                string xPath = GetPath(ouEntry); //获取OU的相对路径

                                if (xmlDoc.SelectSingleNode(xPath) == null)  //判断当前的OU,是否在xml中存在,如果不存在就创建一个节点
                                {
                                    XmlElement newElement = xmlDoc.CreateElement(nodeName); //创建OU节点
                                    newElement.SetAttribute("name",nodeName);  //将name作为一个属性保存
                                    newElement.SetAttribute("guid",nodeId);    //将gud作为一个属性保存
                                    XmlNode parent = xmlDoc.SelectSingleNode(xPathParent); //查找父节点是否存在,如果存在就将当前的OU节点append上去
                                    if (parent != null)
                                        parent.AppendChild(newElement);
                                }
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }

            OuTreeNode tree = BuildOuTree(xmlDoc); //根据xml文件构建OU树
            return tree;
        }    

根据之前构造的xml文档,递归构造一棵树:
        private static OuTreeNode BuildOuTree(XmlDocument tempXml)
        {
            OuTreeNode root = new OuTreeNode();
            using (Domain domain = Domain.GetCurrentDomain())
            {
                root.Name = domain.Name;
            }
            GetOUTreeNode(root,tempXml.FirstChild);
            return root;
        }


        private static void GetOUTreeNode(OuTreeNode root,XmlNode tempXml)
        {
            foreach (XmlNode xmlNode in tempXml.ChildNodes)
            {
                OuTreeNode childNode = new OuTreeNode() { Name = XmlConvert.DecodeName(xmlNode.Attributes["name"].Value),Id = xmlNode.Attributes["guid"].Value }; //将转义的name属性转义回来。
                root.Children.Add(childNode);
                GetOUTreeNode(childNode,xmlNode);
            }
        }
使用这种方法的好处就是一次获取所有的OU,减少访问AD的次数,适用于访问AD较慢的情况,但是这个方法是有一个问题的,就是会丢OU,因为在构建xml的时候,有可能父节点还没有创建出来,就处理子节点了,为了解决这个问题,可以将查询出来的OU列表,先做一下排序,根据“canonicalName”属性排序就可以确保父节点一定会在子节点添加之前就存在了。

另外,如果把这个方法稍微改一下,就可以用来导出一个OU结构的xml文件,可以用在AD和其他系统的数据交换中。

猜你在找的XML相关文章