IEnumerable vs IList and weird CrossThreadMessagingException during debugging - c#

The initial code is this:
var processes = Process.GetProcesses().Where(p => p.MainWindowTitle.ToUpperInvariant().Contains("FOO"));
During debugging, if I try to call Count() on processes in the immediate window pane or inspect the "Results View" in the locals pane, I get a CrossThreadMessagingException. If I don't debug but just run the code, everything is fine. It is also fine if I convert the collection to a list before assigning it to processes and use the Count property during debugging.
What exactly is a CrossThreadMessagingException and why is the IEnumerable approach causing such an exception?
edit: Providing a bit more information about the exception.
Message: An exception 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException' occurred
Source: Microsoft.VisualStudio.Debugger.Runtime
StackTrace:
at Microsoft.VisualStudio.Debugger.Runtime.Main.ThrowCrossThreadMessageException(String formatString)
at Microsoft.Win32.NativeMethods.GetWindowTextLength(HandleRef hWnd)
at System.Diagnostics.Process.get_MainWindowTitle()

This might totally be wrong, but I gather it's a mixture of deferred enumeration with WhereArrayIterator, and the debugger attempting to enumerate it?
I get the feeling, where the immediate window is trying to enumerate your result, it is doing so on another thread (which is causing the CrossThreadMessagingException).
It's not doing it when you call ToList, because ToList causes the enumeration to run immediately and concatenate the results in the list. This is done before you attempt to use the Count method in the immediate window.
When you use Count() without the ToList call, it forces the WhereArrayIterator (which is the return value of your Where method call) to enumerate, which is then trying to access the your lamda delegate from another thread.
On testing, you can actually enumerate other instances of WhereArrayIterator through immediate, so I think this is your particular use case, where you are trying to enumerate over the Process type, which I think internally makes calls using the Win32 API.
Internally, the Process.MainWindowTitle property uses lazy loading for its value. It doesn't actually make the call to grab the information, until the property is accessed for the first time (and also, it does so without locking, so if there where multiple threads accessing that area of the code, it is not atomic, so there is an inherit risk of race conditions - It shouldn't matter to much anyway, as it is a read only property, who's value should always be the same).
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[MonitoringDescription("ProcessMainWindowTitle")]
public string MainWindowTitle
{
get
{
if (this.mainWindowTitle == null)
{
IntPtr mainWindowHandle = this.MainWindowHandle;
if (mainWindowHandle == (IntPtr) 0)
{
this.mainWindowTitle = string.Empty;
}
else
{
StringBuilder lpString = new StringBuilder(Microsoft.Win32.NativeMethods.GetWindowTextLength(new HandleRef((object) this, mainWindowHandle)) * 2);
Microsoft.Win32.NativeMethods.GetWindowText(new HandleRef((object) this, mainWindowHandle), lpString, lpString.Capacity);
this.mainWindowTitle = ((object) lpString).ToString();
}
}
return this.mainWindowTitle;
}
}
When it is first accessed, the property makes a Win32 call to grab the window text. I believe this is where it seems to be falling over. But it only seems to fall over when using deferred enumeration with your WhereArrayIterator instance.
This is all a blind guess to be honest!

Related

LINQ Select only throws IOException when the Enumerable is looked at

I'm currently using LINQ to load a list of files into XDocuments, like so:
var fileNames = new List<string>(){ "C:\file.xml" };
var xDocs = fileNames.Select(XDocument.Load);
var xDocs2 = xDocs.ToList(); // Crashes here
If I deliberately 'lock' one of the files with a different process, the IOException is only thrown when I actually start to look at the XDocuments I've been generating, ie when ToList() is called.
Can anyone explain why this is, and how best to handle this error? I'd like to have access to the working XDocuments still, if possible.
Can anyone explain why this is
As many pointed out, this is because of the so called deferred execution of many LINQ methods. For instanse, the Enumerable.Select method documentation states
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
while the Enumerable.ToList documentation contains
The ToList<TSource>(IEnumerable<TSource>) method forces immediate query evaluation and returns a List that contains the query results. You can append this method to your query in order to obtain a cached copy of the query results.
So the XDocument.Load is really executed for each file name during the ToList call. I guess that covers the why part.
and how best to handle this error? I'd like to have access to the working XDocuments still, if possible.
I don't know what does "best" mean in this context, but if you want to ignore the errors and include the "working XDocuments", then you can use something like this
var xDocs = fileNames.Select(fileName =>
{
try { return XDocument.Load(fileName); }
catch { return null; }
});
and then either append .Where(doc => doc != null), or account for null documents when processing the list.
This is why the linq .Select is an IEnumerable and the elements are first called if you make your IEnumerable to an List. Then you go through all of your elements.

IEnumerable with yield return and VS Quickwatch crash

I have the following method that return an IEnumerable
public IEnumerable<ExternalFilter> GetExternalFilters()
{
if (externalfilters == null)
yield break;
foreach (ExternalFilter filter in externalfilters)
yield return filter;
}
If I look at GetExternalFilter in VS Quickwatch, when expanding the collection, the in-debug program immediately crashes and Quickwatch shows nothing.
The same thing on evalutating Count() of that collection, with Quickwatch message Function evaluation was aborted.
What I need to have this functionality to work?
Just a hunch: maybe your enumeration externalfilter can only be evaluated once? (E.g. because of the source of the data).
Which means that as soon as you try to use QuickWatch to re-evaluate the result of GetExternalFilters it will fail.
If externalfilter is not just an enumerable but something like a simple List<T> then your problem lies somewhere else.
I believe that your parameter externalfilters is not thread safe.
Do you update your parameter from another thread?
If so, use lock to update list and reading from it.

How to lazy load with my own IQueryable that is backed by a list

I have a list of permissions defined like this:
private List<PermissionItem> permissionItems;
private ReadOnlyCollection<PermissionItem> permissionItemsReadOnly;
This list is retrieved from a web service via a background thread. The read only version is filled from the List version.
I expose this list to the rest of my (rather large) application like this:
public IQueryable<PermissionItem> PermissionItems
{
get
{
// Make sure that the permissions have returned.
// If they have not then we need to wait for that to happen.
if (!doneLoadingPermissions.WaitOne(10000))
throw new ApplicationException("Could not load permissions");
return permissionItemsReadOnly.AsQueryable();
}
}
This is all well and good. The user can ask for permissions and get them once they have loaded.
But if I have code like this in a constructor (in a different class):
ThisClassInstanceOfThePermisssions = SecurityStuff.PermissionItems;
Then I am fairly sure that will block until the permissions return. But it does not need to block until the permissions are actually used.
I have read that IQueryable is "Lazy Loading". (I have used this feature in my Entity Framework code.)
Is there a way I could change this to allow references to my IQueryable at any time, and only block when the data is actually used?
Note: This is a "nice to have" feature. Actually loading the permissions does not take too long. So if this is a "roll your own" query/expression stuff, then I will probably pass. But I am curious what it takes to make it work.
Yes this is possible. First, probably you should switch to IEnumerable as you are not using any IQueryable features. Next, you need to implement a new iterator:
public IEnumerable<PermissionItem> PermissionItems
{
get
{
return GetPermissionItems();
}
}
static IEnumerable<PermissionItem> GetPermissionItems()
{
// Make sure that the permissions have returned.
// If they have not then we need to wait for that to happen.
if (!doneLoadingPermissions.WaitOne(10000))
throw new ApplicationException("Could not load permissions");
foreach (var item in permissionItemsReadOnly) yield return item;
}
The event will only be waited on if the caller of the property enumerates the IEnumerable. Just returning it does nothing.
Take a look at the Lazy<T> class.
Lazy initialization occurs the first time the Lazy.Value property
is accessed. Use an instance of Lazy to defer the creation of a
large or resource-intensive object or the execution of a
resource-intensive task

cannot reach my inner breakpoint

I'm running the following code:
hashedUrlDataList.Select(mDalHashedUrlData.Save);
I have put a breakpoint in the called delegate,
public HashedUrlData Save(HashedUrlData item)
{
//breakpoint here
}
but it doesn't stop there.
How can I fix this?
Your method will be called when you'll enumerate the result of Select() not when declared.
Enumerable.Select is Lazy.
Try this and tell me if your break point is caught
hashedUrlDataList.Select(mDalHashedUrlData.Save).ToList();
Or the basic:
hashedUrlDataList.Select(mDalHashedUrlData.Save).GetEnumerator().MoveNext()
It just works if you have at least one element.
You can do it too:
hashedUrlDataList.Select(mDalHashedUrlData.Save).Any();
Any() do the same that GetEnumerator().MoveNext()
I think that what you want is:
List<HashedUrlData> hashedUrlDataList = new List<HashedUrlData>();
hashedUrlDataList.ForEach(Save);
LINQ is for querying data; it's not intended to cause side-effects.
If you're more interested in the side efects of your Savemethod than the HashedUrlData instance it returns, you should really be calling
foreach (HashedUrlData h in hashedUrlDataList)
{
h.Save();
}
If you will eventually be using the returned values and this is just an intermediate/debugging stage, then by all means use LINQ. Just be aware that Save will only be called as you access each returned value, or call something else that enumerates the whole enumerator as the other answers have shown.

Did I really just put a string data type into an IEnumerable<int>

Ok, this is a little weird. Ignore what I am trying to do, and look at the result of what happens in this situation.
The Code:
static string rawNumbers="1,4,6,20,21,22,30,34";
static IEnumerable<int> numbers = null;
static void Main(string[] args)
{
numbers = rawNumbers.Split(',').Cast<int>();
for (int i = 0; i < numbers.Count(); i++)
{
//do something
}
}
The Situation:
The line numbers = rawNumbers.Split(',').Cast<int>(); appears to work, and no exception is thrown. However, when I iterate over the collection, and InvalidCastException is thrown.
Now, drill down into the source and look at CastIterator<TResult>. This seems to be getting called at for (int i = 0; i < numbers.Count(); i++)...specifically numbers.Count()
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
foreach (object obj in source) yield return (TResult)obj;
}
The error is happening on the cast, and when I look at the data in the source varaible it is a string[].
I would have thought that since the line where I call the cast to an int executed successfully everything was fine. What is going on behind the curtain? Is the string array really just being stored somewhere, and not being casted to T until it is called for? Lazy casting perhaps?
I know I cant do: (int)"42". My questions isn't how to make the cast work, but what is going on. Deferred execution of the cast? It seems weird the line where I call out Cast<int>() seems to work, but really doesn't.
A couple of things: first, you can't cast a string to an int. You can parse a string and get a resulting integer. But essentially you're trying to do this:
int x = (int)"32";
That's an exception. Second, the LINQ operators are deferred execution, so nothing actually happens until you attempt to iterate the result, that's when the real work starts happening.
With deferred execution there is no checking on whether the operations you're requesting are valid or can be properly performed, it just sets up the operations and then attempts to perform then individually upon iteration.
As has been noted, 'Deferred Execution' is the issue here. Many LINQ operators do not cause your lambda code to actually execute until you iterate over the result variable.
One reason for this is to allow you to build up a complex series of operations that can be executed at one time, rather than as a string of separate operations. This can be a useful thing - but it also can be tricky if you aren't expecting it.
The reason you are not getting an error immediately at the Cast<int>() line is because remember this is just chaining up the sequence of operations. Only once you iterate over the collection will the conversions be executed.
rawNumbers.Split(',') happens right away, but the Cast<int>() is deferred computation.
If you added a ToList() or ToArray() on that line, it would have executed immediately.
The reason that Cast appears to work, is because the IEnumerable isn't enumerated when the method is called, but rather when you call Count(). So, the call to Cast() really does nothing. The code fails as soon as the Cast is actually evaluated.
Instead of trying to cast, just do a simple conversion.
numbers = rawNumbers.Split(',').Select(str => int.parse(str));
I may be wrong but I wonder if it has to do with casting between int and string. I always get errors when I forget you cannot cast against string using (int), rather you have to use int.Parse. It probably has something to do with that but this is just off the top of my head.

Categories

Resources