大家好,我正在将我的 VBScript 移植到 C#。我遇到了一个问题,即 Active Directory 属性检索在 C# 中要慢得多。
这是我不完整的C#代码
foreach(string s in dictLast.Keys)
{
if(s.Contains("/"))
str = s.Insert(s.IndexOf('/'), "\\");
else
str = s;
dEntry = new DirectoryEntry("LDAP://" + str);
strUAC = dEntry.Properties["userAccountControl"].Value.ToString();
cmd.CommandText = "INSERT INTO [NOW](readTime) VALUES(\"" + test.Elapsed.Milliseconds.ToString() + "\")";
cmd.ExecuteNonQuery();
test.Reset();
test.Start();
}
如果我注释掉这一行。 strUAC = dEntry.Properties["userAccountControl"].Value.ToString();
它以 11 秒的速度运行。但如果我不这样做,它会以 2 分 35 秒的速度运行。记录数为 3700。平均每条记录运行 50 秒。我正在使用秒表类。
我的 VBscript 运行时间仅为 39 秒(使用时差)。每条记录为 0 或 15 毫秒。我正在使用 Timer() 的差异。
这是我的 VBscript
strAttributes = "displayName, pwdLastSet, whenCreated, whenChanged, userAccountControl"
For Each strUser In objList.Keys
prevTime = Timer()
strFilter = "(sAMAccountName=" & strUser & ")"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
Set adoRecordset = adoCommand.Execute
On Error Resume Next
If (adoRecordset.Fields("displayName") = null) Then
strCN = "-"
Else
strCN = adoRecordset.Fields("displayName")
End If
If (Err.Number <> 0) Then
MsgBox(strUser)
End If
strCr8 = DateAdd("h", 8, adoRecordset.Fields("whenCreated"))
strUAC = adoRecordset.Fields("userAccountControl")
If (strUAC AND ADS_UF_DONT_EXPIRE_PASSWD) Then
strPW = "Never expires"
Else
If (TypeName(adoRecordset.Fields("pwdLastSet").Value) = "Object") Then
Set objDate = adoRecordset.Fields("pwdLastSet").Value
dtmPwdLastSet = Integer8Date(objDate, lngBias)
Else
dtmPwdLastSet = #1/1/1601#
End If
If (dtmPwdLastSet = #1/1/1601#) Then
strPW = "Must Change at Next Logon"
Else
strPW = DateAdd("d", sngMaxPwdAge, dtmPwdLastSet)
End If
End If
retTime = Timer() - prevTime
If (objList.Item(strUser) = #1/1/1601#) Then
Wscript.Echo strCN & ";" & strUser & ";" & strPW & ";" & strCr8 & ";" & ObjChange.Item(strUser) & ";0;" & strUAC & ";" & retTime
Else
Wscript.Echo strCN & ";" & strUser & ";" & strPW & ";" & strCr8 & ";" & ObjChange.Item(strUser) & ";" & objList.Item(strUser) & ";" & strUAC & ";" & retTime
End If
Next
有什么想法吗? 如果我没有提供足够的信息,请告诉我。谢谢。
DirectorySearcher 方式。 1 分 8 秒。
dEntry = new DirectoryEntry("LDAP://" + strDNSDomain);
string[] strAttr = {"userAccountControl"};
foreach(string s in dictLast.Keys)
{
if(s.Contains("/"))
str = s.Insert(s.IndexOf('/'), "\\");
else
str = s;
ds = new DirectorySearcher(de, "(sAMAccountName=" + s + ")", strAttr, SearchScope.Subtree);
ds.PropertiesToLoad.Add("userAccountControl");
SearchResult rs = ds.FindOne();
strUAC = rs.Properties["userAccountControl"][0].ToString();
cmd.CommandText = "INSERT INTO [NOW](readTime) VALUES(\"" + test.Elapsed.Milliseconds.ToString() + "\")";
cmd.ExecuteNonQuery();
test.Reset();
test.Start();
}
其中 strDNSDomain 是默认命名上下文。我尝试过使用域名,但运行速度更差,耗时 3 分 30 秒。
看别人的代码。如果我们省略域部分,会有不同吗?
using (var LDAPConnection = new DirectoryEntry("LDAP://domain/dc=domain,dc=com", "username", "password"))
只需使用“LDAP://dc=domain,dc=com”即可。
变通。而不是绑定(bind)每个用户并获取属性。我将所有属性存储在第一次搜索 lastLogon 中。并使用 StreamWriter 输出。
最佳答案
DirectoryEntry
和ADOConnection
都使用 ADSI 底层。应该没有任何性能差异。
造成性能差异的唯一原因是您正在尝试检索两组不同的数据。
在您的 VBScript 中,您将“displayName、pwdLastSet、whenCreated、whenChanged、userAccountControl”设置为 strAttributes
。ADSI 将仅从 AD 加载这五个属性。
在您的 C# 代码中,您没有调用 RefreshCache方法来指定要加载的属性。因此,当您访问 DirectoryEntry.Properties
时,它会自动为您调用 RefreshCache()
,而无需为您传递任何属性。默认情况下,如果您不指定要加载的属性,ADSI 将向您返回所有非构造属性(几乎所有属性)。
另一个问题是,在您的 VBscript 中,您只运行一个 LDAP 查询,而在您的 C# 代码中,您运行许多 LDAP 查询。每个 DirectoryEntry.RefreshCache() 都将转换为一个单一的 LDAP 查询。因此,如果您尝试访问 1000 个对象,您将运行 1000 个不同的 LDAP 查询。
打个关系数据库的比方,在VBscript中,你正在运行
SELECT * FROM USER_TABLE
在 C# 代码中,您多次运行以下查询
SELECT * FROM USER_TABLE WHERE id = @id
当然,C#代码会慢一些。
要在 C# 代码中做类似的事情,您应该使用 DirectorySearcher而不是 DirectoryEntry .
同样,你需要记住指定DirectorySearcher.PropertiesToLoad为了指定从 LDAP 查询返回的属性。如果你不指定,它会再次返回所有非构造的属性给你。
关于c# - C# 中缓慢的 AD 属性检索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4929027/