62 votes

Tenté d'accéder à un déchargé domaine d'application lors de l'utilisation du Système.DirectoryServices

Nous avons mis en œuvre un Fournisseur d'appartenances qui s'authentifie auprès d'Active Directory et d'utilisation du Système.DirectoryServices. Pendant l'utilisation de ce Fournisseur d'appartenances dans un ASP.Net MVC 3 application sur Visual Studio 2010 avec webdev serveur nous de temps en temps (1 fois sur 6 fois) obtenez une exception lors de la connexion à l'application.

System.IO.FileNotFoundException: Could not load file or assembly 'System.Web' or one of its dependencies. The system cannot find the file specified.
File name: 'System.Web' 
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.LoadWithPartialNameInternal(AssemblyName an, Evidence securityEvidence, StackCrawlMark& stackMark)
at System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve(Int32 lnFormatType)
at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()
at System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsDomainName()
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOfAZ(Principal p)
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroupsHelper()
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups()

=== Pre-bind state information ===
LOG: DisplayName = System.Web (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: System.Web | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
Calling assembly : HibernatingRhinos.Profiler.Appender, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0774796e73ebf640.

La convocation de l'assemblée a été HibernatingRhinos.Profiler.Appender donc après la désactivation du générateur de profils en log4net config nous sommes arrivés à la vraie exception:

System.AppDomainUnloadedException: Attempted to access an unloaded appdomain. (Except   at System.StubHelpers.StubHelpers.InternalGetCOMHRExceptionObject(Int32 hr, IntPtr pCPCMD, Object pThis)
at System.StubHelpers.StubHelpers.GetCOMHRExceptionObject(Int32 hr, IntPtr pCPCMD, Object pThis)
at System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve(Int32 lnFormatType)
at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()
at System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsDomainName()
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOfAZ(Principal p)
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroupsHelper()
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups()

L'exception est toujours jeté à la même méthode, mais pour l'instant nous ne sommes pas en mesure de la reproduire telle qu'elle se produit de façon aléatoire, mais environ 1 sur 6 temps. Nous n'avons cependant pas accès à l'exception lorsque vous utilisez IIs, au lieu de l'intégré dans Visual Studio 2010 serveur web.

Il a probablement quelque chose à voir avec les conditions de course lors de l'utilisation de plusieurs domaines d'application dans le contexte de Visual Studio webdev, mais c'est juste deviner. Nous aimerions savoir quelle est la cause du problème comme nous ne voulons pas de ces exceptions dans un environnement de production.

Nous avons trouvé 2 cas similaires, mais personne n'a trouvé une vraie solution:

http://our.umbraco.org/forum/developers/extending-umbraco/19581-Problem-with-custom-membership-and-role-provider

http://forums.asp.net/t/1556949.aspx/1

Mise à jour 18-05-2011

La plus petite quantité de code (dans asp.net mvc) pour reproduire l'exception, où userName est votre Active Directory identifiant de connexion.

using System.DirectoryServices.AccountManagement;
using System.Web.Mvc;

namespace ADBug.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string userName = "nickvane";
            var principalContext = new PrincipalContext(ContextType.Domain);

            UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(
                principalContext,
                IdentityType.SamAccountName,
                userName);

            if (userPrincipal != null)
            {
                PrincipalSearchResult<Principal> list = userPrincipal.GetAuthorizationGroups();
            }

            return View();
        }
    }
}

Hélas, l'exception se produit toujours au hasard, donc pas entièrement reproduit le bug.

83voto

TofuMaster Points 466

Voici ce qui fonctionne pour moi (.Net 4):

Au lieu de cela:

principalContext = new PrincipalContext(ContextType.Domain)

créer le principal contexte avec la chaîne de domaine ainsi:

E. g.

principalContext = new PrincipalContext(ContextType.Domain,"MYDOMAIN")

Il doit être fixé au paragraphe 4.5.

2voto

nickvane Points 1247

Nous avons résolu dans le code en renouvelant l'appel à GetAuthorizationGroups mais avec un sommeil entre les deux. Il résout notre problème, mais je ne suis pas tout à fait heureux avec elle.

private PrincipalSearchResult<Principal> GetAuthorizationGroups(UserPrincipal userPrincipal, int tries)
{
    try
    {
        return userPrincipal.GetAuthorizationGroups();
    }
    catch (AppDomainUnloadedException ex)
    {
        if (tries > 5)
        {
            throw;
        }
        tries += 1;
        Thread.Sleep(1000);
        return GetAuthorizationGroups(userPrincipal, tries);
    }
}

Si nous obtenons l'exception de 1 retry est apparemment assez.

1voto

BlackBeak Points 11

J'ai eu le même problème, et j'ai trouvé la réponse dans ce post fonctionne. Semble être un problème avec le PrincipalContext constructeur qui ne prend ContextType en tant que paramètre. Je sais que ce post est vieux, mais pensé que je voudrais un lien pour tous ceux qui dans l'avenir :)

1voto

jinxen Points 92

Cette solution est vraiment lent, et quand, par exemple, lorsque vous utilisez cette dans une webapplication GetAuthorizationGroups est appelée très souvent, ce qui rend le site très lent. J'ai travaillé en œuvre som mise en cache au lieu de cela, qui le rend beaucoup plus vite après la première fois. Je suis également une nouvelle tentative, parce que l'exception se produit toujours.

J'ai d'abord remplacer le GetRolesForUser méthode et de mettre en œuvre la mise en cache.

    public override string[] GetRolesForUser(string username)
    {
        // List of Windows groups for the given user.
        string[] roles;

        // Create a key for the requested user.
        string cacheKey = username + ":" + ApplicationName;

        // Get the cache for the current HTTP request.
        Cache cache = HttpContext.Current.Cache;
        // Attempt to fetch the list of roles from the cache.
        roles = cache[cacheKey] as string[];
        // If the list is not in the cache we will need to request it.
        if (null == roles)
        {
            // Allow the base implementation to load the list of roles.
            roles = GetRolesFromActiveDirectory(username);
            // Add the resulting list to the cache.
            cache.Insert(cacheKey, roles, null, Cache.NoAbsoluteExpiration,
                Cache.NoSlidingExpiration);
        }

        // Return the resulting list of roles.
        return roles;
    }

Le GetRolesFromActiveDirectory ressemble à ceci.

    public String[] GetRolesFromActiveDirectory(String username)
    {            
        // If SQL Caching is enabled, try to pull a cached value.);));
        if (_EnableSqlCache)
        {
            String CachedValue;
            CachedValue = GetCacheItem('U', username);
            if (CachedValue != "*NotCached")
            {
                return CachedValue.Split(',');
            }
        }

        ArrayList results = new ArrayList();
        using (PrincipalContext context = new PrincipalContext(ContextType.Domain, null, _DomainDN))
        {
            try
            {                    
                UserPrincipal p = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);

                var tries = 0;
                var groups = GetAuthorizationGroups(p, tries);

                foreach (GroupPrincipal group in groups)
                {
                    if (!_GroupsToIgnore.Contains(group.SamAccountName))
                    {
                        if (_IsAdditiveGroupMode)
                        {
                            if (_GroupsToUse.Contains(group.SamAccountName))
                            {
                                results.Add(group.SamAccountName);
                            }
                        }
                        else
                        {
                            results.Add(group.SamAccountName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ProviderException("Unable to query Active Directory.", ex);
            }
        }
        // If SQL Caching is enabled, send value to cache
        if (_EnableSqlCache)
        {
            SetCacheItem('U', username, ArrayListToCSString(results));
        }

        return results.ToArray(typeof(String)) as String[];
    }

La dernière méthode est GetAuthorizationGroups et il ressemble à ceci.

    private PrincipalSearchResult<Principal> GetAuthorizationGroups(UserPrincipal userPrincipal, int tries)
    {
        try
        {
            return userPrincipal.GetAuthorizationGroups();
        }
        catch(FileNotFoundException ex)
        {
            if (tries > 5) throw;

            tries++;
            Thread.Sleep(1000);

            return GetAuthorizationGroups(userPrincipal, tries);
        }
        catch (AppDomainUnloadedException ex)
        {
            if (tries > 5) throw;

            tries++;
            Thread.Sleep(1000);

            return GetAuthorizationGroups(userPrincipal, tries);
        }
    }

J'ai trouvé que la mise en cache des rôles est beaucoup plus rapide. Espérons que cela aide quelqu'un. Des acclamations.

1voto

Magnus Lindhe Points 2391

J'ai rencontré le même problème lors de l'utilisation de la ActiveDirectoryMembershipProvider. Pour moi, c'est arrivé quand j'ai appelé l'Adhésion.ValidateUser() pour la première fois, et le cadre a été d'essayer de créer le fournisseur.

J'ai remarqué que mon ordinateur de développement n'a pas de Visual Studio 2010 SP1 installé, donc je l'ai installé et qui a résolu le problème pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X