Searching Arraylist not working - c#

Below is a snippet of code i am trying to use to search an arraylist for. I have used code very similar to this in a different form and it has worked. However here it has not. It is meant to search for the Customer Number within the Arraylist on frmMain, it then searches for that customers Account Number typed in by the user. But for some reason it is almost as if it skips the second if statement and go to the error message at the bottom.
The Arraylist is on the frmMain, then there are classes for Customer Account and Transaction. In Customer there is another ArrayList storing the Accounts, then in the Account Class there is an ArrayList storing the transactions of the customer account.
foreach (Customer a in frmMain.bankDetails)
{
if (a.getCustomerNumber().ToUpper().Equals(custSearch))
{
foreach (Account b in a.Accounts)
{
if (b.getAccNumber().Equals(searchString))
{
txtSearch.BackColor = Color.PaleGreen;
txtAccSortCode.Text = b.getAccSort();
txtAccNumber.Text = Convert.ToString(b.getAccNumber());
txtAccNickName.Text = b.getAccNick();
txtAccDate.Text = b.getAccDate();
txtAccCurBal.Text = Convert.ToString(b.getAccCurBal());
txtAccOverDraft.Text = Convert.ToString(b.getAccOverDraft());
txtNumTrans.Text = Convert.ToString(b.getAccNumTrans());
found = true;
break;
}
}
}
}
Any help appreciated!

If b.getAccNumber() is returning an int and searchString is a string, then it will always return false. For example:
int int_four = 4;
string string_four = "4";
bool eq1 = int_four.Equals(string_four); // false
bool eq2 = int_four.ToString().Equals(string_four); // true

is the "getCustomerNumber" property an actual number (an integer) or not? Which I think its not, because you else wouldn`t use ToUpper() method. There are letter as well (I think).
Anyway, would you mind showing me your Customer class? And paste some customer number example.
Mitja

Related

Getting email addresses from AD Group MVC

I have the name of my AD Group and I need to return a list of every single email address of the users found inside. I've been looking around trying to find the answer to this but all of the results I've found are aimed at checking an AD Group for a single users email address. Often by using their windows log on username.
However, what I'm wanting to do is to grab every email address inside my target active directory group. As eventually this will be turned into an automated email that needs to be sent to only those individuals within the active directory group.
The project I'm building is making use of MVC 5.
Could someone please point me in the right direction for this?
You need to split this into two steps:
Find the group (I assume you just have the name of the group)
Get all the members.
Finding the group
You can use GroupPrincipal.FindByIdentity, which is arguably easier, but I find that's quite slow (slower the bigger your group is). I prefer to use DirectorySearcher/DirectoryEntry (which is what GroupPrincipal uses behind the scenes anyway).
To find the group, your code would look something like this:
var groupName = "MyGroup";
var search = new DirectorySearcher() {
Filter = $"(&(objectClass=group)(cn={groupName}))"
};
search.PropertiesToLoad.Add("cn"); //this is just to prevent it from returning every attribute
//This will throw an exception if the group is not found
var group = ((SearchResult)search.FindOne()).GetDirectoryEntry();
If you already have the distinguishedName of the group, you can actually skip that and just make a DirectoryEntry:
var group = new DirectoryEntry($"LDAP://{disginguishedName}");
Getting the members
I wrote an article on how to do this, with example C# code: Find all the members of a group
The example code in that article returns the DOMAIN\username, so here is the same method, modified to return a list of email addresses for all the members that have one.
Pass this method the DirectoryEntry object we found for the group. Set recursive to true if you want it to look inside nested groups.
public static IEnumerable<string> GetGroupMemberList(DirectoryEntry group, bool recursive = false) {
var members = new List<string>();
group.RefreshCache(new[] { "member" });
while (true) {
var memberDns = group.Properties["member"];
foreach (string member in memberDns) {
using (var memberDe = new DirectoryEntry($"LDAP://{member.Replace("/", "\\/")}")) {
memberDe.RefreshCache(new[] { "objectClass", "mail" });
if (recursive && memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(memberDe, true));
} else {
var email = memberDe.Properties["mail"].Value.ToString();
if (!string.IsNullOrEmpty(email)) {
members.Add(email);
}
}
}
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
In my article, I also talk about primary groups and Foreign Security Principals, but you don't need to worry about that here since it's not applicable to distribution lists.

How do display security groups and users in that security group using List View?

I need to show all my security groups and the users that are members of the security groups in a listView.
At the moment I can display all the security groups but not sure how to display the users in the security group.
Here is my code I am currently using:
private void Security_group_btn_Click(object sender, EventArgs e)
{
DirectorySearcher searcher = new DirectorySearcher(DomainName);
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, DomainName);
UserPrincipal userPrin = new UserPrincipal(ctx);
userPrin.Name = "*";
var search = new System.DirectoryServices.AccountManagement.PrincipalSearcher();
search.QueryFilter = userPrin;
var results = searcher.FindAll();
ListView lvwListView = this.security_listView;
lvwListView.Clear();
lvwListView.Columns.Add("Security Group", 175, HorizontalAlignment.Left);
lvwListView.Columns.Add("Users", 175, HorizontalAlignment.Left);
searcher.Filter = "(&(objectClass=group))";
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("sAMAccountName");
SearchResultCollection result = searcher.FindAll();
foreach (SearchResult entry in result)
{
lvwListView.Items.Add(entry.GetDirectoryEntry().Properties["sAMAccountName"].Value.ToString());
}
result.Dispose();
searcher.Dispose();
}
}
}
So basically I would like to display something like this in my ListView:
Security Group Name
Users User1
User2
User3
Administrators User1
User2
User3
User4
Thanks
First, be careful of this:
lvwListView.Items.Add(entry.GetDirectoryEntry().Properties["sAMAccountName"].Value.ToString());
Particularly, using GetDirectoryEntry() just to get a value. When you get a value using DirectoryEntry.Properties, it checks to see if it already has the value you're asking for in the cache. If not, it asks AD for every attribute that has a value, which will usually be a whole lot more data than you actually need. Since you're looping over a bunch of accounts, that can significantly slow you down.
You are already asking for the sAMAccountName in the search results, since you used searcher.PropertiesToLoad.Add("sAMAccountName"), so you can pull the value from the SearchResult object directly:
lvwListView.Items.Add((string) entry.Properties["sAMAccountName"][0]);
I talk more about that in an article I wrote about better performance when programming with Active Directory.
Now to actually answer your question:
Getting the members is a different story. You can ask for the member attribute in the search, but you get problems when a group has more than 1500 members. You have to ask for the members in pages. But also, you have to decide how you are going to handle nested groups: when a group is a member of a group. Do you want to just list that group name? Or do you want to look inside that group and get those members too?
I wrote a whole article about this too: Find all the members of a group
But it's more than likely you can just use one of the sample methods I show in that article. This will take a DirectoryEntry object of a group, and give you a list of strings containing the DOMAIN\username of each object inside the group (you can change that if you want). You can use the recursive parameter to decide what you want to do with nested groups.
public static IEnumerable<string> GetGroupMemberList(DirectoryEntry group, bool recursive = false) {
var members = new List<string>();
group.RefreshCache(new[] { "member" });
while (true) {
var memberDns = group.Properties["member"];
foreach (string member in memberDns) {
using (var memberDe = new DirectoryEntry($"LDAP://{member.Replace("/", "\\/")}")) {
memberDe.RefreshCache(new[] { "objectClass", "msDS-PrincipalName", "cn" });
if (recursive && memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(memberDe, true));
} else {
var username = memberDe.Properties["msDS-PrincipalName"].Value.ToString();
if (!string.IsNullOrEmpty(username)) {
members.Add(username);
}
}
}
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
You would call it from your code like this:
var members = GetGroupMemberList(entry.GetDirectoryEntry());
Then you can display them however you want.
This will work fine if you are only working in a single AD forest. But if your domain trusts other domains outside your forest and you can expect to see users from those domains in the groups you are looking at, then you need to account for that. But I cover that in that article. I also cover primary groups there too, which is rare that you need to care about, but you might.
Update: To add two columns in a ListViewItem, you have to create it separately. You can use the constructor that takes a string array of the "subitems", as they call it.
You could put something like this inside your loop where you loop through the members, where groupName is the name of the group and member is the name of the group member.
lvwListView.Items.Add(new ListViewItem(new [] {
groupName,
member
});
If you only want the groupName on the first one, then you can just put an empty string ("") instead of groupName for the others.

LINQ Expression - Returning a UserID based on the Name Specified in a LinkLabel

Please, I am trying to access my database to get access to a user id. But when i try accessing, what gets returned is 0. Initially, i had issues figuring out how to change this SQL statement (SELECT user_id FROM users WHERE firstname = 'Godymn') to LINQ. But i later found that i could use :
var result = users.Where(x=>x.firstname == "Godymn").Select(x=>x.userid);
So far, when i tried to use the query in my code, it returns a 0. But what i need is to return the user id based on the name specified in the linkLabel. I have not been able to figure out why its not working.
Will appreciate any help.
Here is my code
private int Chk()
{
string link = linkLabelPartner.Text;
var result = (dynamic)null;
using (DbModel db = new DbModel())
{
result = db.users.Where(x => x.firstname == link).Select(x => x.user_id).FirstOrDefault();
}
return result;
}
Here is the button i used to check the content of what got returned.
private void btnTey_Click(object sender, EventArgs e)
{
int test = Chk();
MessageBox.Show(test.ToString());
}
Good Afternoon Godymn.
Here are some possible scenarios:
1 - The user name Godymn does not exist in the database
2 - The user_id is 0.
Try checking this 2 scenarios.
After that, you could change your approach to a more "debug friendly" level.
var result = db.users.First(f => f.firstname == link);
Then, check if result is null. If it is null, the name was not found in the database.
If it is not null, check if the result is 0.
Why this approach: This way, you can tell if the problem is when you are searching for the user or when you are binding the value of Id.
I can't comment, i'm sory, but first of all i'll try this:
var foo = db.users.Select(x => x).ToList();
then you can investigate your result like this
i suppose you try get wrong data (or wrong structure)
when i stuck i do this:
Or maybe you can attach your structure of object?
So yeah, I was able to fix the bug. The problem was with the linkLabelPartner.Text as Chris Dunaway rightly said. As soon as he mentioned if the linklabel contains only firstname, I just knew that was the missing piece. This holds firstname and lastname and I only accounted for just firstname. Hence the value returned remains at 0. So, all I had to do was to concatenate both firstname and lastname which solved the issue. So, yeah the query I used initially was right.
Here is the code
private int Chk()
{
string link = linkLabelPartner.Text;
var result = (dynamic)null;
using (DbModel db = new DbModel ())
{
result = db.users.Where(x => x.lastname + " " + x.firstname == link ).Select(x => x.users).FirstOrDefault();
}
return result;
}
private void btnTey_Click(object sender, EventArgs e)
{
int test = Chk();
MessageBox.Show(test.ToString());
}
Many thanks everyone for your suggestions, appreciate.

How can I avoid adding duplicate rows to my list view control?

I have a list view control that I want to add accounts to. The first column of the list view is for an account's username, and the second column is for the account's password.
I want avoid adding duplicate rows of login credentials. What I mean by that is that I don't care if a username appears in the list view control more than once. Just as long as each instance of the username has a different password. The same rule applies to passwords. I don't care if multiple accounts have the same password.
I just want to avoid duplicate ROWS.
The following code is something I have tried with no success:
private void AddAccounts()
{
List<string> usernames = new List<string>();
usernames.Add("Margaret Parker");
usernames.Add("Steven Stewart");
usernames.Add("Heather Powell");
usernames.Add("Denise Simmons");
usernames.Add("Ronald Moore");
List<string> passwords = new List<string>();
passwords.Add("mExEvHb3");
passwords.Add("muFLdtHu");
passwords.Add("GrcSNCyY");
passwords.Add("S8qenUZY");
passwords.Add("PVAzFYyu");
// usernames.Count will always be the same as passwords.Count
// so it doesn't matter which property I use.
for (int i = 0; i < usernames.Count; i++)
{
ListViewItem lvi = new ListViewItem(usernames[i]);
lvi.SubItems.Add(passwords[i]);
if (!accountsListView.Items.Contains(lvi))
{
// It is unsafe to call a control from a thread other
// than the one that created the control without using
// the Invoke method.
Invoke((MethodInvoker)delegate { accountsListView.Items.Add(lvi); });
}
}
}
When AddAccounts() is called, it will add the following items to accountsListView:
/*
# Margaret Parker mExEvHb3
# Steven Stewart muFLdtHu
# Heather Powell GrcSNCyY
# Denise Simmons S8qenUZY
# Ronald Moore PVAzFYyu
*/
It will not take into consideration if an account has already been added to the list view control. Which is why I'm coming to you folks for help.
How can I avoid adding duplicate rows to a list view control?
Thank you for taking the time to read my question.
I want to ALLOW multiple instances of the same username OR password:
/*
# JamesEdwards ---- LZsDVQ7A ---- different password (GOOD!)
# GeraldLopez ---- LZsDVQ7A
# JamesEdwards ---- 7cbrPRzt ---- different password (GOOD!)
*/
I want to avoid EXACT duplicates:
/*
# PhillipAnderson ---- 4ZN5TKfM ---- exact duplicate (BAD!)
# NicholasPowell ---- 4ZN5TKfM
# PhillipAnderson ---- 4ZN5TKfM ---- exact duplicate (BAD!)
*/
If an account's password ever gets changed, then I want to be able to add that account again to my list view control with the new password. I want to keep the old account information in my list view control as well. That is why I want to allow duplicate usernames in my list view control.
You are creating a new instance of ListViewItem and validating existence in the collection, which performs reference check and returns false all the time.
instead of this accountsListView.Items.Contains(lvi)
do this
ListViewItem item = accountsListView.FindItemWithText(userNames[i]);
if (item == null)
{
// logic goes here.
}
or
bool found = false;
foreach(ListViewItem lv in accountsListView.Items)
{
if(lv.Text ==userNames[i])
{
found = true;
break;
}
}
if(!found)
{
// logic goes here.
}
Make a note ListView also provides FindItemWithText to validate existence of an item.
There are few things that you need to change in your code. You do not have to use 2 lists for this purpose. One list should be enough. You can use available features in C# to do this kind of things more efficiently.
So as the first step; create a class and inherit it from IEqualityComparer. We have to override Equals and GetHashCode methods to make it possible to be used in Linq etc.
public class UserAccount : IEqualityComparer<UserAccount>
{
public string UserName { get; set; }
public string Password { get; set; }
public bool Equals(UserAccount x, UserAccount y)
{
return (x.UserName == y.UserName && x.Password == y.Password);
}
public int GetHashCode(UserAccount obj)
{
return obj.UserName.GetHashCode() + obj.Password.GetHashCode();
}
}
Then use the above created class to create a List of user accounts.
var userAccounts = new List<UserAccount>
{
new UserAccount() {UserName = "PhillipAnderson", Password = "4ZN5TKfM"},
new UserAccount() {UserName = "NicholasPowell", Password = "4ZN5TKfM"},
new UserAccount() {UserName = "PhillipAnderson", Password = "4ZN5TKfM"}
};
Now you can use Distinct method to filter out duplicate user accounts. you will notice uniqueUserAccounts has only 2 user accounts after applying Distinct.
var uniqueUserAccounts = userAccounts.Distinct(new UserAccount()).ToList();
foreach (var uniqueUserAccount in uniqueUserAccounts)
{
//your code to add user accounts to the list view
}

C#: recursive search in two different data structures

I need to perform a search in two different data structures in C#, and here's the deal:
I have one name (which is a string) and I want to perform a search. I have a function called Exists which will return a bool indicating whether it exists or not.
In case it exists, I increase the name (simply adding a 1 at the end of the string), and then I need to perform the search again (via method exists) to see if an object with the new name exists.
This would go on until there's an unused name, which I could use, BUT, in case it doesn't exist, now I should perform a search another data structure which contains the objects that were deleted, and if the string is found there, then I'd have to increase the name again, and start searching since the beginning.
This would all end in case there's no object with such name neither using Exists method nor in the data structure where all the deleted objects are.
How could I approach this problem?
I hope I expressed myself clearly :-)
Thanks a lot in advance!
string BuildNextName(string originalName)
{
string name = originalName;
while( Exists(name) || deletedNames.Contains(name))
{
name = Increment(name);
}
return name;
}
Or did I miss something?
Using a for loop:
string BuildNextName(string originalName)
{
for (string name=originalName;
Exists(name) || deletedNames.Contains(name);
name = Increment(name));
return name;
}
BTW, I guess your name incrementation algorithm is more complex than simply adding 1: name, name1, name2,... Basically, if the name doesn't end in a number, you append "1". If it does, you increment that number. right?
a non recursive and simple solution could be something like this ( I don't see any need of recursion in this case)
//pseudocode
String name;
bool condition = true;
while(condition)
{
if(ExistInFirstDataStructure(name))
{
//increment name
}
else
{
if(ExistInDeletedDataStructure(String name))
{
//increment name
}
else
{
condition = false;
}
}
}
bool ExistInFirstDataStructure(String name)
{
}
bool ExistInDeletedDataStructure(String name)
{
}
Why use a loop at all?? (I know LINQ will under the hood)
var LastUsedObjectName =
MyObjects.Select(mo => mo.Name)
.Union( MyDeletedObjects.Select(mo => mo.Name))
.OrderByDescending(name => /*Function to order by integer part of name*/).First();
// Now add 1 to LastUseObjectName and use that.
How about this one:
var listOfExistingNames = new List<string> { "MyName", "MyName1", "MyName3" };
var listOfDeletedNames = new List<string> { "MyName2", "MyName5" };
int counter = 0;
string baseToFindFreePlace = "MyName";
string newName = baseToFindFreePlace;
var allNames = listOfExistingNames.Concat(listOfDeletedNames);
while (allNames.Contains(newName))
{
counter++;
newName = baseToFindFreePlace + counter;
}
listOfExistingNames.Add(newName);
if you create Exists methods for both data structures, you can search with recursion like this:
pseudo code:
string resultName;
void Search(string name)
{
if(ExistsInFirstStructure(name)) //name is in first data structure
Search(name + "1"); //add 1 and try again
else
if(ExistsInSecondStructure(name)) //name exists in second data structure
Search(name + "1"); //perform search again
else
resultName = name; //current name wasn't found in first and second data structures - we have result
}

Categories

Resources