大家好我将我的VBScript移植到C#。我遇到了一个问题,即在C#中Active Directory属性检索要慢得多。
这是我不完整的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是defaultNamingContext。我尝试过使用域名,但它的运行速度更差,只需3分30秒。
看着别人的代码。如果我们省略域名部分会有区别吗?
using (var LDAPConnection = new DirectoryEntry("LDAP://domain/dc=domain,dc=com", "username", "password"))
只需使用“LDAP:// dc = domain,dc = com”。
解决。而不是绑定每个用户并获取属性。我在第一次搜索lastLogon时存储了所有属性。并使用StreamWriter输出。
答案 0 :(得分:3)
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查询返回的属性。如果您未指定,它将再次返回所有非构造属性。
答案 1 :(得分:1)
以下是您可以做的几件事
在LDAP服务器中启用审核日志,并查看您的请求是如何进行的。审核日志将显示您的应用程序的每个请求所花费的时间以及打开的连接数等。
使用可以对LDAP进行异步调用的System.DirectoryServices.Protocols。检查此sample post。使用此名称空间的另一个好处是您可以指定属性。
正确关闭连接。
答案 2 :(得分:1)
使用DirectorySearcher.PropertiesToLoad仅加载必需的属性而不是所有属性。
我从vbscript中看到的内容更接近了......从某个项目复制,善意测试
DirectoryEntry de = new DirectoryEntry("ldap://domainname");
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(&(ObjectCategory=user)(sAMAccountName="+strUser+"))";
deSearch.PropertiesToLoad.Add("displayName");
deSearch.PropertiesToLoad.Add("pwdLastSet");
deSearch.PropertiesToLoad.Add("whenCreated");
deSearch.PropertiesToLoad.Add("whenChanged");
deSearch.PropertiesToLoad.Add("userAccountControl);
deSearch.SearchScope = SearchScope.Subtree;
SearchResult sr = deSearch.FindOne();
答案 3 :(得分:1)
这是阅读该属性的正确方法:
If searchResult.Properties.Contains(PropertyName) Then
Return searchResult.Properties(PropertyName)(0).ToString()
Else
Return String.Empty
End If