How can I search 10 OUs in parallel using DirectoryServices - c#

I need to query LDAP on multiple paths and I wish to use DirectoryServices for various reasons.
var ADobjects = new Dictionary<string, ADobject>();
foreach (var OUItem in OUs)
{
using (DirectoryEntry ldap = new DirectoryEntry("LDAP://" + OUItem))
{
using (DirectorySearcher searcher = new DirectorySearcher(ldap))
{
searcher.Filter = "(objectClass=user)";
searcher.PropertiesToLoad.Add("distinguishedName");
searcher.PropertiesToLoad.Add("cn");
searcher.PropertiesToLoad.Add("displayName");
using (SearchResultCollection results = searcher.FindAll())
{
foreach (SearchResult result in results)
{
var dn = result.Properties["distinguishedName"][0].ToString();
if (!ADobjects.ContainsKey(dn))
{
ADobjects.Add(dn, new ADobject(result));
}
}
}
}
}
}
This works.. but when I have 10 OU's to query I would like to launch these queries in parallel.
I know how to launch an async method (more or less) but how can I use these queries to fill 1 dictionary at the same time??
I've clicked around but I'm really unsure if this can and should be done async. Since every search might take a few seconds, it could really benefit from async tasks.

You can use futures http://msdn.microsoft.com/en-us/library/ff963556.aspx
example from mentioned source:
int BufferSize = ...
var buffer1 = new BlockingCollection<string>(BufferSize);
var f = new TaskFactory(TaskCreationOptions.LongRunning,
TaskContinuationOptions.None);
var stage1 = f.StartNew(() => LdapSearch(buffer1));
var stage2 = f.StartNew(() => LdapSearch(buffer1));
var stage3 = f.StartNew(() => LdapSearch(buffer1));
var stage4 = f.StartNew(() => LdapSearch(buffer1));
Task.WaitAll(stage1, stage2, stage3, stage4);
static void LdapSearch(BlockingCollection<string> output,
)
{
output.Add(some result);
}

Related

Active Directory Query Users with contains ids from an array using C#

I am trying to perform a query to Active Directory to obtain all users where id is contained in the input array using a single query.
Is it possible? It
public List<Principal> Get(IEnumerable<string> ids)
{
var context = new PrincipalContext(ContextType.Domain);
var userPrincipal = new UserPrincipal(context)
{
// SOME logic with input array
};
var searcher = new PrincipalSearcher(userPrincipal);
return searcher.FindAll().ToList();
}
It is not a problem to query single user by id:
public static UserPrincipal? Get(string id)
{
var context = new PrincipalContext(ContextType.Domain);
var userPrincipal = UserPrincipal.FindByIdentity(context, id);
return userPrincipal;
}
Also, I can not find any information in docs
You can't do it with PrincipalSearcher. You'll have to use DirectorySearcher (which is what PrincipalSearcher uses behind the scenes anyway).
I actually already wrote a method that does exactly this in an article I wrote about getting better performance when programming withActive Directory. That method takes usernames and returns a list of email addresses:
public IEnumerable<string> GetEmailAddresses(IEnumerable<string> usernames) {
var filter = new StringBuilder();
var numUsernames = 0;
var e = usernames.GetEnumerator();
var hasMore = e.MoveNext();
while (hasMore) {
var username = e.Current;
filter.Append($"(sAMAccountName={username})");
numUsernames++;
hasMore = e.MoveNext();
if (numUsernames == 50 || !hasMore) {
var search = new DirectorySearcher(new DirectoryEntry()) {
PageSize = 1000,
Filter = $"(&(objectClass=user)(objectCategory=person)(|{filter}))"
};
search.PropertiesToLoad.Add("mail");
using (var results = search.FindAll()) {
foreach (SearchResult result in results) {
yield return (string) result.Properties["mail"][0];
}
}
filter.Clear();
numUsernames = 0;
}
}
}
This method builds an LDAP query in the format:
(&(objectClass=user)(objectCategory=person)(|(sAMAccountName={username1})(sAMAccountName={username2})))
It does them in batches of 50 just to make sure you never hit the size limit on LDAP requests, although you could probably push that number higher.
You can adapt it to return whatever you'd like by changing the method signature and the yield return line.
In that same article I discuss the benefits of using DirectoryEntry/DirectorySearcher instead of the AccountManagement namespace. You can get far better performance.

Saved search returns no results programmatically

I have the following code:
var search = new TransactionSearchAdvanced();
search.savedSearchId = "680";
SearchResult searchResult = Client.Service.search(search);
var resultList = searchResult.searchRowList;
var castList = resultList.Cast<TransactionSearchRow>();
Everytime I call this method I get 0 search results returned. If I view the saved search in NetSuite itself I have over 1000 results.
I am running a similar search on customers that is 100% working.
public static List<Account> GetCustomerList()
{
var search = new CustomerSearchAdvanced();
search.savedSearchId = "678";
try
{
SearchResult searchResult = Client.Service.search(search);
var resultList = searchResult.searchRowList;
var castList = resultList.Cast<CustomerSearchRow>();
var accountList = new List<Account>();
foreach (var resultRow in castList)
{
var basic = resultRow.basic;
var account = new Account();
account.NsAccountId = basic.entityId?.FirstOrDefault()?.searchValue;
account.Name = basic.companyName?.FirstOrDefault()?.searchValue;
account.EmailAddress1 = basic.email?.FirstOrDefault()?.searchValue;
account.Address = basic.address?.FirstOrDefault()?.searchValue;
account.BillingAddress = basic.billAddress?.FirstOrDefault()?.searchValue;
account.Telephone1 = basic.phone?.FirstOrDefault()?.searchValue;
account.BillingPhone = basic.billPhone?.FirstOrDefault()?.searchValue;
account.Fax = basic.fax?.FirstOrDefault()?.searchValue;
account.WebAddress = basic.url?.FirstOrDefault()?.searchValue;
accountList.Add(account);
}
return accountList;
}
I have tried adding the the role to view transactions. I am totally unfamiliar with netsuite itself and have no idea what it could be since all the settings on my 2 searches are identical.
EDIT
The SearchResult objects are actually different:
looking into this now
In the saved search interface of NetSuite there is a field that needs to be chacked "Run Unrestricted" this is what solved it for me.

How to combine a distinct query with projection with C# MongoDB driver

I try to combine a projection and a distinct with the MongoDB driver but don't get anywhere...
I have:
var coll = db.GetCollection<Vat>(CommonConstants.VatCodeCollection);
// I like to combine in one statement:
var result = coll.Distinct<DateTime>("ValidSince", filter).ToList();
var projection = Builders<Vat>.Projection.Expression(x => new VatPeriod { ValidSince = x.ValidSince });
So at the end I like to get a List<VatPeriod> as a result of one statement. Of course I could do something like
var coll = db.GetCollection<Vat>(CommonConstants.VatCodeCollection);
List<VatPeriod> vatPeriods = null;
try
{
var result = coll.Distinct<DateTime>("ValidSince", filter).ToList();
if (result.Count > 0)
{
vatPeriods = new List<VatPeriod>(result.Count);
foreach (var dateTime in result)
{
vatPeriods.Add(new VatPeriod() {ValidSince = dateTime});
}
}
return vatPeriods;
}
catch .....
in my repository class, but I would prefer to do everything on the Mongo server. Any idea if and how this is possible?

Using PrincipalSearcher to find users with "or" parameters

Is it possible to use System.DirectoryServices.AccountManagement.PrincipalSearcher to search based on multiple parameters using "or" (not "and").
i.e.
// This uses an and
//(&(objectCategory=person)(!UserAccountControl:1.2.840.113556.1.4.803:=2)(&(SAMAccountName=tom*)(DisplayName=tom*)))
var searchPrinciple = new UserPrincipal(context);
searchPrinciple.DisplayName = "tom*";
searchPrinciple.SamAccountName = "tom*";
var searcher = new PrincipalSearcher();
searcher.QueryFilter = searchPrinciple;
var results = searcher.FindAll();
and I would like a search similar to this (in LDAP) using PrincipalSearcher (not DirectorySearcher)
// (&(objectCategory=person)(!UserAccountControl:1.2.840.113556.1.4.803:=2)(|(SAMAccountName=tom*)(DisplayName=tom*)))
It's obviously not possible, here is a workaround:
List<UserPrincipal> searchPrinciples = new List<UserPrincipal>();
searchPrinciples.Add(new UserPrincipal(context) { DisplayName="tom*"});
searchPrinciples.Add(new UserPrincipal(context) { SamAccountName = "tom*" });
searchPrinciples.Add(new UserPrincipal(context) { MiddleName = "tom*" });
searchPrinciples.Add(new UserPrincipal(context) { GivenName = "tom*" });
List<Principal> results = new List<Principal>();
var searcher = new PrincipalSearcher();
foreach (var item in searchPrinciples)
{
searcher = new PrincipalSearcher(item);
results.AddRange(searcher.FindAll());
}
Not necessarily as clean as some of the other answers but here is how I've implemented this in a project I'm working on. I wanted both searches to be run async to try and reduce any slow down due to running two AD queries.
public async static Task<List<ADUserEntity>> FindUsers(String searchString)
{
searchString = String.Format("*{0}*", searchString);
List<ADUserEntity> users = new List<ADUserEntity>();
using (UserPrincipal searchMaskDisplayname = new UserPrincipal(domainContext) { DisplayName = searchString })
using (UserPrincipal searchMaskUsername = new UserPrincipal(domainContext) { SamAccountName = searchString })
using (PrincipalSearcher searcherDisplayname = new PrincipalSearcher(searchMaskDisplayname))
using (PrincipalSearcher searcherUsername = new PrincipalSearcher(searchMaskUsername))
using (Task<PrincipalSearchResult<Principal>> taskDisplayname = Task.Run<PrincipalSearchResult<Principal>>(() => searcherDisplayname.FindAll()))
using (Task<PrincipalSearchResult<Principal>> taskUsername = Task.Run<PrincipalSearchResult<Principal>>(() => searcherUsername.FindAll()))
{
foreach (UserPrincipal userPrincipal in (await taskDisplayname).Union(await taskUsername))
using (userPrincipal)
{
users.Add(new ADUserEntity(userPrincipal));
}
}
return users.Distinct().ToList();
}
My ADUserEntity class has an equality check based on the SID. I tried to add the Distinct() on to the Union() of the two searcher results but that didn't work.
I welcome any constructive criticism on my answer as I'd like to know if there is any way I can improve it.
I know this is kind of late, but this is the construct I use when searching AD:
public static Task<IEnumerable<SomeUserModelClass>> GetUsers(//Whatever filters you want)
{
return Task.Run(() =>
{
PrincipalContext context = new PrincipalContext(ContextType.Domain);
UserPrincipal principal = new UserPrincipal(context);
principal.Enabled = true;
PrincipalSearcher searcher = new PrincipalSearcher(principal);
var users = searcher.FindAll().Cast<UserPrincipal>()
.Where(x => x.SomeProperty... // Perform queries)
.Select(x => new SomeUserModelClass
{
userName = x.SamAccountName,
email = x.UserPrincipalName,
guid = x.Guid.Value
}).OrderBy(x => x.userName).AsEnumerable();
return users;
});
}
The FindAll method searches the domain specified in the principal
context for objects that have identical properties to those set on the
query filter. The FindAll method returns all objects that match the
supplied object whereas the FindOne method returns only a single
matching principal object.
http://msdn.microsoft.com/en-us/library/bb384378(v=vs.90).aspx
I don't know what you need, but you could do a search by 1 proprety and 1 by other and then use LINQ on the lists to merge,filter and etc...
PrincipalContext pContext = new PrincipalContext(ContextType.Machine, Environment.MachineName);
GroupPrincipal gp = GroupPrincipal.FindByIdentity(pContext, "Administrators");
bool isMember = UserPrincipal.Current.IsMemberOf(gp);

C# Lucene get all the index

I am working on a windows application using Lucene. I want to get all the indexed keywords and use them as a source for a auto-suggest on search field. How can I receive all the indexed keywords in Lucene? I am fairly new in C#. Code itself is appreciated. Thanks.
Are you looking extract all terms from the index?
private void GetIndexTerms(string indexFolder)
{
List<String> termlist = new ArrayList<String>();
IndexReader reader = IndexReader.open(indexFolder);
TermEnum terms = reader.terms();
while (terms.next())
{
Term term = terms.term();
String termText = term.text();
int frequency = reader.docFreq(term);
termlist.add(termText);
}
reader.close();
}
For inspiration with Apache Lucene.Net version 4.8 you can look at GitHub msigut/LuceneNet48Demo. Use classes: SearcherManager, *QueryParser and IndexWriter for build index.
// you favorite Query parser (MultiFieldQueryParser for example)
_queryParser = new MultiFieldQueryParser(...
// Execute the search with a fresh indexSearcher
_searchManager.MaybeRefreshBlocking();
var searcher = _searchManager.Acquire();
try
{
var q = _queryParser.Parse(query);
var topDocs = searcher.Search(q, 10);
foreach (var scoreDoc in topDocs.ScoreDocs)
{
var document = searcher.Doc(scoreDoc.Doc);
var hit = new QueryHit
{
Title = document.GetField("title")?.GetStringValue(),
// ... you logic to read data from index ...
};
}
}
finally
{
_searchManager.Release(searcher);
searcher = null;
}

Categories