web-dev-qa-db-ja.com

MVCからActiveDirectoryをクエリすると、次のようになります。アンロードされたアプリドメインにアクセスしようとしました。 (HRESULTからの例外:0x80131014)

MVCWebアプリケーションの.Net4でc#を使用すると、Active Directoryにクエリを実行すると、頻繁にエラーが発生します:アンロードされたアプリドメインにアクセスしようとしました。(HRESULTからの例外:0x80131014)。

奇妙なことは、それがしばらくの間完璧に機能し、それからそれが起こり始め、そして再び消えるということです。

関数を機能させるためにいくつかの変更を加えましたが、すべて失敗しているようです。私は何か間違ったことをしているのか、それとももっと良い方法があるのだろうかと思っています。

これが私の現在の関数で、loginIdとPrincipalContextを受け入れます。 loginIdは、ユーザーDisplayName、つまり「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));
_

[〜#〜]時々[〜#〜]が得るエラーは次のとおりです。

System.AppDomainUnloadedException:アンロードされたappdomainにアクセスしようとしました。 (HRESULTからの例外:0x80131014)System.StubHelpers.StubHelpers.InternalGetCOMHRExceptionObject(Int32 hr、IntPtr pCPCMD、Object pThis)at System.StubHelpers.StubHelpers.GetCOMHRExceptionObject(Int32 hr、IntPtr pCPCMD、Object pThis)atSystem.DirectoryServices.AccountManagement。 UnsafeNativeMethods.IADsPathname.Retrieve(Int32 lnFormatType)at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()at System.DirectoryServices.AccountManagement.ADStoreCtx.get_UserSuppliedServerName()at System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.BuildPathFromDN(String DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNextPrimaryGroupDN()at System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNext()at System.DirectoryServices.AccountManagement.FindResultEnumerator1.MoveNext() at System.DirectoryServices.AccountManagement.FindResultEnumerator 1.System.Collections.IEnumerator.MoveNext()

26
Ryk

system.DirectoryServices.AccountManagementのリフレクターを見ると、内部クラス「UnsafeNativeMethods」がネイティブコードで実装されているため、CLR VMを見なくても1レベル上のUserSuppliedServerNameで続行できます(率直に言って、その方法すらわかりません)。ノードがプライマリグループを返すことに失敗しているようです。少しグーグルした後、他の実装を検討してください。

  • Active Directoryとネストされたグループこれは有望かもしれませんここにコードサンプルがあります。

        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();
    }
    

これらは情報提供のためのものです

9
almog.ori

ここでは、PrincipalContextがどのように渡されているのかわかりませんが、このエラーが発生したときに自分のコードと調査で気付いたことが1つあります。

_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と呼ばれ、問題がなかったとしても、このコードを呼び出したアプリの特定のタブをクリックすると発生します)。 MessageresultプロパティはAttempted to access an unloaded appdomain. (Exception from HRESULT: 0x80131014)でした。

同様の投稿 で、これと同じように、PrincipalContextでドメインを指定するだけで済みました。コードをハードコーディングできなかったため、開発環境、テスト環境、本番環境の間でコードを移動し、それぞれに異なるドメインがあるため、_Environment.UserDomainName_として指定できました。

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

これは私にとって、エラーを取り除きました。

5
vapcguy

この問題は、 ユーザーが.NET 4.0アプリケーションのADグループに属しているかどうかを確認する と同じです。

これは、修正プログラムで解決されたADSIのバグのようです。 Windows 7SP1およびWindowsServer 2008 R2 SP1には修正が含まれていないため、開発マシンおよびサーバー環境に手動で展開する必要があります。

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

4
ShadowChaser

問題を絞り込むために、ログを記録することができます。そのThread.Sleepは、Webアプリケーションに必要なもののようには見えません:)

例外が発生している場合は、別の方法で処理できる可能性があります。

ADがブードゥーをしている間、あなたのAppDomainはリサイクルされていると思います。 Application_Endにロギングを追加することも、いくつかの手がかりを提供する可能性があります。

0
Eben Roux

試してみてください

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));
    }
}

例外が発生すると、(trysの値に応じて)catch-blockから関数を再度呼び出しましたが、戻り値を破棄しました-したがって、2番目/ 3番目の...呼び出しが機能した場合でも、空の結果を返しました。元の呼び出し元。結果が破棄されないように変更しました...

2番目の関数では、foreachを開始する前にプリンシパルパラメーターのnullをチェックしていません...私もそれを変更しました...

そして、catch block catch内から再帰を削除しました(ただし、この変更が実際に効果があるかどうかはわかりません)。

0
Yahia