I discovered that the problem was that the PrincipalSearchers that I was using in the role provider did not always contact the same domain controller as the code used in set up did. This would result in errors due to propagation delays between domain controllers. To solve this problem I used constructor injection to provide the PrincipalContext used in set up to the role provider. This allows the role provider to always use the same context as the test code. In addition I replaced the SearchRoot on the PrincipalSearcher with a search root based on the PrincipalContext provided via constructor injection. Relevant code below. Note that the role provider implements IDisposable in order to dispose of the domain context if one isn't supplied externally.
private bool DisposeContext { get; set; }
private PrincipalContext DomainContext { get; set; }
public PrintAccountingRoleProvider() : this( null ) { }
public PrintAccountingRoleProvider( PrincipalContext domainContext )
{
this.DisposeContext = domainContext == null;
this.DomainContext = domainContext ?? new PrincipalContext( ContextType.Domain );
}
...
private UserPrincipal FindUser( string userName )
{
using (PrincipalSearcher userSearcher = new PrincipalSearcher())
{
UserPrincipal userFilter = new UserPrincipal( this.DomainContext );
userFilter.SamAccountName = userName;
userSearcher.QueryFilter = userFilter;
// Replace the searcher with one directly associated with the context to ensure that any changes
// made elsewhere will be reflected if we call the search immediately following the change. This
// is critical in the integration tests.
var searcher = userSearcher.GetUnderlyingSearcher() as DirectorySearcher;
searcher.SearchRoot = new DirectoryEntry( @"LDAP://" + this.DomainContext.ConnectedServer + @"/dc=iowa,dc=uiowa,dc=edu" );
return userSearcher.FindOne() as UserPrincipal;
}
}
...
private void Dispose( bool disposing )
{
if (!this.disposed)
{
if (disposing)
{
if (this.DisposeContext && this.DomainContext != null)
{
this.DomainContext.Dispose();
this.DomainContext = null;
}
}
this.disposed = true;
}
}