递归同步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中做了配置
以上代码要引入
至于性能这块暂时还没考虑,如果大家有什么更好的办法请不吝赐教。
posted on 2011-05-10 12:43 我每天都在进步o(∩_∩)o... 阅读(1190) 评论(6) 编辑 收藏 举报