.net 使用 System.DirectoryServices 时尝试访问未加载的应用程序域

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5895128/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 15:32:26  来源:igfitidea点击:

Attempted to access an unloaded appdomain when using System.DirectoryServices

.netasp.net-mvcvisual-studio-2010

提问by nickvane

We've implemented a Membership Provider that authenticates to Active Directory and it's using System.DirectoryServices. While using this Membership Provider in an ASP.Net MVC 3 application on Visual Studio 2010 with webdev server we sometimes (1 out of 6 times) get an exception when logging in the application.

我们已经实现了一个成员身份提供程序,它对 Active Directory 进行身份验证,并且它使用 System.DirectoryServices。在带有 webdev 服务器的 Visual Studio 2010 上的 ASP.Net MVC 3 应用程序中使用此成员资格提供程序时,我们有时(6 次中有 1 次)在登录应用程序时遇到异常。

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.

The calling assembly was HibernatingRhinos.Profiler.Appender so after disabling the profiler in log4net config we got to the real exception:

调用程序集是 HibernatingRhinos.Profiler.Appender,因此在 log4net 配置中禁用分析器后,我们遇到了真正的异常:

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

The exception is always thrown at the same method, but for now we are not able to reproduce it as it happens randomly, but approximately 1 out of 6 times. We do however not get the exception when using IIs instead of the built-in Visual Studio 2010 web server.

异常总是以相同的方法抛出,但目前我们无法重现它,因为它是随机发生的,但大约 6 次中有 1 次。然而,当使用 IIs 而不是内置的 Visual Studio 2010 Web 服务器时,我们不会得到异常。

It probably has something to do with racing conditions when using multiple appdomains in the context of Visual Studio webdev, but that's just guessing. We would really like to know what's the cause of the problem as we don't want to have these exceptions in a production environment.

在 Visual Studio webdev 的上下文中使用多个应用程序域时,它可能与赛车条件有关,但这只是猜测。我们真的很想知道问题的原因是什么,因为我们不希望在生产环境中出现这些异常。

We found 2 similar cases but no one has found a real solution:

我们发现了 2 个类似的案例,但没有人找到真正的解决方案:

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

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

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

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

Update 18-05-2011

更新 18-05-2011

The smallest amount of code (in asp.net mvc) to reproduce the exception, where userName is your Active Directory loginname.

重现异常的最少代码(在 asp.net mvc 中),其中 userName 是您的 Active Directory 登录名。

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

Alas, the exception still happens at random, so no fully reproducable bug.

唉,异常仍然是随机发生的,所以没有完全可重现的错误。

回答by TofuMaster

Here is what works for me (.Net 4):

这是对我有用的(.Net 4):

Instead of this:

取而代之的是:

principalContext = new PrincipalContext(ContextType.Domain)

create the principal context with the domain string as well:

还使用域字符串创建主体上下文:

E.g.

例如

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

It should be fixed in 4.5.See comment, hasn't been fixed yet, but adding the second argument still works as workaround.

它应该在4.5中修复。请参阅评论,尚未修复,但添加第二个参数仍然可以作为解决方法。

回答by nickvane

We've solved it in code by retrying the call to GetAuthorizationGroups but with a sleep in between. It solves our problem but I'm not quite happy with it.

我们通过重试对 GetAuthorizationGroups 的调用在代码中解决了这个问题,但中间有一个睡眠。它解决了我们的问题,但我对此并不满意。

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

If we get the exception then 1 retry is apparently enough.

如果我们得到异常,那么 1 次重试显然就足够了。

回答by noshitsherlock

This solution is really slow, and when for example when you are using this in a webapplication GetAuthorizationGroups gets called very often which makes the site very slow. I worked implemented som caching instead, that makes alot faster after the first time. I am also retrying, because the exception still occurs.

这个解决方案真的很慢,例如当你在 web 应用程序中使用它时,GetAuthorizationGroups 被频繁调用,这使得站点非常慢。我工作实现了一些缓存,这在第一次之后变得更快了。我也在重试,因为异常仍然发生。

First i override the GetRolesForUser method and implement the caching.

首先,我覆盖 GetRolesForUser 方法并实现缓存。

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

The GetRolesFromActiveDirectory looks like this.

GetRolesFromActiveDirectory 看起来像这样。

    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[];
    }

The last method is GetAuthorizationGroups and it looks like this.

最后一个方法是 GetAuthorizationGroups,它看起来像这样。

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

I found out that caching the roles makes it a whole lot faster. Hope this helps someone. Cheers.

我发现缓存角色可以让它更快。希望这可以帮助某人。干杯。

回答by Magnus Lindhe

I've encountered the same issue when using the ActiveDirectoryMembershipProvider. For me it was happening when I called Membership.ValidateUser() for the first time and the framework was trying to create the provider.

我在使用 ActiveDirectoryMembershipProvider 时遇到了同样的问题。对我来说,这是在我第一次调用 Membership.ValidateUser() 并且框架试图创建提供程序时发生的。

I noticed that my temporary development computer did not have Visual Studio 2010 SP1 installed so I installed it and that solved the problem for me.

我注意到我的临时开发计算机没有安装 Visual Studio 2010 SP1,所以我安装了它并解决了我的问题。

回答by BlackBeak

I've had the same issue, and I have found the answer in thispost works. Seems to be an issue with the PrincipalContext constructor that only takes a ContextType as a parameter. I know this post is old, but thought I would link it for anyone in the future :)

我遇到了同样的问题,我在这篇文章中找到了答案。似乎是 PrincipalContext 构造函数的一个问题,它只接受一个 ContextType 作为参数。我知道这篇文章很旧,但我想我将来会为任何人链接它:)

回答by user1230898

Go to project properties/web tab/Servers section and check in the checkbox for NTML authentication.

转到项目属性/Web 选项卡/服务器部分并选中 NTML 身份验证复选框。

This is required for Cassini (VS Development Server) to use Windows authentication.

这是 Cassini(VS 开发服务器)使用 Windows 身份验证所必需的。