Why does my IEnumerator<AppiumWebElement> not have .Where()? - c#

The title mostly explains my question, my IEnumerable<AppiumWebElement> does not have a .Where method, and I'm hoping to learn why.
I have a method that returns an IEnumerable<AppiumWebElement>, as follows:
public IEnumerable<AppiumWebElement> GetLayers()
{
var items = PaneOrView.FindElementsByClassName("TreeViewItem");
List<AppiumWebElement> layers = new List<AppiumWebElement>();
foreach(AppiumWebElement item in items)
{
string automationId = item.AutomationId();
if (automationId != null && automationId.Contains("Layer"))
{
layers.Add(item);
}
}
return layers.AsEnumerable();
}
When I check whether .Where is present on the return item in this method (using code completion), I get a full list of methods I would expect to see from an IEnumerable.
However, when I later use this method from another namespace, for example like this:
var test = Contents.GetLayers();
The result in test doesn't contain .Where or any of the other methods I would expect to see from an IEnumerable. Any idea why?
Thanks for any pointers!

Where() and other methods in Linq are extension methods that you are using. You need to import System.Linq:
using System.Linq;

Related

What's the syntax for this linq expression calling the in method

I have to query a repository where column a = {myvalue} and column b has any value in a collection.
Here's what I have:
Application[] applicationCollection = GetAllApplications();
var result = repo.GetAll(r => r.Email == myEmail && r.Application.In(applicationCollection));
I can't figure out how to write the "in" part...
Preferrably links to how to do this would be best, so I can learn it, as opposed to just getting the answer :). Thanks all.
(I'm sure this is a repeat question, but my Google/search skills are obviously poor.)
The SQL idea of item in collection is written in C# (including LINQ) as collection.Contains(item). In your example, this might be:
var result = repo.GetAll(r => r.Email == myEmail &&
applicationCollection.Contains(r.Application));
ApplicationCollection.Contains(r.Application)
Use .Contains(collection) instead of in. Here's a link since you wanted one.
If you want to write it the way you've shown, you can write this extension method:
public static bool In<T>(this T item, IEnumerable<T> set)
{
return set.Contains(item);
}
And then use it exactly like you did in your question:
Application[] applicationCollection = GetAllApplications();
var result = repo.GetAll(r =>
r.Email == myEmail &&
r.Application.In(applicationCollection));
This will work fine if you're only working with in-memory sets. If you're doing something like LINQ-to-SQL, however, this won't be compatible.
Note: I'm not trying to imply that this extension method might be a good idea to use - I'm just saying it's possible to write one like that.

Linq query against a collection property with a collection parameter

I have a method which takes an array of strings as parameter and queries against a collection property which is also a collection of strings. If that property has one of the values inside the string array passed as parameter, it should be returned.
Here is my code:
public IEnumerable<BlogPost> GetAll(string[] tags,
bool includeUnapprovedEntries = false) {
foreach (var tag in tags) {
foreach (var blogPost in GetAll(includeUnapprovedEntries).
ToList().Where(x => x.Tags.Any(t => t == tag))) {
yield return blogPost;
}
}
}
Note:
Here is the complete code:
https://github.com/tugberkugurlu/MvcBloggy/blob/master/src/MvcBloggy.Data/DataAccess/SqlServer/BlogPostRepository.cs
This does the job but it just doesn't seem right. I could have made this better with some extension methods but couldn't figure out what would do the trick and make this implementation right.
Any idea?
How about this:
public IEnumerable<BlogPost> GetAll(string[] tags,
bool includeUnapprovedEntries = false) {
return GetAll(includeUnapprovedEntries)
.Where(x => x.Tags.Any(t => tags.Contains(t));
}
You may want to call ToList() to materialize the result. Note that this will (hopefully!) result in an IN query in SQL; if you have a large number of tags, I wouldn't be surprised if that failed. (I don't know how the Entity Framework handles that situation.) I believe it should be okay with smaller numbers of tags though.
Note that whether or not this is supported may depend on the version of the entity framework you're using; I seem to remember that some transformations like this (using Contains on a "local" collection) to translate to IN in SQL) have improved over time. Make sure you develop against the same version you'll be deploying against :)

Why does this linq extension method hit the database twice?

I have an extension method called ToListIfNotNullOrEmpty(), which is hitting the DB twice, instead of once. The first time it returns one result, the second time it returns all the correct results.
I'm pretty sure the first time it hits the database, is when the .Any() method is getting called.
here's the code.
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value.IsNullOrEmpty())
{
return null;
}
if (value is IList<T>)
{
return (value as IList<T>);
}
return new List<T>(value);
}
public static bool IsNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value != null)
{
return !value.Any();
}
return true;
}
I'm hoping to refactor it so that, before the .Any() method is called, it actually enumerates through the entire list.
If i do the following, only one DB call is made, because the list is already enumerated.
var pewPew = (from x in whatever
select x)
.ToList() // This enumerates.
.ToListIsNotNullOrEmpty(); // This checks the enumerated result.
I sorta don't really want to call ToList() then my extension method.
Any ideas, folks?
I confess that I see little point in this method. Surely if you simply do a ToList(), a check to see if the list is empty suffices as well. It's arguably harder to handle the null result when you expect a list because then you always have to check for null before you iterate over it.
I think that:
var query = (from ...).ToList();
if (query.Count == 0) {
...
}
works as well and is less burdensome than
var query = (from ...).ToListIfNotNullOrEmpty();
if (query == null) {
...
}
and you don't have to implement (and maintain) any code.
How about something like this?
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value == null)
return null;
var list = value.ToList();
return (list.Count > 0) ? list : null;
}
To actually answer your question:
This method hits the database twice because the extension methods provided by the System.Linq.Enumerable class exhibit what is called deferred execution. Essentially, this is to eliminate the need for constructing a string of temporarily cached collections for every part of a query. To understand this, consider the following example:
var firstMaleTom = people
.Where(p => p.Gender = Gender.Male)
.Where(p => p.FirstName == "Tom")
.FirstOrDefault();
Without deferred execution, the above code might require that the entire collection people be enumerated over, populating a temporary buffer array with all the individuals whose Gender is Male. Then it would need to be enumerated over again, populating another buffer array with all of the individuals from the first buffer whose first name is Tom. After all that work, the last part would return the first item from the resulting array.
That's a lot of pointless work. The idea with deferred execution is that the above code really just sets up the firstMaleTom variable with the information it needs to return what's being requested with the minimal amount of work.
Now, there's a flip side to this: in the case of querying a database, deferred execution means that the database gets queried when the return value is evaluated. So, in your IsNullOrEmpty method, when you call Any, the value parameter is actually being evaluated right then and there -- hence a database query. After this, in your ToListIfNotNullOrEmpty method, the line return new List<T>(value) also evaluates the value parameter -- because it's enumerating over the values and adding them to the newly created List<T>.
You could stick the .ToList() call inside the extension, the effect is slightly different, but does this still work in the cases you have?
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if(value == null)
{
return null;
}
var result = value.ToList();
return result.IsNullOrEmpty() ? null : result;
}

Cast from filtered custom List<T> with LINQ

I have a custom list which inherits from Generic.List<T> like this:
public class TransferFileList<T> : List<TransferFile> { .. }
When I set (where 'Files' is a TransferFileList<T>):
var files = uploadResponse.Files.Where(x => !x.Success).ToList()
the 'files' object resolves as System.Collections.Generic.List<TransferFile>, not TransferFileList<T>, which is what I would expect as it was what was being filtered through the Where, so how could I successfully return a list of TransferFileList<T> into 'files'?
I did try:
var files = uploadResponse.Files.Where(x => !x.Success).ToList()
as TransferFileList<TransferFile>;
but using that safe cast, it just resolves as null.
Thanks guys and gals.
First, I have to ask why you are inheriting from List<T>? 99% of the time that's a bad idea.
If you want to extend the functionality of a list, use extension methods:
public static something PrintErrors(this List<TransferFile> list)
{
//do your printing logic
}
On to the answer: ToList() operates on an IEnumerable<T> and converts the members of the sequence to a List of the same type. Since you inherit from List<T> which implements IEnumerable<T>, that's what happens there.
Where() works the same way - operates on an IEnumerable<T> and returns an IEnumerable<T>.
To get some arbitrary list-like object back, like you have, you need to add the items in a sequence to your custom list, like so:
var myFiles = new TransferFileList<TransferFile>();
myFiles.AddRange(originalFileList.Where(condition));
You can add an extension method for IEnumerable<TransferFile> to handle that scenario:
public static TransferFileList ToTransferFileList(
this IEnumerable<TransferFile> files)
{
return new TransferFileList(files);
}
// ...
var files = uploadResponse.Files.Where(x => !x.Success).ToTransferFileList();
This provides you with the TransferFileList instead of just a List<TransferFile>. Note the reason your as returns null is because while TransferFileList is a List<TransferFile>, the same does not hold in the other direction. That is, your List<TransferFile> is NOT a TransferFileList object.
I agree with #RexM that any attempt at subclassing List<T> be avoided due to the multitude of pitfalls associated. I suggest Composition (Has-A rather than Is-A) or sticking with the base class library collections instead.
Thanks guys.
I like SLV's extension approach, but is there any other straight casting approach?
If not I might just go with the reverted in-line approach I was hoping to avoid:
var transferFiles = new TransferFileList<TransferFile>();
if (files != null)
transferFiles.AddRange(files);

Using Linq to run a method on a collection of objects?

This is a long shot, I know...
Let's say I have a collection
List<MyClass> objects;
and I want to run the same method on every object in the collection, with or without a return value. Before Linq I would have said:
List<ReturnType> results = new List<ReturnType>();
List<int> FormulaResults = new List<int>();
foreach (MyClass obj in objects) {
results.Add(obj.MyMethod());
FormulaResults.Add(ApplyFormula(obj));
}
I would love to be able to do something like this:
List<ReturnType> results = new List<ReturnType>();
results.AddRange(objects.Execute(obj => obj.MyMethod()));
// obviously .Execute() above is fictitious
List<int> FormulaResults = new List<int>();
FormulaResults.AddRange(objects.Execute(obj => ApplyFormula(obj)));
I haven't found anything that will do this. Is there such a thing?
If there's nothing generic like I've posited above, at least maybe there's a way of doing it for the purposes I'm working on now: I have a collection of one object that has a wrapper class:
class WrapperClass {
private WrappedClass wrapped;
public WrapperClass(WrappedClass wc) {
this.wrapped = wc;
}
}
My code has a collection List<WrappedClass> objects and I want to convert that to a List<WrapperClass>. Is there some clever Linq way of doing this, without doing the tedious
List<WrapperClass> result = new List<WrapperClass>();
foreach (WrappedClass obj in objects)
results.Add(new WrapperClass(obj));
Thanks...
Would:
results.AddRange(objects.Select(obj => ApplyFormula(obj)));
do?
or (simpler)
var results = objects.Select(obj => ApplyFormula(obj)).ToList();
I think that the Select() extension method can do what you're looking for:
objects.Select( obj => obj.MyMethod() ).ToList(); // produces List<Result>
objects.Select( obj => ApplyFormula(obj) ).ToList(); // produces List<int>
Same thing for the last case:
objects.Select( obj => new WrapperClass( obj ) ).ToList();
If you have a void method which you want to call, here's a trick you can use with IEnumerable, which doesn't have a ForEach() extension, to create a similar behavior without a lot of effort.
objects.Select( obj => { obj.SomeVoidMethod(); false; } ).Count();
The Select() will produce a sequence of [false] values after invoking SomeVoidMethod() on each [obj] in the objects sequence. Since Select() uses deferred execution, we call the Count() extension to force each element in the sequence to be evaluated. It works quite well when you want something like a ForEach() behavior.
If the method MyMethod that you want to apply returns an object of type T then you can obtain an IEnumerable<T> of the result of the method via:
var results = objects.Select(o => o.MyMethod());
If the method MyMethod that you want to apply has return type void then you can apply the method via:
objects.ForEach(o => o.MyMethod());
This assumes that objects is of generic type List<>. If all you have is an IEnumerable<> then you can roll your own ForEach extension method or apply objects.ToList() first and use the above syntax .
The C# compiler maps a LINQ select onto the .Select extension method, defined over IEnumerable (or IQueryable which we'll ignore here). Actually, that .Select method is exactly the kind of projection function that you're after.
LBushkin is correct, but you can actually use LINQ syntax as well...
var query = from o in objects
select o.MyMethod();
You can also run a custom method using the marvelous Jon Skeet's morelinq library
For example if you had a text property on your MyClass that you needed to change in runtime using a method on the same class:
objects = objects.Pipe<MyClass>(class => class.Text = class.UpdateText()).ToList();
This method will now be implemented on every object in your list. I love morelinq!
http://www.hookedonlinq.com/UpdateOperator.ashx has an extended Update method you can use. Or you can use a select statement as posted by others.

Categories