从MVC中查询Active Directory导致:试图访问一个未加载的appdomain.(HRESULT: 0x80

本文关键字:一个 加载 0x80 HRESULT appdomain 访问 查询 MVC Active Directory 导致 | 更新日期: 2023-09-27 18:01:19

我有一个问题,在MVC web应用程序中使用。net 4上的c#,当我查询Active Directory时,我经常得到一个错误:试图访问一个卸载的appdomain。(异常from HRESULT: 0x80131014).

奇怪的是,它会完美地工作一段时间,然后它就会开始发生,然后又消失了。

我已经做了一些修改的功能,使其工作,但他们似乎都失败了。我想知道我是否做错了什么,或者是否有更好的方法去做。

这是我当前的函数,它将接受loginId和PrincipalContext。loginId可以是用户DisplayName i。e "John Smith",或DOMAINNAME'josmi。默认是使用他们名字的前2个字母,然后是他们姓氏的前3个字母。如果不是这样的话,这里有一张支票。这部分很好。

public List<ADGroup> GetMemberGroups(string loginId, PrincipalContext principalContext, int tries = 0)
{
    var result = new List<ADGroup>();
    try
    {
        var samAccountName = "";
        if (loginId.Contains(" "))
        {
            var fName = loginId.Split(Char.Parse(" "))[0].ToLower();
            var sName = loginId.Split(Char.Parse(" "))[1].ToLower();
            if (sName.Trim().Length == 2)
                samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 4) : fName.Substring(0, 3), sName.Substring(0, 2));
            else
                samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 3) : fName.Substring(0, 2), sName.Substring(0, 3));
        }
        else
            samAccountName = loginId.Substring(loginId.IndexOf(@"'") + 1);
        var authPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName);
        if (authPrincipal == null)
            throw new Exception(string.Format("authPrincipal is null for loginId - {0}", loginId));
        var firstLevelGroups = authPrincipal.GetGroups();
        AddGroups(firstLevelGroups, ref result);
    }
    catch
    {
        if (tries > 5)
            throw;
        tries += 1;
        System.Threading.Thread.Sleep(1000);
        GetMemberGroups(loginId, principalContext, tries);
    }
    return result;
}
    private void AddGroups(PrincipalSearchResult<Principal> principal, ref List<ADGroup> returnList)
    {
        foreach (var item in principal)
        {
            if (item.GetGroups().Count() > 0)
                AddGroups(item.GetGroups(), ref returnList);
            returnList.Add(new ADGroup(item.SamAccountName, item.Sid.Value));
        }
    }

这个函数是这样调用的:

MembershipGroups = ad.GetMemberGroups(user.SamAccountName, new PrincipalContext(ContextType.Domain));

有时得到的错误是:

系统。AppDomainUnloadedException:试图访问未加载的文件应用程序域中。(来自HRESULT的例外:0 x80131014)System.StubHelpers.StubHelpers.InternalGetCOMHRExceptionObject (Int32pCPCMD,对象pThis)System.StubHelpers.StubHelpers.GetCOMHRExceptionObject (Int32pCPCMD,对象pThis)System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve (Int32lnFormatType)System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo ()在System.DirectoryServices.AccountManagement.ADStoreCtx.get_UserSuppliedServerName ()在System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.BuildPathFromDN(字符串dn)System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNextPrimaryGroupDN ()在System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNext ()在System.DirectoryServices.AccountManagement.FindResultEnumerator 1.MoveNext() at System.DirectoryServices.AccountManagement.FindResultEnumerator . system . collections . ienumerator . movenext ()

从MVC中查询Active Directory导致:试图访问一个未加载的appdomain.(HRESULT: 0x80

虽然反射镜看着System.DirectoryServices.AccountManagement内部类"UnsafeNativeMethods"是在本地代码中实现,所以UserSuppliedServerName一层是所有我可以不看CLR VM,(说实话我甚至不知道如何去做),一个节点未能返回其主要组,所以也许考虑其他实现,后一点google我遇到这些可以帮助

  • 活动目录和嵌套组这个可能很有前途这里是代码示例…

        public IList<string> FindUserGroupsLdap(string username)
    { 
        // setup credentials and connection  
        var credentials = new NetworkCredential("username", "password", "domain");  
        var ldapidentifier = new LdapDirectoryIdentifier("server", 389, true, false); 
        var ldapConn = new LdapConnection(ldapidentifier, credentials); 
        // retrieving the rootDomainNamingContext, this will make sure we query the absolute root   
        var getRootRequest = new SearchRequest(string.Empty, "objectClass=*", SearchScope.Base, "rootDomainNamingContext");  
        var rootResponse = (SearchResponse)ldapConn.SendRequest(getRootRequest);   
        var rootContext = rootResponse.Entries[0].Attributes["rootDomainNamingContext"][0].ToString();  
        // retrieve the user 
        string ldapFilter = string.Format("(&(objectCategory=person)(sAMAccountName={0}))", username); 
        var getUserRequest = new SearchRequest(rootContext, ldapFilter, SearchScope.Subtree, null);  
        var userResponse = (SearchResponse)ldapConn.SendRequest(getUserRequest);     
        // send a new request to retrieve the tokenGroups attribute, we can not do this with our previous request since 
        // tokenGroups needs SearchScope.Base (dont know why...)  
        var tokenRequest = new SearchRequest(userResponse.Entries[0].DistinguishedName, "(&(objectCategory=person))", SearchScope.Base, "tokenGroups");  
        var tokenResponse = (SearchResponse)ldapConn.SendRequest(tokenRequest);  
        var tokengroups = tokenResponse.Entries[0].Attributes["tokenGroups"].GetValues(typeof(byte[])); 
        // build query string this query will then look like (|(objectSid=sid)(objectSid=sid2)(objectSid=sid3))  
        // we need to convert the given bytes to a hexadecimal representation because thats the way they   
        // sit in ActiveDirectory  
        var sb = new StringBuilder();  
        sb.Append("(|");   
        for (int i = 0; i < tokengroups.Length; i++)  
        {  
            var arr = (byte[])tokengroups[i];    
            sb.AppendFormat("(objectSid={0})", BuildHexString(arr));   
        }   
        sb.Append(")");    
        // send the request with our build query. This will retrieve all groups with the given objectSid
        var groupsRequest = new SearchRequest(rootContext, sb.ToString(), SearchScope.Subtree, "sAMAccountName"); 
        var groupsResponse = (SearchResponse)ldapConn.SendRequest(groupsRequest); 
        // loop trough and get the sAMAccountName (normal, readable name)   
        var userMemberOfGroups = new List<string>();    
        foreach (SearchResultEntry entry in groupsResponse.Entries)    
        userMemberOfGroups.Add(entry.Attributes["sAMAccountName"][0].ToString());  
        return userMemberOfGroups;
    } 
    private string BuildHexString(byte[] bytes)
    {  
        var sb = new StringBuilder(); 
        for (int i = 0; i < bytes.Length; i++) 
        sb.AppendFormat("''{0}", bytes[i].ToString("X2")); 
        return sb.ToString();
    }
    

这些是更多的信息目的

  • 如何使用PrimaryGroupID属性查找用户的主组
  • 确定Active Directory和ADAM中的用户组成员资格

我不知道PrincipalContext是如何传入的,在这里,但我注意到一件事在我自己的代码和研究当我有这个错误,我有:

PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext , strUserName);

其中strUserName为某用户,即DOMAIN'johndoe

我正在调用该代码(这是在一个单独的函数中)并返回UserPrincipal对象作为up并将其传递给:

using (PrincipalSearchResult<Principal> result = up.GetGroups())
{
    // do something with result, here
}

result不会是null,但是在我检查了那个条件之后,我检查了result.Count() > 0,那就是它会失败的时候(有时-虽然我可以通过点击我的应用程序中调用此代码的特定选项卡来重新创建条件-即使相同的代码被称为我的应用程序的onload并且没有问题)。resultMessage属性为Attempted to access an unloaded appdomain. (Exception from HRESULT: 0x80131014)

我在一个类似的帖子中发现,我所要做的就是在我的PrincipalContext中指定域。由于我不能硬编码我的代码,当我们在开发、测试和生产环境之间移动代码时,它们对每个环境都有不同的域,我能够将其指定为Environment.UserDomainName:

PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, Environment.UserDomainName);

这消除了错误,对我来说。

此问题与确定用户是否在。net 4.0应用程序的AD组

相同

这似乎是ADSI中的一个bug,已经通过hotfix解决了。Windows 7 SP1和Windows Server 2008 R2 SP1不包括修复,因此需要手动部署到您的开发机器和服务器环境中。

http://support.microsoft.com/kb/2683913

您可以添加一些日志记录来缩小问题范围。Thread.Sleep看起来不像web应用程序中想要的东西:)

如果你得到异常,也许你可以用不同的方式处理它们。

我认为你的AppDomain正在被回收,而AD正在做它的巫术。向Application_End添加日志记录也可以提供一些线索。

try

public List<ADGroup> GetMemberGroups(string loginId, PrincipalContext principalContext, int tries = 0)
{
var result = new List<ADGroup>();
bool Done = false;
try
{
    var samAccountName = "";
    if (loginId.Contains(" "))
    {
        var fName = loginId.Split(Char.Parse(" "))[0].ToLower();
        var sName = loginId.Split(Char.Parse(" "))[1].ToLower();
        if (sName.Trim().Length == 2)
            samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 4) : fName.Substring(0, 3), sName.Substring(0, 2));
        else
            samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 3) : fName.Substring(0, 2), sName.Substring(0, 3));
    }
    else
        samAccountName = loginId.Substring(loginId.IndexOf(@"'") + 1);
    var authPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName);
    if (authPrincipal == null)
        throw new Exception(string.Format("authPrincipal is null for loginId - {0}", loginId));
    var firstLevelGroups = authPrincipal.GetGroups();
    AddGroups(firstLevelGroups, ref result);
    Done = true;
}
catch
{
    if (tries > 5)
        throw;
    tries += 1;
}
if ( ( !Done) && (tries < 6) )
     {
     System.Threading.Thread.Sleep(1000);
     result = GetMemberGroups(loginId, principalContext, tries);
     }
return result;
}
private void AddGroups(PrincipalSearchResult<Principal> principal, ref List<ADGroup> returnList)
{
    if ( principal == null )
         return;
    foreach (var item in principal)
    {
        if (item.GetGroups().Count() > 0)
            AddGroups(item.GetGroups(), ref returnList);
        returnList.Add(new ADGroup(item.SamAccountName, item.Sid.Value));
    }
}

当一个异常发生时,你从捕获块中再次调用函数(取决于tries的值),但是丢弃了它的返回值-所以即使第二/第三…呼叫成功,您将空结果返回给原始调用者。我改变了,所以结果不会被丢弃了…

在第二个函数中,您在开始foreach之前从未检查主参数是否为null…我也改了…

并且我从catch块catch中删除了递归(尽管我真的不确定这个更改是否有任何实际效果)。