c# - C# 中缓慢的 AD 属性检索

标签 c# vbscript active-directory

大家好,我正在将我的 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 输出。

最佳答案

DirectoryEntryADOConnection 都使用 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/

相关文章:

java - 如何将附加搜索查询应用于 LDAP

c# - 我是否需要自己在 xamarin.forms 中清理内存?

javascript - 将 JavaScript JQuery 添加到 HTA

c# - 测试具有不同值的函数

vbscript - 从 VB 脚本代码创建文档

vbscript - 这个 ASP 代码可以容纳多大的数字?

在 Azure .Net SDK 中找不到 AzureCredentials

asp.net-mvc - asp.net mvc 用户身份验证/权限

c# - 打开 PDF 文档并为其添加书签

c# - 在 Unity3d 中从 iphone 加载图像