How to check if a value exists in a list? - c#

I'm relatively new to C# programming (programming as a whole, actually), but I've built an application to manage the application pools on a server that my team at work uses. It does everything it's supposed to fairly well, but the only issue I'm running into is in saving previously-used configurations to the app.config file so the user doesn't have to put them in manually every time. As it stands, I can save to and load from the file magnificently (along with all of the strings I need in each group).
The issue is that I want to do a cursory check to see if a Name string exists in the group before writing it. Example of the part of the app.config:
<appSettings>
<add Name="RowName" MachineName="MS-02348" AppSrvName="AppServer" WebSrvName="AppNet"/>
<add Name="RowName2" MachineName="MS-68186" AppSrvName="AppServer2" WebSrvName="AppNet2"/>
</appSettings>
So what I'm currently doing to load the values is I have a method that retrieves the appSettings/add nodes and throws them into a list, then sets the values to properties of an object. The reason I do this is so that I can have a drop-down that lists only the Name of an object, and then the rest of the information is all available for when I call the method on the selected item.
Anyway, what I'm running into now is that I want to make sure that if the Name already exists in the app.config, I prompt the user to write another name instead of saving it to the database. Having two child nodes with the same "Name" value would wreak havoc on my logic.
I tried a foreach to cycle through the objects in the list, but without knowing how many objects there could be I didn't know of an easy way of really saying it does or does not exist. I've also tried targeting the childnode based on the values listed in the node, but it seems to fail there too. I'm guessing that part is syntax, but it seems to match up with how the method list defines it.
Any thoughts?

if (list.Any())
{
// found something!
}
else
{
// found nothing
}
I always use Any() simply because it's the most performant. List.Count() goes through each item and counts them, but you don't care about the number of items -- you only care if there's an item at all. Any() will enumerate through the list and stop if it finds an item. So, in the extreme case, in a list of a million items Count() will enumerate every single one and return while Any() will enumerate one and return.
Plus, it returns a bool, which is handy for more concise code. :)
As an added bonus, you can call Any() looking for specific things. So, in a list of people I can look to see if there are any people older than 21 in it:
if (list.Any(person => person.Age > 21))
{
// ...
}
Edit: Formatting.

maybe something like this
var list = new List<AppSettings>();
var item = list.FirstOrDefault(x => x.Name == NameEnteredByUser);
if (item == null)
{
//there is no such item
}
else
{
//notify the user
}
or the Any extension method:
var list = new List<AppSettings>();
if (list.Any(x => x.Name == NameEnteredByUser))
{
//name exists
}
else
{
//no such name used before
}
As a sidenote, have a unique field configured in your database so that when your programming logic fails you wont enter a record. corrupt data state in db is bad.

neo112 is correct in his logic, but I am unsure if the main problem you have is performance related, since you mention you dont know if it may get too long.
First, you could also do the following;
int count = list.Count(a => a.Name == NameEnteredByUser);
if(count > 0)
{
// exists
}
I believe .Count() is faster than .First() (anecdotal evidence only) and personally think it's a bit cleaner.
Also, another thing you could try to do is to sort your list by name when adding to the appSettings node. Then, you should instantiate a SortedList instead of just List, then that would also (possitively) affect performance. But, I am unsure if sorting is an option for you.

Related

Add record to DbContext by returning null with a query

I did a research I not found anything so far. Appreciate your help.
As the title says, I'm adding a record to my DbContext by doing a query, and if it returns a null value then I create the record. Otherwise, I modify it.
My question is: Is this a good practice or I shouldn't do this that way? The code is like this:
var desiredEntity = MyDbContext.SingleOrDefault(x => x.Name == "Rob");
if (desiredEntity == null)
{ desiredEntity = new DbSetOfEntity()... //create if doesn't exists }
else
{ desiredEntity.ValueToModify == 3; }
MyDbContext.SaveChanges()
What is the difference between this and creating an entity explicitly and adding it with the DbContext.Add() method?
Edit:
A similar question here is asked, but I don't want to use AddOrUpdate method or Attach and changing the state of the entities manually. I just want to know if my approach is not problematic and what are the differences by using the conventional .Add(myEntity) approach.
AddOrUpdate() is checking record for existance by EntityKey. So if x.Name which you are checking is not Key - your approach is correct.
Also as you have only name value as an input parameter ("Rob") you will have to request record from context, and you will have it attached. So then you can just change it as you need and the changes will be tracked automatically. Alternatively, create it if it doesn't exist and call Add().
Taking into account the above your approach is OK and will work and I dont see any problems with it.
On the other hand, your code should be as simple and elegant as it can be. It is important for supporting your code by you or by your teammates. That is why using constructions like AddOrUpdate or other built-in stuff is good practice. But not a law.
P.S. I am not looking at code like { desiredEntity.ValueToModify == 3; }. This is potentially bad practice as 3 should perhaps be passed to your method as a parameter.

Querying LDAP in C# to get list of computers

I am working with LDAP in my Windows Forms C# project.
I created a CheckListBox, and started to create a method that queries the Active Directory for all my computers is the environment.
The method is:
public string ComputerList()
{
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://MyDomain.Local");
DirectorySearcher computerSercher = new DirectorySearcher(rootDSE);
computerSercher.PageSize = 10000;
computerSercher.Filter = "(&(objectClass=computer))";
}
I am also have as I said, a CheckListBox.
What I want to do is to have a result for the query and every computer that founds. add it to the Items property for the CheckListBox.
But I don't how even approach the result. it's not like PowerShell that gives you a list of objects...
Thank you
You're almost there. A few things:
Set the page size to 1000. AD won't give you any more than 1000 at a time, so if you set it to anything over that you'll only get 1000 (if DirectorySearcher doesn't get back what it considers a full page, it'll stop asking)
Add the attributes you want to read to the PropertiesToLoad collection. If you don't add anything, it'll give you every attribute with a value, which is a bunch of unnecessary data you won't use. You'll likely only want to see the cn attribute (Common Name).
Use FindAll() to get the results. Make sure you wrap this in a using statement to prevent memory leaks (the documentation says so).
When you look at the results, every property is presented as an array, whether it is or not in AD. So you'll need to use [0] in most cases. For future reference (not applicable here): if a property is not set in AD, it won't be in the Properties collection at all, so, for optional attributes, you'll have to use Properties.Contains() to see if it's there first.
Working from what you have, here is a method that will return a list of computer names:
public IEnumerable<string> ComputerList()
{
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://MyDomain.Local");
DirectorySearcher computerSercher = new DirectorySearcher(rootDSE)
{
PageSize = 1000,
Filter = "(&(objectClass=computer))"
};
computerSercher.PropertiesToLoad.Add("cn");
using (var results = computerSercher.FindAll())
{
foreach (SearchResult result in results)
{
yield return (string) result.Properties["cn"][0];
}
}
}
Update: To answer your questions in your comment:
The yield basically tells it to "add this item to the collection that will be returned". There is a little more going on in the background, which you can read about here. But in simplest terms, it saves you from having to create your own list, add items to that list and return the list.
I changed the return type from string to IEnumerable<string> because you are getting multiple results from your search, so I assume you want to return all of those results. This method will give you a list of computer names, not just one computer name.
FindAll() returns a SearchResultCollection. For some reason I don't know, the objects returned from SearchResultCollection in a foreach are presented as object. So you need to cast them to SearchResult explicitly to use them.

Difficulty in Removing Items From List?

I have two lists. The first is of all students and the second is of selected students. I want if I one time select some student they will remove from the all-student list. Here is my code but it doesn't. Students won't get removed.
foreach (var li in ListSelectedStudents.ToList())
{
if (ListAllStudents.Contains(li))
{
ListAllStudents.Remove(li);
}
}
Contains will use equality to determine what is "equal", I am assuming here that your custom class hasn't provided a custom equality implementation, which means the default equatable will be provided for that type and it's just using reference equality. So even though you think two things are "equal", the Contains method doesn't and so doesn't step into the Remove call.
To get that particular code to behave what you need to do is provide an implementation of IEquatable<Student> on the Student class, as described in the remarks here.
In this instance, Contains isn't actually required as Remove will do the same checks. If there is nothing to remove, the Remove call will be transparent, effectively doing nothing.
As has been caught in the comments before I had chance to provide the information, Remove will also rely on IEquatable<Student> (docs) so you still need to provide an implementation, but it will make your code look a little cleaner:
foreach (var li in ListSelectedStudents.ToList())
{
ListAllStudents.Remove(li);
}
There may be various ways to do this without the need to implement the interface, but you won't be able to use your current code for it. I'll leave other answers to field those alternatives as it's Friday and my brain is not yet functioning properly.
have you tried using linq:
ListAllStudents.RemoveAll(m => ListSelectedStudents.Contains(m));
if it does not work, it could be something wrong with the default comparison implemented in the object, and you could either fix the comparer, or do something like:
ListAllStudents.RemoveAll(m => ListSelectedStudents.Any(n=>n.Id == m.Id)); // Assume the Id is the primary key of the object...
Try this:
ListSelectedStudents = ListSelectedStudents.Where(a => !ListSelectedStudents.Contains(a)).Select(a => a).ToList();

Populating a Dictionary with PdfOutlines

So I am attempting to write a program that combines a PDF from a list of PDF's that are already available. I've got most of it done to this point, but I'm having an issue with one step in particular. I'm also writing this in c# if that isn't apparent.
I have an array of strings that contains a category name, and for each category name I want to create a variable of type PdfOutline that gets initialized to null that I can iterate through later in the program.
I've tried to look into this myself and it seems like dictionary is the way to do it, but I'm not really sure how to go about it. Firstly, is making a dictionary the right way to do it and secondly I need help with implementation.
Thanks for your time!
Something like the following would work (not syntax-checked this, sorry):
// This requires the array of Categories to have no duplicates.
public Dictionary<string, PdfOutline> BuildUpMyCollectionOfOutlines(string[] categories)
{
return categories.ToDictionary(cat => new KeyValuePair<string, PdfOutline>(cat, null));
}
If you do it this way, then you can later consume the result as such (although having a function to do this is silly, is just my way to show you how to consume it):
public PdfOutline GetOutlineByCategory(Dictionary<string, PdfOutline> outlines, string category)
{
// This will be problematic if the category isn't actually in the dictionary.
return outlines[category];
}
Whether you should use a Dictionary<string, PdfOutline> versus something else, like a List<KeyValuePair<string, PdfOutline>> depends on 1) how many of these you'll have and 2) how you'll be accessing them. For example, if you have 10,000 of them and you need to randomly and repeatedly find them by Category name, then the Dictionary is the right approach because it hashes things for quicker searching (think an index in a table in a database). However, if you have 10,000 but only need to find 2 of them, or vice versa, only have 10 of them, then the overheads in building up that quick-searching capability is wasted. So Dictionary vs other is best answered by the question, "If this was in a database table, would you index it?"

Common problem for me in C#, is my solution good, stupid, reasonable? (Advanced Beginner)

Ok, understand that I come from Cold Fusion so I tend to think of things in a CF sort of way, and C# and CF are as different as can be in general approach.
So the problem is: I want to pull a "table" (thats how I think of it) of data from a SQL database via LINQ and then I want to do some computations on it in memory. This "table" contains 6 or 7 values of a couple different types.
Right now, my solution is that I do the LINQ query using a Generic List of a custom Type. So my example is the RelevanceTable. I pull some data out that I want to do some evaluation of the data, which first start with .Contains. It appears that .Contains wants to act on the whole list or nothing. So I can use it if I have List<string>, but if I have List<ReferenceTableEntry> where ReferenceTableEntry is my custom type, I would need to override the IEquatable and tell the compiler what exactly "Equals" means.
While this doesn't seem unreasonable, it does seem like a long way to go for a simple problem so I have this sneaking suspicion that my approach is flawed from the get go.
If I want to use LINQ and .Contains, is overriding the Interface the only way? It seems like if there way just a way to say which field to operate on. Is there another collection type besides LIST that maybe has this ability. I have started using List a lot for this and while I have looked and looked, a see some other but not necessarily superior approaches.
I'm not looking for some fine point of performance or compactness or readability, just wondering if I am using a Phillips head screwdriver in a Hex screw. If my approach is a "decent" one, but not the best of course I'd like to know a better, but just knowing that its in the ballpark would give me little "Yeah! I'm not stupid!" and I would finish at least what I am doing completely before switch to another method.
Hope I explained that well enough. Thanks for you help.
What exactly is it you want to do with the table? It isn't clear. However, the standard LINQ (-to-Objects) methods will be available on any typed collection (including List<T>), allowing any range of Where, First, Any, All, etc.
So: what is you are trying to do? If you had the table, what value(s) do you want?
As a guess (based on the Contains stuff) - do you just want:
bool x= table.Any(x=>x.Foo == foo); // or someObj.Foo
?
There are overloads for some of the methods in the List class that takes a delegate (optionally in the form of a lambda expression), that you can use to specify what field to look for.
For example, to look for the item where the Id property is 42:
ReferenceTableEntry found = theList.Find(r => r.Id == 42);
The found variable will have a reference to the first item that matches, or null if no item matched.
There are also some LINQ extensions that takes a delegate or an expression. This will do the same as the Find method:
ReferenceTableEntry found = theList.FirstOrDefault(r => r.Id == 42);
Ok, so if I'm reading this correctly you want to use the contains method. When using this with collections of objects (such as ReferenceTableEntry) you need to be careful because what you're saying is you're checking to see if the collection contains an object that IS the same as the object you're comparing against.
If you use the .Find() or .FindAll() method you can specify the criteria that you want to match on using an anonymous method.
So for example if you want to find all ReferenceTableEntry records in your list that have an Id greater than 1 you could do something like this
List<ReferenceTableEntry> listToSearch = //populate list here
var matches = listToSearch.FindAll(x => x.Id > 1);
matches will be a list of ReferenceTableEntry records that have an ID greater than 1.
having said all that, it's not completely clear that this is what you're trying to do.
Here is the LINQ query involved that creates the object I am talking about, and the problem line is:
.Where (searchWord => queryTerms.Contains(searchWord.Word))
List<queryTerm> queryTerms = MakeQueryTermList();
public static List<RelevanceTableEntry> CreateRelevanceTable(List<queryTerm> queryTerms)
{
SearchDataContext myContext = new SearchDataContext();
var productRelevance = (from pwords in myContext.SearchWordOccuranceProducts
where (myContext.SearchUniqueWords
.Where (searchWord => queryTerms.Contains(searchWord.Word))
.Select (searchWord => searchWord.Id)).Contains(pwords.WordId)
orderby pwords.WordId
select new {pwords.WordId, pwords.Weight, pwords.Position, pwords.ProductId});
}
This query returns a list of WordId's that match the submitted search string (when it was List and it was just the word, that works fine, because as an answerer mentioned before, they were the same type of objects). My custom type here is queryTerms, a List that contains WordId, ProductId, Position, and Weight. From there I go about calculating the relevance by doing various operations on the created object. Sum "Weight" by product, use position matches to bump up Weights, etc. My point for keeping this separate was that the rules for doing those operations will change, but the basic factors involved will not. I would have even rather it be MORE separate (I'm still learning, I don't want to get fancy) but the rules for local and interpreted LINQ queries seems to trip me up when I do.
Since CF has supported queries of queries forever, that's how I tend to lean. Pull the data you need from the db, then do your operations (which includes queries with Aggregate functions) on the in-memory table.
I hope that makes it more clear.

Categories

Resources