递归同步AD账号

思路:因为AD本身就是一棵树,而且.Net Framework中提供了对AD的操作对象(DirectoryEntry).该对象包含children和parent属性.所以利用这些属性使用递归算法可以批量生成Insert语句.获得这些SQL语句后,你就可以按照你想要的方式来执行了.

假设你的部门表有三个字段:DeptID,DeptName,ParentDeptID.根据一般情况,DeptID分类两种类型:整型和GUID类型.DeptName为字符型,ParentDeptID跟DeptID一致.

先说DeptID为GUID类型:

这种情况比较简单,因为window os 中的AD存储时,默认使用GUID作为唯一标识.而且DirectoryEntry对象正好包含GUID属性.这样我们就可以方便的遍历出AD中的所有项,而且因为使用递归算法,遍历完生成的SQL已经包含了层级关系.看代码:


static StringBuilder sbDepts=new StringBuilder ();

public static string GetBatchSQLFromSRC(DirectoryEntry entry) {

    DirectoryEntries entries 
= entry.Children;
    
foreach (DirectoryEntry item in entries){
        sbDepts.Append(
string.Format("INSERT INTO Ts_Dept(DeptID,DeptName,ParentDeptID) VALUES <br /> ({0},'{1}','{2}') <br />", item.Guid, item.Name, entry.Guid));

                GetBatchSQLFromSRC(item, intchildDeptID);   
//递归调用

}
    
return sbDepts.ToString();
}

这个方法返回一个批量的SQL,可以使用事物来执行这个方法。

再说DeptID为整型的情况:

这种情况稍微复杂,因为递归方法本身的特点,一旦某个节点既有子节点又有兄弟节点(确切的说是弟节点),那么就会出现重复的DeptID。为了避免这种情况,需要额外的声明一个静态变量单独存储递归过程中的DeptID最大值。然后使用这个静态变量在递归过程中传递就可以避免重号。


static StringBuilder sbDepts=new StringBuilder ();

static int intchildDeptID = 10002;//可做成配置,具体值可以根据实际情况设定

public static string GetBatchSQLFromSRC(DirectoryEntry entry,int pid) {

    DirectoryEntries entries 
= entry.Children;
   intchildDeptID 
= pid == intchildDeptID ? pid + 1 : intchildDeptID;//避免重号
    foreach (DirectoryEntry item in entries){
             sbDepts.Append(
string.Format("INSERT INTO Ts_Dept(DeptID,DeptName,ParentID) VALUES ({0},'{1}',{2}) <br />", intchildDeptID, item.Name, pid));
        GetBatchSQLFromSRC(item, intchildDeptID); 
//递归调用
         }
    
return sbDepts.ToString();
}

下面两个是辅助方法。第一个用来获取指定LDAP路径下的所有节点,另一个是在第一个方法中找特定的节点。

/// <summary>
/// 获取AD中指定目录下所有单位
/// </summary>
/// <param name="filter">DirectorySearcher的过滤条件</param>
/// <returns></returns>
public static SearchResultCollection GetADDepts(string filter) {
    DirectoryEntry entry 
= new DirectoryEntry(ADDeptPath);
    
if (entry != null) {
        DirectorySearcher sercher;
        
try{
            sercher 
= new DirectorySearcher(entry);
            sercher.Filter 
= filter;
        }
        
catch(Exception ex){
            
throw ex;
        }
        
return sercher.FindAll();
    }
    
return null;
}

private static readonly string ADDeptPath = ConfigurationManager.AppSettings["ADDept"];//要获取的AD账号的域。

Config中的配置格式格式:

<add key="ADDept" value="LDAP://OU=Company,DC=caini,DC=ac,DC=cn"/><!--要同步的单位在AD中的路径-->

 

/// <summary>
/// 获取指定LDAP路径对应的AD对象
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static DirectoryEntry GetDirectotyEntryInCollection(string path) {
    
foreach (SearchResult result in GetADDepts()) {
        
if (result.Path == path){             
            
return result.GetDirectoryEntry();
        }
    }
    
return null;
}

为了方便客户端代码调用,可以把递归方法封装一下。下面只封装了GUID类型的,整型的大家可以自己试着封装一下。如下:

/// <summary>
/// 从域中获取
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static string GetBatchSQLFromSRC(string path) {
    sbDepts.Length 
= 0;
    
//SearchResult result = GetSearchResultInCollection(path);
    DirectoryEntry entry = GetDirectotyEntryInCollection(path);
    
int rootdeptid = 10001;
    
//return GetBatchSQLFromSRC(entry);
    return GetBatchSQLFromSRC(entry, rootdeptid);
}

这样客户端代码只要传递一个合法的LDAP路径,就可以获取该路径下所有的节点。

例如:

protected void Page_Load(object sender, EventArgs e)
{
    
if (!this.IsPostBack) {
        
string path = "LDAP://OU=NewDept1,OU=Company,DC=caini,DC=ac,DC=cn";
        
string ret = ADHelper.GetBatchSQLFromSRC(path);
        Response.Clear();
        Response.Write(ret);
        Response.End();
    }
}

注意:以上代码要引入

1.using System.DirectoryServices;命名空间
2.注意红体部门,为了灵活在web.config中做了配置

以上代码要引入

至于性能这块暂时还没考虑,如果大家有什么更好的办法请不吝赐教。

原文地址:https://www.cnblogs.com/jjhe369/p/2042032.html