在使用System.DirectoryServices时尝试访问卸载的AppDomain

我们已经实现了一个对Active Directory进行身份validation的成员资格提供程序,它使用了System.DirectoryServices。 在使用webdev服务器的Visual Studio 2010上的ASP.Net MVC 3应用程序中使用此成员资格提供程序时,我们有时(6次中有1次)在login应用程序时发生exception。

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. 

调用程序集是HibernatingRhinos.Profiler.Appender所以在禁用log4netconfiguration文件后,我们得到了一个真正的例外:

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

这个exception总是以相同的方法抛出,但是现在我们不能随机地重现它,而是大约6次中的1次。 但是,在使用IIs而不是内置的Visual Studio 2010 Web服务器时,我们并没有得到例外。

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

我们发现了两个类似的情况,但没有人find真正的解决scheme:

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

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

更新18-05-2011

最less量的代码(在asp.net mvc中)重现exception,其中userName是您的Active Directorylogin名。

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

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

这是什么适用于我(.Net 4):

而不是这个:

 principalContext = new PrincipalContext(ContextType.Domain) 

使用域string创build主要上下文:

例如

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

它应该被固定在4.5。 查看评论,尚未修复,但添加第二个参数仍然作为解决方法。

我有同样的问题,我已经find了这篇文章的答案。 似乎是一个PrincipalContext构造函数的问题,只有一个ContextType作为参数。 我知道这个post是旧的,但认为我会链接到任何人在未来:)

我们已经通过重试对GetAuthorizationGroups的调用,但在两者之间hibernate的代码解决了它。 它解决了我们的问题,但我并不满意。

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

如果我们得到exception,那么1重试显然是足够的。

这个解决scheme真的很慢,例如,当你在一个web应用程序中使用GetAuthorizationGroups的时候,会非常频繁地调用它,这使得网站非常慢。 我曾经实施过somcaching,而第一次之后就更快了。 我也在重试,因为exception仍然发生。

首先我重写GetRolesForUser方法并实现caching。

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

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

最后一个方法是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); } } 

我发现,cachingangular色使得速度更快。 希望这有助于某人。 干杯。

使用ActiveDirectoryMembershipProvider时遇到同样的问题。 对我来说,当我第一次调用Membership.ValidateUser()时,框架正在尝试创build提供者。

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

转到项目属性/ web选项卡/服务器部分,并签入NTML身份validation的checkbox。

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