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

C# 在System.DirectoryServices.AccountManagement中调用UserPrinciapl.GetGroups时出现奇怪的间歇性错误处理错误 背景,c#,.net,error-handling,active-directory,C#,.net,Error Handling,Active Directory,我们有一个用C编写的asp.NET4.0Web应用程序,它调用用C编写的.NET3.5Web服务。web服务被传递一个用户id,并根据用户所属的active directory组返回一个数据列表 web服务使用.net 3.5版本的System.DirectoryServices.AccountManagement获取用户所属组的SID 对UserPrincipal.GetGroups的调用间歇性失败,错误如下。两次发生之间有很长的时间间隔,但当它确实发生时,会重复发生几分钟。该问题针对不同的广

我们有一个用C编写的asp.NET4.0Web应用程序,它调用用C编写的.NET3.5Web服务。web服务被传递一个用户id,并根据用户所属的active directory组返回一个数据列表

web服务使用.net 3.5版本的System.DirectoryServices.AccountManagement获取用户所属组的SID

对UserPrincipal.GetGroups的调用间歇性失败,错误如下。两次发生之间有很长的时间间隔,但当它确实发生时,会重复发生几分钟。该问题针对不同的广告用户

这个异常的堆栈跟踪对我们来说毫无意义。我们花了很多时间查看Reflector/ILSpy中的Microsoft广告代码,但除了调用IADsPathName.Retrieve之外,我们没有其他方法

例外情况 要复制的代码 请注意,CausenotSupportDexception方法模拟的代码不是在我们的应用程序中运行的,而是在我们无法控制的环境中其他地方的代码中运行的。

实现流以模拟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) { }
}
问题
  • 如果运行上面的示例,则在调用GetGroups时会抛出CausenotSupportDexception方法中发生的错误。这怎么可能?如有任何理论或进一步见解,将不胜感激
  • 关于如何进一步调查有何建议
  • 还有比捕获异常并重试更好的建议吗?这是我们目前的工作
  • 谢谢

    澄清

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


    我不清楚这是如何发生的。

    如果您使用的是.NET 3.5或更高版本,您可以使用新的
    System.DirectoryServices.AccountManagement
    (S.DS.AM)命名空间,这比以前容易得多

    请阅读此处的全部内容:[在.NET Framework 3.5中管理目录安全主体][1]

    基本上,您需要有一个“主体上下文”(通常是您的域)、一个用户主体,然后您就可以很容易地获得其组:

    public List<GroupPrincipal> GetGroups(string userName)
    {
       List<GroupPrincipal> result = new List<GroupPrincipal>();
    
       // establish domain context
       PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
    
       // find your user
       UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);
    
       // if found - grab its groups
       if(user != null)
       {
          PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
    
          // iterate over all groups
          foreach(Principal p in groups)
          {
             // make sure to add only group principals or change this to add to a list or varible if needed.
             if(p is GroupPrincipal)
             {
                 result.Add(p);
             }
          }
       }
    
       return result;
    }
    

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

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


    感谢那些提供建议的人。

    问题,您是否正在尝试查找用户是否属于特定组。。我正试图更好地了解你真正想要的东西。。我可能有一个更好的编码方法的建议。。这里有一个链接也会有帮助,我将在下面粘贴两段代码片段,这将帮助您更轻松地获得所需内容。非常感谢您的回复。我需要的是用户所属组的SID列表。我当前正在使用System.DirectoryServices.AccountManagement命名空间(UserPrincipal.GetGroups位于该命名空间中)。因此,我的代码示例与您在下面发布的代码示例非常相似。我所拥有的代码很好地满足了我的需求,它只是由我试图理解的无关代码引起的间歇性错误。如果您有时间,那么运行示例代码并逐步完成它是值得的。。一开始很难确定你想要什么。。很抱歉,您仍然需要检查该属性或(字符串)属性[“SamaAccountName”][0]。ToString(),因为此时它将是一个对象。。希望这是有意义的…对不起,我发现写这些东西很难提供足够的信息,但仍然很清楚。奇怪的是,当CausenotSupportDexception中出现错误时,它会跳转到GetGroups并抛出异常。如果不查看所有代码,我无法判断有多少种方法可以获得您想要的内容,但我正在尝试为您提供答案,以帮助您解决代码问题,以防您需要修复或重构某些内容
    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) { }
    }
    
    public List<GroupPrincipal> GetGroups(string userName)
    {
       List<GroupPrincipal> result = new List<GroupPrincipal>();
    
       // establish domain context
       PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
    
       // find your user
       UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);
    
       // if found - grab its groups
       if(user != null)
       {
          PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
    
          // iterate over all groups
          foreach(Principal p in groups)
          {
             // make sure to add only group principals or change this to add to a list or varible if needed.
             if(p is GroupPrincipal)
             {
                 result.Add(p);
             }
          }
       }
    
       return result;
    }
    
    public string GetDepartment(Principal principal)
    {
        string result = string.Empty;
    
        DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry);
    
        if (de != null)
        {
           if (de.Properties.Contains("samAccountName"))
           {
              result = de.Properties["samAccountName"][0].ToString();
           }
        }
    
        return result;
    }
    
    //Change this Method to fit what ever your needs desire.. 
    public string GetDepartment(string username)
    {
        string result = string.Empty;
    
        // if you do repeated domain access, you might want to do this *once* outside this method, 
        // and pass it in as a second parameter!
        PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
    
        // find the user
        UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);
    
        // if user is found
        if(user != null)
        {
           // get DirectoryEntry underlying it
           DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);
    
           if (de != null)
           {
              if (de.Properties.Contains("department"))
              {
                 result = de.Properties["department"][0].ToString();
              }
           }
        }
    
        return result;
    }