I have a C# .NET 6 console application running under a Windows service that uses DirectorySearcher
to determine if contacts (read from a CRM's SQL database) currently also exist in Active Directory. The results returned from DirectorySearcher
are inconsistent, even when the exact same search is repeated multiple times one after another (essentially the search will sometimes find the contact and sometimes not, even though it is stored in the searched OU).
I then created a C# .NET 6 console test application that runs as a scheduled task. It isolates the exact same search code used in the malfunctioning application and repeats the same search three times for search criteria that is input. This application consistently returns accurate search results. (The test application is a scheduled task due to credentials provided by a gMSA and the need to input email IDs for testing.)
This is the code used in the malfunctioning application and the correctly functioning test application:
public (bool ContactExists, DirectoryEntry DirectoryEntryObjectNullIfFalse) ADContactExists(int srcId, long emailId, bool shortEmailIdNeedsConversion, bool searchExceptionsPath = false)
{
#if STAGING || DEBUG
string searchPath = configuration.GetValue<string>("ActiveDirectory:StagingAdsSearchPath");
#else
string searchPath = App_Configuration.GetAdsClientsPath();
#endif
if (searchExceptionsPath)
searchPath = App_Configuration.GetAdsExceptionsPath();
using (DirectoryEntry rootDirectoryEntry = App_Configuration.GetCredentialedDirectoryEntry(false, searchPath))
{
using (DirectorySearcher ds = new DirectorySearcher(rootDirectoryEntry))
{
ds.SearchScope = SearchScope.OneLevel;
long longEmailId = 0;
if (shortEmailIdNeedsConversion)
longEmailId = InterActionShortIdConversions.ConvertToLongInterActionID(srcId, emailId);
else
longEmailId = emailId;
ds.Filter = GetFilter("extensionAttribute8", "=", longEmailId.ToString());
ds.PropertiesToLoad.Add("extensionAttribute8"); //InterAction Email ID (Converted Long Form)
SearchResult sr = ds.FindOne();
if (sr != null)
{
//Update existing Active Directory contact
DirectoryEntry directoryEntryResult = App_Configuration.GetCredentialedDirectoryEntry(false, sr.Path);
return (true, directoryEntryResult);
}
else
{
//Create new Active Directory contact
DirectoryEntry directoryEntryResult = new DirectoryEntry();
return (false, directoryEntryResult);
}
}
}
}
public string GetFilter(string property, string comparisonOperator, string propertyValue)
{
string filter = "(&(objectCategory=person)(objectClass=contact)(" + property + comparisonOperator + propertyValue + "))";
return filter;
}
System.DirectoryServices
async
await
) code due to using NServiceBus and Quartz.NET, however most is synchronous. That being said, the asynchronous code does call some synchronous methods that then call the DirectorySearcher
code. I mention this should someone think that a possibility exists that the asynchronous code might be affecting the Active Directory searches in some way.All assistance with getting DirectorySearcher
to consistently return accurate results is greatly appreciated.
The issue was caused by a C# Dictionary
being populated and read asynchronously. Because some threads would access theDictionary
before it was fully populated, incorrect information was sometimes submitted to Active Directory searches (for example, null values), which would then result in incorrect results being retrieved.
I attempted to correct this using a ConcurrentDictionary
, as detailed in the SO post Populate C# dictionary in async via conditional arguments before access from threads reading dictionary contents, however this was not possible with my use case so I ended up rewriting the logic for the affected methods and removing the Dictionary
entirely. Once I did this, DirectorySearcher
consistently returned accurate results.