在System.DirectoryServices.AccountManagement中调用UserPrinciapl.GetGroups时出现奇怪的间歇性错误处理错误

Sim*_*ane 6 .net c# error-handling active-directory

背景

我们有一个用C#编写的asp.net 4.0 Web应用程序,它调用用C#编写的.net 3.5 Web服务.Web服务传递用户标识,并根据用户所属的活动目录组返回数据列表.

Web服务使用.net 3.5版本的System.DirectoryServices.AccountManagement来获取用户所属组的Sids.

对UserPrincipal.GetGroups的调用会间歇性地失败并出现以下错误.在发生之间存在很长的时间段但是当它确实发生时它反复发生几分钟.问题出现在不同的AD用户身上.

这个例外的堆栈跟踪对我们没有任何意义.我们花了很多时间在Reflector/ILSpy中查看Microsoft AD代码,但无法超越对IADsPathName.Retrieve的调用.

例外

System.NotSupportedException: Specified method is not supported.
at System.Web.HttpResponseStream.get_Position()
at System.Drawing.UnsafeNativeMethods.ComStreamFromDataStream.Seek(Int64 offset, Int32 origin)
at System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve(Int32 lnFormatType)
at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()
at System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsForestName()
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOf(Principal p)
at System.DirectoryServices.AccountManagement.Principal.GetGroupsHelper()
at System.DirectoryServices.AccountManagement.Principal.GetGroups()
at Data.SoftwarePublishingItemData.GetSids(String requestedForUserId)
at Data.SoftwarePublishingItemData.GetSoftwarePublishingItems(IDatabaseContext dbContext, GetSoftwarePublishingItemsSettings settings, XBXmlDocument parameters)
at Web.GetSoftwarePublishingItems.GetFlexiFieldData(String xml)
Run Code Online (Sandbox Code Playgroud)

代码重现

请注意,CauseNotSupportedException方法模仿我们的应用程序中没有运行的代码,但是在我们无法控制的环境中的其他地方的代码中.

class Program
{
    static void Main(string[] args)
    {
        CauseNotSupportedException();

        string samAccountName = "domain.user";

        using (var principalContext = new PrincipalContext(ContextType.Domain))
        {
            using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName))
            {
                if (userPrincipal == null)
                    throw new ActiveDirectoryObjectNotFoundException();

                using (var groups = userPrincipal.GetGroups())
                {
                    foreach (GroupPrincipal group in groups)
                    {
                        Console.WriteLine(group.Sid);
                    }
                }
            }
        }
    }

    public static void CauseNotSupportedException()
    {
        using (var b = new Bitmap(500, 500, PixelFormat.Format32bppArgb))
        {
            b.Save(new FakeStream(), ImageFormat.Png);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Stream的实现模仿HttpResponseStream行为

public class FakeStream : Stream
{
    public override bool CanRead { get { return false; } }
    public override bool CanSeek { get { return false; } }
    public override bool CanWrite { get { return true; } }

    public override void Flush() { }

    public override long Length { get { throw new NotSupportedException("No Seek"); } }

    public override long Position
    {
        get { throw new NotSupportedException("No Seek"); }
        set { throw new NotSupportedException("No Seek"); }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new InvalidOperationException("Write only stream");
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException("net_noseek");
    }

    public override void SetLength(long value) { }

    public override void Write(byte[] buffer, int offset, int count) { }
}
Run Code Online (Sandbox Code Playgroud)

问题

  1. 如果运行上面的示例,则会在对GetGroups的调用中抛出CauseNotSupportedException方法中发生的错误.怎么可能?任何理论或进一步的见解将不胜感激.
  2. 有关如何进一步调查的任何建议?
  3. 有什么比捕获异常和重试更好的建议?这是我们目前的工作.

谢谢.

澄清

我不确定我的解释有多清楚,所以这里有一些澄清.首先,我对获取Sids的活动目录代码感到满意.这就是我想要它做的事情,我不认为问题就是这样.真正的问题是,当其他无关代码发生错误(它不在我们的应用程序中)时,错误会在GetGroups调用中显现,因此奇怪的堆栈跟踪最初发生在System.Web.HttpResponseStream.get_Position()中.在示例应用程序中,NotSupportedException发生在CauseNotSupportedException中但代码没有在那里中断,它在对GetGroups的调用中断.如果您在示例应用程序中注释掉CauseNotSupportedException(),则永远不会发生错误.

我不清楚这是怎么发生的.

Sim*_*ane 4

在拨打支持电话后,微软已针对此问题发布了热修复程序。请参阅下面的链接。

声明的原因是:“出现此问题的原因是 System.DirectoryServices.AccountManagement 命名空间是本机 API Active Directory 服务接口 (ADSI) 的精简包装。IADsPathName 接口实现的 IErrorInfo 接口响应 ADSI 不响应的异常当堆栈上没有 ADSI 异常时,IErrorInfo 接口会抛出位于堆栈顶部的异常,即使该异常是由应用程序中的另一个处理程序处理的。

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

感谢那些提出建议的人。